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 177276 : class ArrayBufferAllocatorBase : public v8::ArrayBuffer::Allocator {
76 : public:
77 0 : void* Allocate(size_t length) override {
78 213168 : return allocator_->Allocate(length);
79 : }
80 :
81 0 : void* AllocateUninitialized(size_t length) override {
82 11551 : return allocator_->AllocateUninitialized(length);
83 : }
84 :
85 0 : void Free(void* data, size_t length) override {
86 224717 : 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 59092 : class ShellArrayBufferAllocator : public ArrayBufferAllocatorBase {
96 : public:
97 213913 : void* Allocate(size_t length) override {
98 213913 : if (length >= kVMThreshold) return AllocateVM(length);
99 213071 : return ArrayBufferAllocatorBase::Allocate(length);
100 : }
101 :
102 11569 : void* AllocateUninitialized(size_t length) override {
103 11569 : if (length >= kVMThreshold) return AllocateVM(length);
104 11551 : return ArrayBufferAllocatorBase::AllocateUninitialized(length);
105 : }
106 :
107 225436 : void Free(void* data, size_t length) override {
108 225436 : if (length >= kVMThreshold) {
109 818 : FreeVM(data, length);
110 : } else {
111 : ArrayBufferAllocatorBase::Free(data, length);
112 : }
113 225441 : }
114 :
115 : private:
116 : static constexpr size_t kVMThreshold = 65536;
117 : static constexpr size_t kTwoGB = 2u * 1024u * 1024u * 1024u;
118 :
119 862 : 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 862 : if (length >= kTwoGB) return nullptr;
124 :
125 827 : v8::PageAllocator* page_allocator = i::GetPlatformPageAllocator();
126 827 : size_t page_size = page_allocator->AllocatePageSize();
127 827 : size_t allocated = RoundUp(length, page_size);
128 : // Rounding up could go over the limit.
129 827 : if (allocated >= kTwoGB) return nullptr;
130 : return i::AllocatePages(page_allocator, nullptr, allocated, page_size,
131 818 : PageAllocator::kReadWrite);
132 : }
133 :
134 818 : void FreeVM(void* data, size_t length) {
135 818 : v8::PageAllocator* page_allocator = i::GetPlatformPageAllocator();
136 818 : size_t page_size = page_allocator->AllocatePageSize();
137 818 : size_t allocated = RoundUp(length, page_size);
138 818 : CHECK(i::FreePages(page_allocator, data, allocated));
139 818 : }
140 : };
141 :
142 : // ArrayBuffer allocator that never allocates over 10MB.
143 118184 : class MockArrayBufferAllocator : public ArrayBufferAllocatorBase {
144 : protected:
145 99 : void* Allocate(size_t length) override {
146 99 : 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 99 : void Free(void* data, size_t length) override {
154 99 : 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 198 : 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 29546 : class MockArrayBufferAllocatiorWithLimit : public MockArrayBufferAllocator {
167 : public:
168 : explicit MockArrayBufferAllocatiorWithLimit(size_t allocation_limit)
169 29546 : : 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 29546 : std::unique_ptr<v8::Platform> g_platform;
199 :
200 3991 : static Local<Value> Throw(Isolate* isolate, const char* message) {
201 : return isolate->ThrowException(
202 : String::NewFromUtf8(isolate, message, NewStringType::kNormal)
203 7982 : .ToLocalChecked());
204 : }
205 :
206 614 : static Local<Value> GetValue(v8::Isolate* isolate, Local<Context> context,
207 : Local<v8::Object> object, const char* property) {
208 : Local<String> v8_str =
209 : String::NewFromUtf8(isolate, property, NewStringType::kNormal)
210 614 : .ToLocalChecked();
211 1228 : return object->Get(context, v8_str).ToLocalChecked();
212 : }
213 :
214 2263 : Worker* GetWorkerFromInternalField(Isolate* isolate, Local<Object> object) {
215 2263 : 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 2263 : 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 : 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 : trace_config->SetTraceRecordMode(
271 0 : 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 0 : .ToLocalChecked()
304 : ->ToString(context)
305 0 : .ToLocalChecked();
306 0 : String::Utf8Value str(isolate, v->ToString(context).ToLocalChecked());
307 0 : trace_config->AddIncludedCategory(*str);
308 0 : }
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 0 : }
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 29546 : const base::TimeTicks Shell::kInitialTicks =
367 : base::TimeTicks::HighResolutionNow();
368 29546 : Global<Function> Shell::stringify_function_;
369 : base::LazyMutex Shell::workers_mutex_;
370 : bool Shell::allow_new_workers_ = true;
371 29546 : std::vector<Worker*> Shell::workers_;
372 29546 : std::vector<ExternalizedContents> Shell::externalized_contents_;
373 : std::atomic<bool> Shell::script_executed_{false};
374 : base::LazyMutex Shell::isolate_status_lock_;
375 29546 : 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 29546 : Shell::cached_code_map_;
379 :
380 29546 : Global<Context> Shell::evaluation_context_;
381 : ArrayBuffer::Allocator* Shell::array_buffer_allocator;
382 29546 : 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 38997 : class DummySourceStream : public v8::ScriptCompiler::ExternalSourceStream {
387 : public:
388 25998 : DummySourceStream(Local<String> source, Isolate* isolate) : done_(false) {
389 12999 : source_length_ = source->Utf8Length(isolate);
390 12999 : source_buffer_.reset(new uint8_t[source_length_]);
391 : source->WriteUtf8(isolate, reinterpret_cast<char*>(source_buffer_.get()),
392 25998 : source_length_);
393 12999 : }
394 :
395 25906 : size_t GetMoreData(const uint8_t** src) override {
396 25906 : if (done_) {
397 : return 0;
398 : }
399 12999 : *src = source_buffer_.release();
400 12999 : done_ = true;
401 :
402 12999 : 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 25998 : class BackgroundCompileThread : public base::Thread {
412 : public:
413 12999 : BackgroundCompileThread(Isolate* isolate, Local<String> source)
414 : : base::Thread(GetThreadOptions("BackgroundCompileThread")),
415 : source_(source),
416 12999 : streamed_source_(new DummySourceStream(source, isolate),
417 : v8::ScriptCompiler::StreamedSource::UTF8),
418 : task_(v8::ScriptCompiler::StartStreamingScript(isolate,
419 38997 : &streamed_source_)) {}
420 :
421 25998 : 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 474 : 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 114311 : 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 114311 : 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 : parse_info.set_language_mode(
481 0 : i::construct_language_mode(i::FLAG_use_strict));
482 : 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 114311 : HandleScope handle_scope(isolate);
493 228622 : TryCatch try_catch(isolate);
494 114311 : 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 114311 : Local<Context>::New(isolate, data->realms_[data->realm_current_]);
502 : Context::Scope context_scope(realm);
503 : MaybeLocal<Script> maybe_script;
504 114311 : Local<Context> context(isolate->GetCurrentContext());
505 : ScriptOrigin origin(name);
506 :
507 114311 : 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 114209 : } else if (options.stress_background_compile) {
521 : // Start a background thread compiling the script.
522 12999 : BackgroundCompileThread background_compile_thread(isolate, source);
523 12999 : background_compile_thread.Start();
524 :
525 : // In parallel, compile on the main thread to flush out any data races.
526 : {
527 12999 : TryCatch ignore_try_catch(isolate);
528 : ScriptCompiler::Source script_source(source, origin);
529 12999 : USE(ScriptCompiler::Compile(context, &script_source,
530 12999 : ScriptCompiler::kNoCompileOptions));
531 : }
532 :
533 : // Join with background thread and finalize compilation.
534 12999 : background_compile_thread.Join();
535 : maybe_script = v8::ScriptCompiler::Compile(
536 12999 : 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 101210 : options.compile_options);
541 : }
542 :
543 : Local<Script> script;
544 114311 : if (!maybe_script.ToLocal(&script)) {
545 : // Print errors that happened during compilation.
546 1821 : if (report_exceptions) ReportException(isolate, &try_catch);
547 : return false;
548 : }
549 :
550 112490 : 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 112490 : maybe_result = script->Run(realm);
559 112490 : 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 112490 : if (process_message_queue && !EmptyMessageQueues(isolate)) success = false;
568 112490 : data->realm_current_ = data->realm_switch_;
569 : }
570 : Local<Value> result;
571 112490 : if (!maybe_result.ToLocal(&result)) {
572 : DCHECK(try_catch.HasCaught());
573 : // Print errors that happened during execution.
574 4189 : if (report_exceptions) ReportException(isolate, &try_catch);
575 : return false;
576 : }
577 : DCHECK(!try_catch.HasCaught());
578 108301 : 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 0 : 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 0 : printf("\n");
591 : }
592 : }
593 222612 : return success;
594 : }
595 :
596 : namespace {
597 :
598 2787 : std::string ToSTLString(Isolate* isolate, Local<String> v8_str) {
599 2787 : String::Utf8Value utf8(isolate, v8_str);
600 : // Should not be able to fail since the input is a String.
601 2787 : CHECK(*utf8);
602 2787 : 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 3462 : return path[0] == '/';
612 : #endif
613 : }
614 :
615 1080 : 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 1080 : CHECK_NOT_NULL(getcwd(curdir, PATH_MAX));
625 1080 : return curdir;
626 : #endif
627 : }
628 :
629 : // Returns the directory part of path, without the trailing '/'.
630 2753 : 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 2753 : 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 3462 : std::string NormalizePath(const std::string& path,
641 : const std::string& dir_name) {
642 : std::string result;
643 3462 : if (IsAbsolutePath(path)) {
644 : result = path;
645 : } else {
646 4746 : result = dir_name + '/' + path;
647 : }
648 3462 : std::replace(result.begin(), result.end(), '\\', '/');
649 : size_t i;
650 3823 : while ((i = result.find("/./")) != std::string::npos) {
651 361 : result.erase(i, 2);
652 : }
653 3462 : return result;
654 : }
655 :
656 : // Per-context Module data, allowing sharing of module maps
657 : // across top-level module loads.
658 104376 : class ModuleEmbedderData {
659 : private:
660 : class ModuleGlobalHash {
661 : public:
662 52188 : explicit ModuleGlobalHash(Isolate* isolate) : isolate_(isolate) {}
663 2366 : size_t operator()(const Global<Module>& module) const {
664 4732 : return module.Get(isolate_)->GetIdentityHash();
665 : }
666 :
667 : private:
668 : Isolate* isolate_;
669 : };
670 :
671 : public:
672 52188 : explicit ModuleEmbedderData(Isolate* isolate)
673 104376 : : 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 52188 : void InitializeModuleEmbedderData(Local<Context> context) {
688 : context->SetAlignedPointerInEmbedderData(
689 52188 : kModuleEmbedderDataIndex, new ModuleEmbedderData(context->GetIsolate()));
690 52188 : }
691 :
692 : ModuleEmbedderData* GetModuleDataFromContext(Local<Context> context) {
693 : return static_cast<ModuleEmbedderData*>(
694 : context->GetAlignedPointerFromEmbedderData(kModuleEmbedderDataIndex));
695 : }
696 :
697 52188 : void DisposeModuleEmbedderData(Local<Context> context) {
698 52188 : delete GetModuleDataFromContext(context);
699 52188 : context->SetAlignedPointerInEmbedderData(kModuleEmbedderDataIndex, nullptr);
700 52188 : }
701 :
702 993 : MaybeLocal<Module> ResolveModuleCallback(Local<Context> context,
703 : Local<String> specifier,
704 : Local<Module> referrer) {
705 993 : Isolate* isolate = context->GetIsolate();
706 : ModuleEmbedderData* d = GetModuleDataFromContext(context);
707 : auto specifier_it =
708 993 : d->module_to_specifier_map.find(Global<Module>(isolate, referrer));
709 993 : CHECK(specifier_it != d->module_to_specifier_map.end());
710 : std::string absolute_path = NormalizePath(ToSTLString(isolate, specifier),
711 2979 : DirName(specifier_it->second));
712 : auto module_it = d->specifier_to_module_map.find(absolute_path);
713 993 : CHECK(module_it != d->specifier_to_module_map.end());
714 1986 : return module_it->second.Get(isolate);
715 : }
716 :
717 : } // anonymous namespace
718 :
719 1591 : MaybeLocal<Module> Shell::FetchModuleTree(Local<Context> context,
720 : const std::string& file_name) {
721 : DCHECK(IsAbsolutePath(file_name));
722 1591 : Isolate* isolate = context->GetIsolate();
723 1591 : Local<String> source_text = ReadFile(isolate, file_name.c_str());
724 1591 : 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 : String::NewFromUtf8(isolate, file_name.c_str(), NewStringType::kNormal)
731 : .ToLocalChecked(),
732 : Local<Integer>(), Local<Integer>(), Local<Boolean>(), Local<Integer>(),
733 1528 : Local<Value>(), Local<Boolean>(), Local<Boolean>(), True(isolate));
734 : ScriptCompiler::Source source(source_text, origin);
735 : Local<Module> module;
736 3056 : if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)) {
737 173 : return MaybeLocal<Module>();
738 : }
739 :
740 : ModuleEmbedderData* d = GetModuleDataFromContext(context);
741 2710 : CHECK(d->specifier_to_module_map
742 : .insert(std::make_pair(file_name, Global<Module>(isolate, module)))
743 : .second);
744 2710 : CHECK(d->module_to_specifier_map
745 : .insert(std::make_pair(Global<Module>(isolate, module), file_name))
746 : .second);
747 :
748 1355 : std::string dir_name = DirName(file_name);
749 :
750 2339 : for (int i = 0, length = module->GetModuleRequestsLength(); i < length; ++i) {
751 984 : Local<String> name = module->GetModuleRequest(i);
752 : std::string absolute_path =
753 1968 : NormalizePath(ToSTLString(isolate, name), dir_name);
754 984 : if (!d->specifier_to_module_map.count(absolute_path)) {
755 1166 : if (FetchModuleTree(context, absolute_path).IsEmpty()) {
756 0 : return MaybeLocal<Module>();
757 : }
758 : }
759 : }
760 :
761 1355 : 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 405 : 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 18 : 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 : String::NewFromUtf8(isolate, "url", NewStringType::kNormal)
816 18 : .ToLocalChecked();
817 : Local<String> url = String::NewFromUtf8(isolate, specifier_it->second.c_str(),
818 : NewStringType::kNormal)
819 18 : .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 : 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 279 : if (root_module->InstantiateModule(realm, ResolveModuleCallback)
859 558 : .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 675 : bool Shell::ExecuteModule(Isolate* isolate, const char* file_name) {
877 675 : HandleScope handle_scope(isolate);
878 :
879 : PerIsolateData* data = PerIsolateData::Get(isolate);
880 675 : Local<Context> realm = data->realms_[data->realm_current_].Get(isolate);
881 : Context::Scope context_scope(realm);
882 :
883 2025 : std::string absolute_path = NormalizePath(file_name, GetWorkingDirectory());
884 :
885 1350 : TryCatch try_catch(isolate);
886 675 : try_catch.SetVerbose(true);
887 :
888 : Local<Module> root_module;
889 : MaybeLocal<Value> maybe_exception;
890 :
891 1350 : 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 565 : if (root_module->InstantiateModule(realm, ResolveModuleCallback)
899 1130 : .FromMaybe(false)) {
900 490 : maybe_result = root_module->Evaluate(realm);
901 490 : EmptyMessageQueues(isolate);
902 : }
903 : Local<Value> result;
904 565 : 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 675 : return true;
912 : }
913 :
914 30196 : PerIsolateData::PerIsolateData(Isolate* isolate)
915 90588 : : isolate_(isolate), realms_(nullptr) {
916 : isolate->SetData(0, this);
917 30196 : if (i::FLAG_expose_async_hooks) {
918 50 : async_hooks_wrapper_ = new AsyncHooks(isolate);
919 : }
920 30196 : }
921 :
922 30196 : PerIsolateData::~PerIsolateData() {
923 30196 : isolate_->SetData(0, nullptr); // Not really needed, just to be sure...
924 30196 : if (i::FLAG_expose_async_hooks) {
925 50 : delete async_hooks_wrapper_; // This uses the isolate
926 : }
927 30196 : }
928 :
929 1265 : 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 1265 : }
934 :
935 159240 : MaybeLocal<Function> PerIsolateData::GetTimeoutCallback() {
936 159240 : 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 51492 : PerIsolateData::RealmScope::RealmScope(PerIsolateData* data) : data_(data) {
950 51492 : data_->realm_count_ = 1;
951 51492 : data_->realm_current_ = 0;
952 51492 : data_->realm_switch_ = 0;
953 102984 : data_->realms_ = new Global<Context>[1];
954 51492 : data_->realms_[0].Reset(data_->isolate_,
955 102984 : data_->isolate_->GetEnteredOrMicrotaskContext());
956 51492 : }
957 :
958 :
959 51491 : 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 52178 : 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 51491 : data_->realm_count_ = 0;
969 102983 : delete[] data_->realms_;
970 51491 : }
971 :
972 :
973 316 : int PerIsolateData::RealmFind(Local<Context> context) {
974 2233 : 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 19827 : 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 9909 : ->Int32Value(args.GetIsolate()->GetCurrentContext())
990 19818 : .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 6 : 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 596 : 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 596 : 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 54 : 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 : int index = data->RealmFind(args[0]
1032 18 : ->ToObject(isolate->GetCurrentContext())
1033 18 : .ToLocalChecked()
1034 18 : ->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 1422 : 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 948 : if (index == -1) return;
1046 : args.GetReturnValue().Set(
1047 948 : Local<Context>::New(args.GetIsolate(), data->realms_[index])->Global());
1048 : }
1049 :
1050 696 : MaybeLocal<Context> Shell::CreateRealm(
1051 1392 : const v8::FunctionCallbackInfo<v8::Value>& args, int index,
1052 : v8::MaybeLocal<Value> global_object) {
1053 : Isolate* isolate = args.GetIsolate();
1054 696 : 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 3822 : 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 17 : const v8::FunctionCallbackInfo<v8::Value>& args) {
1097 : Local<Context> context;
1098 34 : if (CreateRealm(args, -1, v8::MaybeLocal<Value>()).ToLocal(&context)) {
1099 : 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 27 : 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 26972 : 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 9120 : if (!ScriptCompiler::CompileUnboundScript(isolate, &script_source)
1162 9120 : .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 10338 : 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 234 : 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 90 : const v8::FunctionCallbackInfo<v8::Value>& args) {
1211 : Isolate* isolate = args.GetIsolate();
1212 45 : HandleScope handle_scope(isolate);
1213 : args.GetReturnValue().Set(v8::Number::New(
1214 : isolate,
1215 90 : PerIsolateData::Get(isolate)->GetAsyncHooks()->GetExecutionAsyncId()));
1216 45 : }
1217 :
1218 45 : void Shell::AsyncHooksTriggerAsyncId(
1219 90 : const v8::FunctionCallbackInfo<v8::Value>& args) {
1220 : Isolate* isolate = args.GetIsolate();
1221 45 : HandleScope handle_scope(isolate);
1222 : args.GetReturnValue().Set(v8::Number::New(
1223 : isolate,
1224 90 : PerIsolateData::Get(isolate)->GetAsyncHooks()->GetTriggerAsyncId()));
1225 45 : }
1226 :
1227 13554652 : void WriteToFile(FILE* file, const v8::FunctionCallbackInfo<v8::Value>& args) {
1228 6870562 : for (int i = 0; i < args.Length(); i++) {
1229 2228030 : HandleScope handle_scope(args.GetIsolate());
1230 2228030 : if (i != 0) {
1231 : fprintf(file, " ");
1232 : }
1233 :
1234 : // Explicitly catch potential exceptions in toString().
1235 4456060 : v8::TryCatch try_catch(args.GetIsolate());
1236 : Local<Value> arg = args[i];
1237 : Local<String> str_obj;
1238 :
1239 2228030 : if (arg->IsSymbol()) {
1240 0 : arg = Local<Symbol>::Cast(arg)->Name();
1241 : }
1242 4456060 : if (!arg->ToString(args.GetIsolate()->GetCurrentContext())
1243 2228030 : .ToLocal(&str_obj)) {
1244 0 : try_catch.ReThrow();
1245 1207251 : return;
1246 : }
1247 :
1248 4456060 : v8::String::Utf8Value str(args.GetIsolate(), str_obj);
1249 2228030 : int n = static_cast<int>(fwrite(*str, sizeof(**str), str.length(), file));
1250 2228030 : if (n != str.length()) {
1251 : printf("Error in fwrite\n");
1252 0 : base::OS::ExitProcess(1);
1253 : }
1254 2228030 : }
1255 : }
1256 :
1257 1207251 : void WriteAndFlush(FILE* file,
1258 : const v8::FunctionCallbackInfo<v8::Value>& args) {
1259 1207251 : WriteToFile(file, args);
1260 : fprintf(file, "\n");
1261 1207251 : fflush(file);
1262 1207251 : }
1263 :
1264 1207251 : void Shell::Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
1265 1207251 : WriteAndFlush(stdout, args);
1266 1207251 : }
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 150 : void Shell::Read(const v8::FunctionCallbackInfo<v8::Value>& args) {
1277 30 : 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 0 : }
1288 : }
1289 60 : 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 30 : 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 : 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 : String::NewFromUtf8(isolate, buffer,
1324 : NewStringType::kNormal, length - 1)
1325 0 : .ToLocalChecked());
1326 : } else {
1327 : return String::Concat(
1328 : isolate, accumulator,
1329 : String::NewFromUtf8(isolate, buffer, NewStringType::kNormal,
1330 : length - 1)
1331 0 : .ToLocalChecked());
1332 : }
1333 : }
1334 : }
1335 :
1336 :
1337 47680 : void Shell::Load(const v8::FunctionCallbackInfo<v8::Value>& args) {
1338 16456 : for (int i = 0; i < args.Length(); i++) {
1339 5961 : HandleScope handle_scope(args.GetIsolate());
1340 8228 : String::Utf8Value file(args.GetIsolate(), args[i]);
1341 5961 : if (*file == nullptr) {
1342 0 : Throw(args.GetIsolate(), "Error loading file");
1343 0 : return;
1344 : }
1345 5961 : Local<String> source = ReadFile(args.GetIsolate(), *file);
1346 5961 : if (source.IsEmpty()) {
1347 4 : Throw(args.GetIsolate(), "Error loading file");
1348 4 : return;
1349 : }
1350 5957 : if (!ExecuteString(
1351 : args.GetIsolate(), source,
1352 5957 : String::NewFromUtf8(args.GetIsolate(), *file,
1353 : NewStringType::kNormal)
1354 : .ToLocalChecked(),
1355 : kNoPrintResult,
1356 : options.quiet_load ? kNoReportExceptions : kReportExceptions,
1357 17871 : kNoProcessMessageQueue)) {
1358 3690 : Throw(args.GetIsolate(), "Error executing file");
1359 3690 : return;
1360 : }
1361 2267 : }
1362 : }
1363 :
1364 3795 : 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 1265 : Local<Function> callback = Local<Function>::Cast(args[0]);
1369 1265 : Local<Context> context = isolate->GetCurrentContext();
1370 1265 : PerIsolateData::Get(isolate)->SetTimeout(callback, context);
1371 : }
1372 :
1373 3873 : void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) {
1374 : Isolate* isolate = args.GetIsolate();
1375 659 : HandleScope handle_scope(isolate);
1376 1309 : 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 1273 : if (args.Length() > 1 && args[1]->IsObject()) {
1385 614 : Local<Object> object = args[1].As<Object>();
1386 614 : Local<Context> context = isolate->GetCurrentContext();
1387 614 : Local<Value> value = GetValue(args.GetIsolate(), context, object, "type");
1388 614 : if (value->IsString()) {
1389 614 : Local<String> worker_type = value->ToString(context).ToLocalChecked();
1390 614 : String::Utf8Value str(isolate, worker_type);
1391 614 : 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 605 : }
1399 : }
1400 : }
1401 :
1402 : Local<Value> source;
1403 641 : if (load_from_file) {
1404 36 : String::Utf8Value filename(args.GetIsolate(), args[0]);
1405 72 : source = ReadFile(args.GetIsolate(), *filename);
1406 36 : if (source.IsEmpty()) {
1407 27 : Throw(args.GetIsolate(), "Error loading worker script");
1408 27 : return;
1409 9 : }
1410 : } else {
1411 : source = args[0];
1412 : }
1413 :
1414 614 : 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 1228 : 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 614 : args.Holder()->SetAlignedPointerInInternalField(0, nullptr);
1430 :
1431 614 : if (!allow_new_workers_) return;
1432 :
1433 605 : Worker* worker = new Worker;
1434 605 : args.Holder()->SetAlignedPointerInInternalField(0, worker);
1435 605 : workers_.push_back(worker);
1436 :
1437 1210 : String::Utf8Value script(args.GetIsolate(), source);
1438 605 : if (!*script) {
1439 0 : Throw(args.GetIsolate(), "Can't get worker script");
1440 0 : return;
1441 : }
1442 605 : worker->StartExecuteInThread(*script);
1443 605 : }
1444 : }
1445 :
1446 :
1447 2535 : void Shell::WorkerPostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
1448 : Isolate* isolate = args.GetIsolate();
1449 845 : HandleScope handle_scope(isolate);
1450 :
1451 845 : if (args.Length() < 1) {
1452 0 : Throw(isolate, "Invalid argument");
1453 0 : return;
1454 : }
1455 :
1456 845 : Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
1457 845 : if (!worker) {
1458 : return;
1459 : }
1460 :
1461 836 : Local<Value> message = args[0];
1462 : Local<Value> transfer =
1463 1672 : args.Length() >= 2 ? args[1] : Local<Value>::Cast(Undefined(isolate));
1464 : std::unique_ptr<SerializationData> data =
1465 836 : Shell::SerializeValue(isolate, message, transfer);
1466 836 : if (data) {
1467 1294 : worker->PostMessage(std::move(data));
1468 836 : }
1469 : }
1470 :
1471 :
1472 2994 : void Shell::WorkerGetMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
1473 : Isolate* isolate = args.GetIsolate();
1474 1007 : HandleScope handle_scope(isolate);
1475 1007 : Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
1476 1007 : if (!worker) {
1477 0 : return;
1478 : }
1479 :
1480 1007 : std::unique_ptr<SerializationData> data = worker->GetMessage();
1481 1007 : if (data) {
1482 : Local<Value> value;
1483 1960 : if (Shell::DeserializeValue(isolate, std::move(data)).ToLocal(&value)) {
1484 : args.GetReturnValue().Set(value);
1485 : }
1486 1007 : }
1487 : }
1488 :
1489 :
1490 822 : void Shell::WorkerTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
1491 : Isolate* isolate = args.GetIsolate();
1492 411 : HandleScope handle_scope(isolate);
1493 411 : Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
1494 411 : if (!worker) {
1495 0 : return;
1496 : }
1497 :
1498 411 : 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 0 : .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 1779 : void Shell::WaitUntilDone(const v8::FunctionCallbackInfo<v8::Value>& args) {
1519 1779 : SetWaitUntilDone(args.GetIsolate(), true);
1520 1779 : }
1521 :
1522 518 : void Shell::NotifyDone(const v8::FunctionCallbackInfo<v8::Value>& args) {
1523 518 : SetWaitUntilDone(args.GetIsolate(), false);
1524 518 : }
1525 :
1526 81 : void Shell::Version(const v8::FunctionCallbackInfo<v8::Value>& args) {
1527 : args.GetReturnValue().Set(
1528 : String::NewFromUtf8(args.GetIsolate(), V8::GetVersion(),
1529 54 : NewStringType::kNormal).ToLocalChecked());
1530 27 : }
1531 :
1532 :
1533 6290 : void Shell::ReportException(Isolate* isolate, v8::TryCatch* try_catch) {
1534 6290 : HandleScope handle_scope(isolate);
1535 6290 : Local<Context> context = isolate->GetCurrentContext();
1536 : bool enter_context = context.IsEmpty();
1537 6290 : 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 21354 : auto ToCString = [](const v8::String::Utf8Value& value) {
1543 : return *value ? *value : "<string conversion failed>";
1544 21354 : };
1545 :
1546 12580 : v8::String::Utf8Value exception(isolate, try_catch->Exception());
1547 : const char* exception_string = ToCString(exception);
1548 6290 : Local<Message> message = try_catch->Message();
1549 6290 : 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 12580 : } 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 6262 : message->GetScriptOrigin().ResourceName());
1563 : const char* filename_string = ToCString(filename);
1564 12524 : int linenum = message->GetLineNumber(context).FromMaybe(-1);
1565 : printf("%s:%i: %s\n", filename_string, linenum, exception_string);
1566 : Local<String> sourceline;
1567 12524 : if (message->GetSourceLine(context).ToLocal(&sourceline)) {
1568 : // Print line of source code.
1569 6262 : 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 12524 : int start = message->GetStartColumn(context).FromJust();
1574 76927 : for (int i = 0; i < start; i++) {
1575 : printf(" ");
1576 : }
1577 12524 : int end = message->GetEndColumn(context).FromJust();
1578 21417 : for (int i = start; i < end; i++) {
1579 : printf("^");
1580 : }
1581 6262 : printf("\n");
1582 6262 : }
1583 : }
1584 : Local<Value> stack_trace_string;
1585 15129 : if (try_catch->StackTrace(context).ToLocal(&stack_trace_string) &&
1586 : stack_trace_string->IsString()) {
1587 : v8::String::Utf8Value stack_trace(isolate,
1588 2540 : Local<String>::Cast(stack_trace_string));
1589 2540 : printf("%s\n", ToCString(stack_trace));
1590 : }
1591 : printf("\n");
1592 12580 : if (enter_context) context->Exit();
1593 6290 : }
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 29546 : magic_number_ = 0xDEADFACE;
1614 29546 : max_counters_ = kMaxCounters;
1615 29546 : max_name_size_ = Counter::kMaxNameSize;
1616 29546 : 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 : 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 : String::NewFromUtf8(isolate, "d8-stringify", NewStringType::kNormal)
1693 0 : .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 0 : return result.ToLocalChecked().As<String>();
1706 : }
1707 :
1708 :
1709 52188 : Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
1710 52188 : Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate);
1711 : global_template->Set(
1712 : String::NewFromUtf8(isolate, "print", NewStringType::kNormal)
1713 : .ToLocalChecked(),
1714 156564 : FunctionTemplate::New(isolate, Print));
1715 : global_template->Set(
1716 : String::NewFromUtf8(isolate, "printErr", NewStringType::kNormal)
1717 : .ToLocalChecked(),
1718 156564 : FunctionTemplate::New(isolate, PrintErr));
1719 : global_template->Set(
1720 : String::NewFromUtf8(isolate, "write", NewStringType::kNormal)
1721 : .ToLocalChecked(),
1722 156564 : FunctionTemplate::New(isolate, Write));
1723 : global_template->Set(
1724 : String::NewFromUtf8(isolate, "read", NewStringType::kNormal)
1725 : .ToLocalChecked(),
1726 156564 : FunctionTemplate::New(isolate, Read));
1727 : global_template->Set(
1728 : String::NewFromUtf8(isolate, "readbuffer", NewStringType::kNormal)
1729 : .ToLocalChecked(),
1730 156564 : FunctionTemplate::New(isolate, ReadBuffer));
1731 : global_template->Set(
1732 : String::NewFromUtf8(isolate, "readline", NewStringType::kNormal)
1733 : .ToLocalChecked(),
1734 156564 : FunctionTemplate::New(isolate, ReadLine));
1735 : global_template->Set(
1736 : String::NewFromUtf8(isolate, "load", NewStringType::kNormal)
1737 : .ToLocalChecked(),
1738 156564 : FunctionTemplate::New(isolate, Load));
1739 : global_template->Set(
1740 : String::NewFromUtf8(isolate, "setTimeout", NewStringType::kNormal)
1741 : .ToLocalChecked(),
1742 156564 : 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 52188 : if (!options.omit_quit) {
1747 : global_template->Set(
1748 : String::NewFromUtf8(isolate, "quit", NewStringType::kNormal)
1749 : .ToLocalChecked(),
1750 156462 : FunctionTemplate::New(isolate, Quit));
1751 : }
1752 52188 : Local<ObjectTemplate> test_template = ObjectTemplate::New(isolate);
1753 : global_template->Set(
1754 : String::NewFromUtf8(isolate, "testRunner", NewStringType::kNormal)
1755 : .ToLocalChecked(),
1756 104376 : test_template);
1757 : test_template->Set(
1758 : String::NewFromUtf8(isolate, "notifyDone", NewStringType::kNormal)
1759 : .ToLocalChecked(),
1760 156564 : FunctionTemplate::New(isolate, NotifyDone));
1761 : test_template->Set(
1762 : String::NewFromUtf8(isolate, "waitUntilDone", NewStringType::kNormal)
1763 : .ToLocalChecked(),
1764 156564 : FunctionTemplate::New(isolate, WaitUntilDone));
1765 : global_template->Set(
1766 : String::NewFromUtf8(isolate, "version", NewStringType::kNormal)
1767 : .ToLocalChecked(),
1768 156564 : FunctionTemplate::New(isolate, Version));
1769 : global_template->Set(
1770 : Symbol::GetToStringTag(isolate),
1771 : String::NewFromUtf8(isolate, "global", NewStringType::kNormal)
1772 156564 : .ToLocalChecked());
1773 :
1774 : // Bind the Realm object.
1775 52188 : Local<ObjectTemplate> realm_template = ObjectTemplate::New(isolate);
1776 : realm_template->Set(
1777 : String::NewFromUtf8(isolate, "current", NewStringType::kNormal)
1778 : .ToLocalChecked(),
1779 156564 : FunctionTemplate::New(isolate, RealmCurrent));
1780 : realm_template->Set(
1781 : String::NewFromUtf8(isolate, "owner", NewStringType::kNormal)
1782 : .ToLocalChecked(),
1783 156564 : FunctionTemplate::New(isolate, RealmOwner));
1784 : realm_template->Set(
1785 : String::NewFromUtf8(isolate, "global", NewStringType::kNormal)
1786 : .ToLocalChecked(),
1787 156564 : FunctionTemplate::New(isolate, RealmGlobal));
1788 : realm_template->Set(
1789 : String::NewFromUtf8(isolate, "create", NewStringType::kNormal)
1790 : .ToLocalChecked(),
1791 156564 : FunctionTemplate::New(isolate, RealmCreate));
1792 : realm_template->Set(
1793 : String::NewFromUtf8(isolate, "createAllowCrossRealmAccess",
1794 : NewStringType::kNormal)
1795 : .ToLocalChecked(),
1796 156564 : FunctionTemplate::New(isolate, RealmCreateAllowCrossRealmAccess));
1797 : realm_template->Set(
1798 : String::NewFromUtf8(isolate, "navigate", NewStringType::kNormal)
1799 : .ToLocalChecked(),
1800 156564 : FunctionTemplate::New(isolate, RealmNavigate));
1801 : realm_template->Set(
1802 : String::NewFromUtf8(isolate, "dispose", NewStringType::kNormal)
1803 : .ToLocalChecked(),
1804 156564 : FunctionTemplate::New(isolate, RealmDispose));
1805 : realm_template->Set(
1806 : String::NewFromUtf8(isolate, "switch", NewStringType::kNormal)
1807 : .ToLocalChecked(),
1808 156564 : FunctionTemplate::New(isolate, RealmSwitch));
1809 : realm_template->Set(
1810 : String::NewFromUtf8(isolate, "eval", NewStringType::kNormal)
1811 : .ToLocalChecked(),
1812 156564 : FunctionTemplate::New(isolate, RealmEval));
1813 : realm_template->SetAccessor(
1814 : String::NewFromUtf8(isolate, "shared", NewStringType::kNormal)
1815 : .ToLocalChecked(),
1816 104376 : RealmSharedGet, RealmSharedSet);
1817 : global_template->Set(
1818 : String::NewFromUtf8(isolate, "Realm", NewStringType::kNormal)
1819 : .ToLocalChecked(),
1820 104376 : realm_template);
1821 :
1822 52188 : Local<ObjectTemplate> performance_template = ObjectTemplate::New(isolate);
1823 : performance_template->Set(
1824 : String::NewFromUtf8(isolate, "now", NewStringType::kNormal)
1825 : .ToLocalChecked(),
1826 156564 : FunctionTemplate::New(isolate, PerformanceNow));
1827 : global_template->Set(
1828 : String::NewFromUtf8(isolate, "performance", NewStringType::kNormal)
1829 : .ToLocalChecked(),
1830 104376 : performance_template);
1831 :
1832 : Local<FunctionTemplate> worker_fun_template =
1833 52188 : FunctionTemplate::New(isolate, WorkerNew);
1834 : Local<Signature> worker_signature =
1835 52188 : Signature::New(isolate, worker_fun_template);
1836 : worker_fun_template->SetClassName(
1837 : String::NewFromUtf8(isolate, "Worker", NewStringType::kNormal)
1838 104376 : .ToLocalChecked());
1839 52188 : worker_fun_template->ReadOnlyPrototype();
1840 104376 : worker_fun_template->PrototypeTemplate()->Set(
1841 : String::NewFromUtf8(isolate, "terminate", NewStringType::kNormal)
1842 : .ToLocalChecked(),
1843 : FunctionTemplate::New(isolate, WorkerTerminate, Local<Value>(),
1844 208752 : worker_signature));
1845 104376 : worker_fun_template->PrototypeTemplate()->Set(
1846 : String::NewFromUtf8(isolate, "postMessage", NewStringType::kNormal)
1847 : .ToLocalChecked(),
1848 : FunctionTemplate::New(isolate, WorkerPostMessage, Local<Value>(),
1849 208752 : worker_signature));
1850 104376 : worker_fun_template->PrototypeTemplate()->Set(
1851 : String::NewFromUtf8(isolate, "getMessage", NewStringType::kNormal)
1852 : .ToLocalChecked(),
1853 : FunctionTemplate::New(isolate, WorkerGetMessage, Local<Value>(),
1854 208752 : worker_signature));
1855 104376 : worker_fun_template->InstanceTemplate()->SetInternalFieldCount(1);
1856 : global_template->Set(
1857 : String::NewFromUtf8(isolate, "Worker", NewStringType::kNormal)
1858 : .ToLocalChecked(),
1859 104376 : worker_fun_template);
1860 :
1861 52188 : Local<ObjectTemplate> os_templ = ObjectTemplate::New(isolate);
1862 52188 : AddOSMethods(isolate, os_templ);
1863 : global_template->Set(
1864 : String::NewFromUtf8(isolate, "os", NewStringType::kNormal)
1865 : .ToLocalChecked(),
1866 104376 : os_templ);
1867 :
1868 52188 : if (i::FLAG_expose_async_hooks) {
1869 90 : Local<ObjectTemplate> async_hooks_templ = ObjectTemplate::New(isolate);
1870 : async_hooks_templ->Set(
1871 : String::NewFromUtf8(isolate, "createHook", NewStringType::kNormal)
1872 : .ToLocalChecked(),
1873 270 : FunctionTemplate::New(isolate, AsyncHooksCreateHook));
1874 : async_hooks_templ->Set(
1875 : String::NewFromUtf8(isolate, "executionAsyncId", NewStringType::kNormal)
1876 : .ToLocalChecked(),
1877 270 : FunctionTemplate::New(isolate, AsyncHooksExecutionAsyncId));
1878 : async_hooks_templ->Set(
1879 : String::NewFromUtf8(isolate, "triggerAsyncId", NewStringType::kNormal)
1880 : .ToLocalChecked(),
1881 270 : FunctionTemplate::New(isolate, AsyncHooksTriggerAsyncId));
1882 : global_template->Set(
1883 : String::NewFromUtf8(isolate, "async_hooks", NewStringType::kNormal)
1884 : .ToLocalChecked(),
1885 180 : async_hooks_templ);
1886 : }
1887 :
1888 52188 : return global_template;
1889 : }
1890 :
1891 6916 : static void PrintNonErrorsMessageCallback(Local<Message> message,
1892 : Local<Value> error) {
1893 : // Nothing to do here for errors, exceptions thrown up to the shell will be
1894 : // reported
1895 : // separately by {Shell::ReportException} after they are caught.
1896 : // Do print other kinds of messages.
1897 6916 : switch (message->ErrorLevel()) {
1898 : case v8::Isolate::kMessageWarning:
1899 : case v8::Isolate::kMessageLog:
1900 : case v8::Isolate::kMessageInfo:
1901 : case v8::Isolate::kMessageDebug: {
1902 : break;
1903 : }
1904 :
1905 : case v8::Isolate::kMessageError: {
1906 : // Ignore errors, printed elsewhere.
1907 6305 : return;
1908 : }
1909 :
1910 : default: {
1911 0 : UNREACHABLE();
1912 : break;
1913 : }
1914 : }
1915 : // Converts a V8 value to a C string.
1916 1222 : auto ToCString = [](const v8::String::Utf8Value& value) {
1917 : return *value ? *value : "<string conversion failed>";
1918 1222 : };
1919 611 : Isolate* isolate = Isolate::GetCurrent();
1920 1222 : v8::String::Utf8Value msg(isolate, message->Get());
1921 : const char* msg_string = ToCString(msg);
1922 : // Print (filename):(line number): (message).
1923 : v8::String::Utf8Value filename(isolate,
1924 1222 : message->GetScriptOrigin().ResourceName());
1925 : const char* filename_string = ToCString(filename);
1926 611 : Maybe<int> maybeline = message->GetLineNumber(isolate->GetCurrentContext());
1927 611 : int linenum = maybeline.IsJust() ? maybeline.FromJust() : -1;
1928 611 : printf("%s:%i: %s\n", filename_string, linenum, msg_string);
1929 : }
1930 :
1931 29591 : void Shell::Initialize(Isolate* isolate) {
1932 : // Set up counters
1933 59182 : if (i::StrLength(i::FLAG_map_counters) != 0)
1934 0 : MapCounters(isolate, i::FLAG_map_counters);
1935 : // Disable default message reporting.
1936 : isolate->AddMessageListenerWithErrorLevel(
1937 : PrintNonErrorsMessageCallback,
1938 : v8::Isolate::kMessageError | v8::Isolate::kMessageWarning |
1939 : v8::Isolate::kMessageInfo | v8::Isolate::kMessageDebug |
1940 29591 : v8::Isolate::kMessageLog);
1941 29591 : }
1942 :
1943 :
1944 51492 : Local<Context> Shell::CreateEvaluationContext(Isolate* isolate) {
1945 : // This needs to be a critical section since this is not thread-safe
1946 : base::MutexGuard lock_guard(context_mutex_.Pointer());
1947 : // Initialize the global objects
1948 51492 : Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
1949 51492 : EscapableHandleScope handle_scope(isolate);
1950 51492 : Local<Context> context = Context::New(isolate, nullptr, global_template);
1951 : DCHECK(!context.IsEmpty());
1952 51492 : InitializeModuleEmbedderData(context);
1953 51492 : if (options.include_arguments) {
1954 : Context::Scope scope(context);
1955 51519 : const std::vector<const char*>& args = options.arguments;
1956 51492 : int size = static_cast<int>(args.size());
1957 51492 : Local<Array> array = Array::New(isolate, size);
1958 51519 : for (int i = 0; i < size; i++) {
1959 : Local<String> arg =
1960 54 : v8::String::NewFromUtf8(isolate, args[i], v8::NewStringType::kNormal)
1961 27 : .ToLocalChecked();
1962 27 : Local<Number> index = v8::Number::New(isolate, i);
1963 54 : array->Set(context, index, arg).FromJust();
1964 : }
1965 : Local<String> name =
1966 : String::NewFromUtf8(isolate, "arguments", NewStringType::kInternalized)
1967 51492 : .ToLocalChecked();
1968 154476 : context->Global()->Set(context, name, array).FromJust();
1969 : }
1970 51492 : return handle_scope.Escape(context);
1971 : }
1972 :
1973 : struct CounterAndKey {
1974 : Counter* counter;
1975 : const char* key;
1976 : };
1977 :
1978 :
1979 : inline bool operator<(const CounterAndKey& lhs, const CounterAndKey& rhs) {
1980 0 : return strcmp(lhs.key, rhs.key) < 0;
1981 : }
1982 :
1983 0 : void Shell::WriteIgnitionDispatchCountersFile(v8::Isolate* isolate) {
1984 0 : HandleScope handle_scope(isolate);
1985 0 : Local<Context> context = Context::New(isolate);
1986 : Context::Scope context_scope(context);
1987 :
1988 : Local<Object> dispatch_counters = reinterpret_cast<i::Isolate*>(isolate)
1989 : ->interpreter()
1990 0 : ->GetDispatchCountersObject();
1991 : std::ofstream dispatch_counters_stream(
1992 0 : i::FLAG_trace_ignition_dispatches_output_file);
1993 : dispatch_counters_stream << *String::Utf8Value(
1994 0 : isolate, JSON::Stringify(context, dispatch_counters).ToLocalChecked());
1995 0 : }
1996 :
1997 : namespace {
1998 0 : int LineFromOffset(Local<debug::Script> script, int offset) {
1999 0 : debug::Location location = script->GetSourceLocation(offset);
2000 0 : return location.GetLineNumber();
2001 : }
2002 :
2003 0 : void WriteLcovDataForRange(std::vector<uint32_t>& lines, int start_line,
2004 : int end_line, uint32_t count) {
2005 : // Ensure space in the array.
2006 0 : lines.resize(std::max(static_cast<size_t>(end_line + 1), lines.size()), 0);
2007 : // Boundary lines could be shared between two functions with different
2008 : // invocation counts. Take the maximum.
2009 0 : lines[start_line] = std::max(lines[start_line], count);
2010 0 : lines[end_line] = std::max(lines[end_line], count);
2011 : // Invocation counts for non-boundary lines are overwritten.
2012 0 : for (int k = start_line + 1; k < end_line; k++) lines[k] = count;
2013 0 : }
2014 :
2015 0 : void WriteLcovDataForNamedRange(std::ostream& sink,
2016 : std::vector<uint32_t>& lines,
2017 : const std::string& name, int start_line,
2018 : int end_line, uint32_t count) {
2019 0 : WriteLcovDataForRange(lines, start_line, end_line, count);
2020 0 : sink << "FN:" << start_line + 1 << "," << name << std::endl;
2021 0 : sink << "FNDA:" << count << "," << name << std::endl;
2022 0 : }
2023 : } // namespace
2024 :
2025 : // Write coverage data in LCOV format. See man page for geninfo(1).
2026 50887 : void Shell::WriteLcovData(v8::Isolate* isolate, const char* file) {
2027 101774 : if (!file) return;
2028 0 : HandleScope handle_scope(isolate);
2029 0 : debug::Coverage coverage = debug::Coverage::CollectPrecise(isolate);
2030 0 : std::ofstream sink(file, std::ofstream::app);
2031 0 : for (size_t i = 0; i < coverage.ScriptCount(); i++) {
2032 0 : debug::Coverage::ScriptData script_data = coverage.GetScriptData(i);
2033 0 : Local<debug::Script> script = script_data.GetScript();
2034 : // Skip unnamed scripts.
2035 : Local<String> name;
2036 0 : if (!script->Name().ToLocal(&name)) continue;
2037 0 : std::string file_name = ToSTLString(isolate, name);
2038 : // Skip scripts not backed by a file.
2039 0 : if (!std::ifstream(file_name).good()) continue;
2040 0 : sink << "SF:";
2041 0 : sink << NormalizePath(file_name, GetWorkingDirectory()) << std::endl;
2042 : std::vector<uint32_t> lines;
2043 0 : for (size_t j = 0; j < script_data.FunctionCount(); j++) {
2044 : debug::Coverage::FunctionData function_data =
2045 0 : script_data.GetFunctionData(j);
2046 :
2047 : // Write function stats.
2048 : {
2049 : debug::Location start =
2050 0 : script->GetSourceLocation(function_data.StartOffset());
2051 : debug::Location end =
2052 0 : script->GetSourceLocation(function_data.EndOffset());
2053 0 : int start_line = start.GetLineNumber();
2054 0 : int end_line = end.GetLineNumber();
2055 0 : uint32_t count = function_data.Count();
2056 :
2057 : Local<String> name;
2058 0 : std::stringstream name_stream;
2059 0 : if (function_data.Name().ToLocal(&name)) {
2060 0 : name_stream << ToSTLString(isolate, name);
2061 : } else {
2062 0 : name_stream << "<" << start_line + 1 << "-";
2063 0 : name_stream << start.GetColumnNumber() << ">";
2064 : }
2065 :
2066 : WriteLcovDataForNamedRange(sink, lines, name_stream.str(), start_line,
2067 0 : end_line, count);
2068 : }
2069 :
2070 : // Process inner blocks.
2071 0 : for (size_t k = 0; k < function_data.BlockCount(); k++) {
2072 0 : debug::Coverage::BlockData block_data = function_data.GetBlockData(k);
2073 0 : int start_line = LineFromOffset(script, block_data.StartOffset());
2074 0 : int end_line = LineFromOffset(script, block_data.EndOffset() - 1);
2075 0 : uint32_t count = block_data.Count();
2076 0 : WriteLcovDataForRange(lines, start_line, end_line, count);
2077 : }
2078 : }
2079 : // Write per-line coverage. LCOV uses 1-based line numbers.
2080 0 : for (size_t i = 0; i < lines.size(); i++) {
2081 0 : sink << "DA:" << (i + 1) << "," << lines[i] << std::endl;
2082 : }
2083 0 : sink << "end_of_record" << std::endl;
2084 0 : }
2085 : }
2086 :
2087 29546 : void Shell::OnExit(v8::Isolate* isolate) {
2088 : // Dump basic block profiling data.
2089 29546 : if (i::FLAG_turbo_profiling) {
2090 0 : i::BasicBlockProfiler* profiler = i::BasicBlockProfiler::Get();
2091 0 : i::StdoutStream{} << *profiler;
2092 : }
2093 29546 : isolate->Dispose();
2094 :
2095 29546 : if (i::FLAG_dump_counters || i::FLAG_dump_counters_nvp) {
2096 0 : const int number_of_counters = static_cast<int>(counter_map_->size());
2097 0 : CounterAndKey* counters = new CounterAndKey[number_of_counters];
2098 : int j = 0;
2099 0 : for (auto map_entry : *counter_map_) {
2100 0 : counters[j].counter = map_entry.second;
2101 0 : counters[j].key = map_entry.first;
2102 0 : j++;
2103 : }
2104 0 : std::sort(counters, counters + number_of_counters);
2105 :
2106 0 : if (i::FLAG_dump_counters_nvp) {
2107 : // Dump counters as name-value pairs.
2108 0 : for (j = 0; j < number_of_counters; j++) {
2109 0 : Counter* counter = counters[j].counter;
2110 0 : const char* key = counters[j].key;
2111 0 : if (counter->is_histogram()) {
2112 : printf("\"c:%s\"=%i\n", key, counter->count());
2113 : printf("\"t:%s\"=%i\n", key, counter->sample_total());
2114 : } else {
2115 : printf("\"%s\"=%i\n", key, counter->count());
2116 : }
2117 : }
2118 : } else {
2119 : // Dump counters in formatted boxes.
2120 : printf(
2121 : "+----------------------------------------------------------------+"
2122 : "-------------+\n");
2123 : printf(
2124 : "| Name |"
2125 : " Value |\n");
2126 : printf(
2127 : "+----------------------------------------------------------------+"
2128 : "-------------+\n");
2129 0 : for (j = 0; j < number_of_counters; j++) {
2130 0 : Counter* counter = counters[j].counter;
2131 0 : const char* key = counters[j].key;
2132 0 : if (counter->is_histogram()) {
2133 : printf("| c:%-60s | %11i |\n", key, counter->count());
2134 : printf("| t:%-60s | %11i |\n", key, counter->sample_total());
2135 : } else {
2136 : printf("| %-62s | %11i |\n", key, counter->count());
2137 : }
2138 : }
2139 : printf(
2140 : "+----------------------------------------------------------------+"
2141 : "-------------+\n");
2142 : }
2143 0 : delete [] counters;
2144 : }
2145 :
2146 29546 : delete counters_file_;
2147 59092 : delete counter_map_;
2148 29546 : }
2149 :
2150 :
2151 17 : static FILE* FOpen(const char* path, const char* mode) {
2152 : #if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64))
2153 : FILE* result;
2154 : if (fopen_s(&result, path, mode) == 0) {
2155 : return result;
2156 : } else {
2157 : return nullptr;
2158 : }
2159 : #else
2160 17 : FILE* file = fopen(path, mode);
2161 17 : if (file == nullptr) return nullptr;
2162 : struct stat file_stat;
2163 34 : if (fstat(fileno(file), &file_stat) != 0) return nullptr;
2164 17 : bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0);
2165 17 : if (is_regular_file) return file;
2166 0 : fclose(file);
2167 0 : return nullptr;
2168 : #endif
2169 : }
2170 :
2171 17 : static char* ReadChars(const char* name, int* size_out) {
2172 17 : if (Shell::options.read_from_tcp_port >= 0) {
2173 0 : return Shell::ReadCharsFromTcpPort(name, size_out);
2174 : }
2175 :
2176 17 : FILE* file = FOpen(name, "rb");
2177 17 : if (file == nullptr) return nullptr;
2178 :
2179 17 : fseek(file, 0, SEEK_END);
2180 17 : size_t size = ftell(file);
2181 17 : rewind(file);
2182 :
2183 17 : char* chars = new char[size + 1];
2184 17 : chars[size] = '\0';
2185 51 : for (size_t i = 0; i < size;) {
2186 34 : i += fread(&chars[i], 1, size - i, file);
2187 17 : if (ferror(file)) {
2188 0 : fclose(file);
2189 0 : delete[] chars;
2190 : return nullptr;
2191 : }
2192 : }
2193 17 : fclose(file);
2194 17 : *size_out = static_cast<int>(size);
2195 17 : return chars;
2196 : }
2197 :
2198 :
2199 : struct DataAndPersistent {
2200 : uint8_t* data;
2201 : int byte_length;
2202 : Global<ArrayBuffer> handle;
2203 : };
2204 :
2205 :
2206 0 : static void ReadBufferWeakCallback(
2207 0 : const v8::WeakCallbackInfo<DataAndPersistent>& data) {
2208 0 : int byte_length = data.GetParameter()->byte_length;
2209 : data.GetIsolate()->AdjustAmountOfExternalAllocatedMemory(
2210 0 : -static_cast<intptr_t>(byte_length));
2211 :
2212 0 : delete[] data.GetParameter()->data;
2213 : data.GetParameter()->handle.Reset();
2214 0 : delete data.GetParameter();
2215 0 : }
2216 :
2217 :
2218 34 : void Shell::ReadBuffer(const v8::FunctionCallbackInfo<v8::Value>& args) {
2219 : static_assert(sizeof(char) == sizeof(uint8_t),
2220 : "char and uint8_t should both have 1 byte");
2221 : Isolate* isolate = args.GetIsolate();
2222 17 : String::Utf8Value filename(isolate, args[0]);
2223 : int length;
2224 17 : if (*filename == nullptr) {
2225 0 : Throw(isolate, "Error loading file");
2226 0 : return;
2227 : }
2228 :
2229 17 : DataAndPersistent* data = new DataAndPersistent;
2230 17 : data->data = reinterpret_cast<uint8_t*>(ReadChars(*filename, &length));
2231 17 : if (data->data == nullptr) {
2232 0 : delete data;
2233 0 : Throw(isolate, "Error reading file");
2234 0 : return;
2235 : }
2236 17 : data->byte_length = length;
2237 17 : Local<v8::ArrayBuffer> buffer = ArrayBuffer::New(isolate, data->data, length);
2238 : data->handle.Reset(isolate, buffer);
2239 : data->handle.SetWeak(data, ReadBufferWeakCallback,
2240 : v8::WeakCallbackType::kParameter);
2241 17 : isolate->AdjustAmountOfExternalAllocatedMemory(length);
2242 :
2243 17 : args.GetReturnValue().Set(buffer);
2244 : }
2245 :
2246 : // Reads a file into a v8 string.
2247 113868 : Local<String> Shell::ReadFile(Isolate* isolate, const char* name) {
2248 : std::unique_ptr<base::OS::MemoryMappedFile> file(
2249 113868 : base::OS::MemoryMappedFile::open(name));
2250 113868 : if (!file) return Local<String>();
2251 :
2252 113774 : int size = static_cast<int>(file->size());
2253 113774 : char* chars = static_cast<char*>(file->memory());
2254 : Local<String> result;
2255 113792 : if (i::FLAG_use_external_strings && i::String::IsAscii(chars, size)) {
2256 : String::ExternalOneByteStringResource* resource =
2257 18 : new ExternalOwningOneByteStringResource(std::move(file));
2258 18 : result = String::NewExternalOneByte(isolate, resource).ToLocalChecked();
2259 : } else {
2260 : result = String::NewFromUtf8(isolate, chars, NewStringType::kNormal, size)
2261 113756 : .ToLocalChecked();
2262 : }
2263 113774 : return result;
2264 : }
2265 :
2266 :
2267 0 : void Shell::RunShell(Isolate* isolate) {
2268 0 : HandleScope outer_scope(isolate);
2269 : v8::Local<v8::Context> context =
2270 : v8::Local<v8::Context>::New(isolate, evaluation_context_);
2271 : v8::Context::Scope context_scope(context);
2272 0 : PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
2273 : Local<String> name =
2274 : String::NewFromUtf8(isolate, "(d8)", NewStringType::kNormal)
2275 0 : .ToLocalChecked();
2276 0 : printf("V8 version %s\n", V8::GetVersion());
2277 : while (true) {
2278 0 : HandleScope inner_scope(isolate);
2279 : printf("d8> ");
2280 0 : Local<String> input = Shell::ReadFromStdin(isolate);
2281 0 : if (input.IsEmpty()) break;
2282 : ExecuteString(isolate, input, name, kPrintResult, kReportExceptions,
2283 0 : kProcessMessageQueue);
2284 0 : }
2285 : printf("\n");
2286 : // We need to explicitly clean up the module embedder data for
2287 : // the interative shell context.
2288 0 : DisposeModuleEmbedderData(context);
2289 0 : }
2290 :
2291 : class InspectorFrontend final : public v8_inspector::V8Inspector::Channel {
2292 : public:
2293 5074 : explicit InspectorFrontend(Local<Context> context) {
2294 2537 : isolate_ = context->GetIsolate();
2295 : context_.Reset(isolate_, context);
2296 2537 : }
2297 5074 : ~InspectorFrontend() override = default;
2298 :
2299 : private:
2300 128011 : void sendResponse(
2301 : int callId,
2302 : std::unique_ptr<v8_inspector::StringBuffer> message) override {
2303 128011 : Send(message->string());
2304 128011 : }
2305 143586 : void sendNotification(
2306 : std::unique_ptr<v8_inspector::StringBuffer> message) override {
2307 143586 : Send(message->string());
2308 143586 : }
2309 0 : void flushProtocolNotifications() override {}
2310 :
2311 1086388 : void Send(const v8_inspector::StringView& string) {
2312 271597 : v8::Isolate::AllowJavascriptExecutionScope allow_script(isolate_);
2313 271597 : int length = static_cast<int>(string.length());
2314 : DCHECK_LT(length, v8::String::kMaxLength);
2315 : Local<String> message =
2316 : (string.is8Bit()
2317 : ? v8::String::NewFromOneByte(
2318 : isolate_,
2319 : reinterpret_cast<const uint8_t*>(string.characters8()),
2320 0 : v8::NewStringType::kNormal, length)
2321 : : v8::String::NewFromTwoByte(
2322 : isolate_,
2323 : reinterpret_cast<const uint16_t*>(string.characters16()),
2324 271597 : v8::NewStringType::kNormal, length))
2325 543194 : .ToLocalChecked();
2326 : Local<String> callback_name =
2327 271597 : v8::String::NewFromUtf8(isolate_, "receive", v8::NewStringType::kNormal)
2328 271597 : .ToLocalChecked();
2329 271597 : Local<Context> context = context_.Get(isolate_);
2330 : Local<Value> callback =
2331 814791 : context->Global()->Get(context, callback_name).ToLocalChecked();
2332 271597 : if (callback->IsFunction()) {
2333 271597 : v8::TryCatch try_catch(isolate_);
2334 : Local<Value> args[] = {message};
2335 543194 : USE(Local<Function>::Cast(callback)->Call(context, Undefined(isolate_), 1,
2336 271597 : args));
2337 : #ifdef DEBUG
2338 : if (try_catch.HasCaught()) {
2339 : Local<Object> exception = Local<Object>::Cast(try_catch.Exception());
2340 : Local<String> key = v8::String::NewFromUtf8(isolate_, "message",
2341 : v8::NewStringType::kNormal)
2342 : .ToLocalChecked();
2343 : Local<String> expected =
2344 : v8::String::NewFromUtf8(isolate_,
2345 : "Maximum call stack size exceeded",
2346 : v8::NewStringType::kNormal)
2347 : .ToLocalChecked();
2348 : Local<Value> value = exception->Get(context, key).ToLocalChecked();
2349 : DCHECK(value->StrictEquals(expected));
2350 : }
2351 : #endif
2352 271597 : }
2353 271597 : }
2354 :
2355 : Isolate* isolate_;
2356 : Global<Context> context_;
2357 : };
2358 :
2359 101774 : class InspectorClient : public v8_inspector::V8InspectorClient {
2360 : public:
2361 101774 : InspectorClient(Local<Context> context, bool connect) {
2362 99237 : if (!connect) return;
2363 2537 : isolate_ = context->GetIsolate();
2364 5074 : channel_.reset(new InspectorFrontend(context));
2365 5074 : inspector_ = v8_inspector::V8Inspector::create(isolate_, this);
2366 5074 : session_ =
2367 2537 : inspector_->connect(1, channel_.get(), v8_inspector::StringView());
2368 2537 : context->SetAlignedPointerInEmbedderData(kInspectorClientIndex, this);
2369 : inspector_->contextCreated(v8_inspector::V8ContextInfo(
2370 5074 : context, kContextGroupId, v8_inspector::StringView()));
2371 :
2372 : Local<Value> function =
2373 5074 : FunctionTemplate::New(isolate_, SendInspectorMessage)
2374 5074 : ->GetFunction(context)
2375 2537 : .ToLocalChecked();
2376 : Local<String> function_name =
2377 2537 : String::NewFromUtf8(isolate_, "send", NewStringType::kNormal)
2378 2537 : .ToLocalChecked();
2379 7611 : CHECK(context->Global()->Set(context, function_name, function).FromJust());
2380 :
2381 2537 : context_.Reset(isolate_, context);
2382 : }
2383 :
2384 : private:
2385 : static v8_inspector::V8InspectorSession* GetSession(Local<Context> context) {
2386 : InspectorClient* inspector_client = static_cast<InspectorClient*>(
2387 : context->GetAlignedPointerFromEmbedderData(kInspectorClientIndex));
2388 : return inspector_client->session_.get();
2389 : }
2390 :
2391 513 : Local<Context> ensureDefaultContextInGroup(int group_id) override {
2392 : DCHECK(isolate_);
2393 : DCHECK_EQ(kContextGroupId, group_id);
2394 1026 : return context_.Get(isolate_);
2395 : }
2396 :
2397 128011 : static void SendInspectorMessage(
2398 384033 : const v8::FunctionCallbackInfo<v8::Value>& args) {
2399 : Isolate* isolate = args.GetIsolate();
2400 128011 : v8::HandleScope handle_scope(isolate);
2401 128011 : Local<Context> context = isolate->GetCurrentContext();
2402 : args.GetReturnValue().Set(Undefined(isolate));
2403 128011 : Local<String> message = args[0]->ToString(context).ToLocalChecked();
2404 : v8_inspector::V8InspectorSession* session =
2405 : InspectorClient::GetSession(context);
2406 128011 : int length = message->Length();
2407 128011 : std::unique_ptr<uint16_t[]> buffer(new uint16_t[length]);
2408 128011 : message->Write(isolate, buffer.get(), 0, length);
2409 : v8_inspector::StringView message_view(buffer.get(), length);
2410 128011 : session->dispatchProtocolMessage(message_view);
2411 128011 : args.GetReturnValue().Set(True(isolate));
2412 128011 : }
2413 :
2414 : static const int kContextGroupId = 1;
2415 :
2416 : std::unique_ptr<v8_inspector::V8Inspector> inspector_;
2417 : std::unique_ptr<v8_inspector::V8InspectorSession> session_;
2418 : std::unique_ptr<v8_inspector::V8Inspector::Channel> channel_;
2419 : Global<Context> context_;
2420 : Isolate* isolate_;
2421 : };
2422 :
2423 59092 : SourceGroup::~SourceGroup() {
2424 29546 : delete thread_;
2425 29546 : thread_ = nullptr;
2426 29546 : }
2427 :
2428 107877 : bool ends_with(const char* input, const char* suffix) {
2429 107877 : size_t input_length = strlen(input);
2430 107877 : size_t suffix_length = strlen(suffix);
2431 107877 : if (suffix_length <= input_length) {
2432 107877 : return strcmp(input + input_length - suffix_length, suffix) == 0;
2433 : }
2434 : return false;
2435 : }
2436 :
2437 50887 : bool SourceGroup::Execute(Isolate* isolate) {
2438 : bool success = true;
2439 157732 : for (int i = begin_offset_; i < end_offset_; ++i) {
2440 109376 : const char* arg = argv_[i];
2441 109376 : if (strcmp(arg, "-e") == 0 && i + 1 < end_offset_) {
2442 : // Execute argument given to -e option directly.
2443 1499 : HandleScope handle_scope(isolate);
2444 : Local<String> file_name =
2445 : String::NewFromUtf8(isolate, "unnamed", NewStringType::kNormal)
2446 1499 : .ToLocalChecked();
2447 : Local<String> source =
2448 1499 : String::NewFromUtf8(isolate, argv_[i + 1], NewStringType::kNormal)
2449 2998 : .ToLocalChecked();
2450 : Shell::set_script_executed();
2451 1499 : if (!Shell::ExecuteString(isolate, source, file_name,
2452 : Shell::kNoPrintResult, Shell::kReportExceptions,
2453 1499 : Shell::kNoProcessMessageQueue)) {
2454 : success = false;
2455 1341 : break;
2456 : }
2457 : ++i;
2458 158 : continue;
2459 107877 : } else if (ends_with(arg, ".mjs")) {
2460 : Shell::set_script_executed();
2461 0 : if (!Shell::ExecuteModule(isolate, arg)) {
2462 : success = false;
2463 : break;
2464 : }
2465 : continue;
2466 107877 : } else if (strcmp(arg, "--module") == 0 && i + 1 < end_offset_) {
2467 : // Treat the next file as a module.
2468 675 : arg = argv_[++i];
2469 : Shell::set_script_executed();
2470 675 : if (!Shell::ExecuteModule(isolate, arg)) {
2471 : success = false;
2472 : break;
2473 : }
2474 : continue;
2475 107202 : } else if (arg[0] == '-') {
2476 : // Ignore other options. They have been parsed already.
2477 : continue;
2478 : }
2479 :
2480 : // Use all other arguments as names of files to load and run.
2481 106250 : HandleScope handle_scope(isolate);
2482 : Local<String> file_name =
2483 : String::NewFromUtf8(isolate, arg, NewStringType::kNormal)
2484 106250 : .ToLocalChecked();
2485 : Local<String> source = ReadFile(isolate, arg);
2486 106250 : if (source.IsEmpty()) {
2487 : printf("Error reading '%s'\n", arg);
2488 0 : base::OS::ExitProcess(1);
2489 : }
2490 : Shell::set_script_executed();
2491 106250 : if (!Shell::ExecuteString(isolate, source, file_name, Shell::kNoPrintResult,
2492 : Shell::kReportExceptions,
2493 106250 : Shell::kProcessMessageQueue)) {
2494 : success = false;
2495 1005 : break;
2496 : }
2497 105245 : }
2498 50887 : return success;
2499 : }
2500 :
2501 0 : Local<String> SourceGroup::ReadFile(Isolate* isolate, const char* name) {
2502 106250 : return Shell::ReadFile(isolate, name);
2503 : }
2504 :
2505 0 : SourceGroup::IsolateThread::IsolateThread(SourceGroup* group)
2506 0 : : base::Thread(GetThreadOptions("IsolateThread")), group_(group) {}
2507 :
2508 0 : void SourceGroup::ExecuteInThread() {
2509 : Isolate::CreateParams create_params;
2510 0 : create_params.array_buffer_allocator = Shell::array_buffer_allocator;
2511 0 : Isolate* isolate = Isolate::New(create_params);
2512 : isolate->SetHostImportModuleDynamicallyCallback(
2513 0 : Shell::HostImportModuleDynamically);
2514 : isolate->SetHostInitializeImportMetaObjectCallback(
2515 0 : Shell::HostInitializeImportMetaObject);
2516 0 : Shell::SetWaitUntilDone(isolate, false);
2517 0 : D8Console console(isolate);
2518 0 : debug::SetConsoleDelegate(isolate, &console);
2519 0 : for (int i = 0; i < Shell::options.stress_runs; ++i) {
2520 0 : next_semaphore_.Wait();
2521 : {
2522 : Isolate::Scope iscope(isolate);
2523 0 : PerIsolateData data(isolate);
2524 : {
2525 0 : HandleScope scope(isolate);
2526 0 : Local<Context> context = Shell::CreateEvaluationContext(isolate);
2527 : {
2528 : Context::Scope cscope(context);
2529 : InspectorClient inspector_client(context,
2530 0 : Shell::options.enable_inspector);
2531 0 : PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
2532 0 : Execute(isolate);
2533 0 : Shell::CompleteMessageLoop(isolate);
2534 : }
2535 0 : DisposeModuleEmbedderData(context);
2536 : }
2537 0 : Shell::CollectGarbage(isolate);
2538 : }
2539 0 : done_semaphore_.Signal();
2540 : }
2541 :
2542 0 : isolate->Dispose();
2543 0 : }
2544 :
2545 :
2546 0 : void SourceGroup::StartExecuteInThread() {
2547 0 : if (thread_ == nullptr) {
2548 0 : thread_ = new IsolateThread(this);
2549 0 : thread_->Start();
2550 : }
2551 0 : next_semaphore_.Signal();
2552 0 : }
2553 :
2554 :
2555 0 : void SourceGroup::WaitForThread() {
2556 0 : if (thread_ == nullptr) return;
2557 0 : done_semaphore_.Wait();
2558 : }
2559 :
2560 :
2561 0 : void SourceGroup::JoinThread() {
2562 0 : if (thread_ == nullptr) return;
2563 0 : thread_->Join();
2564 : }
2565 :
2566 0 : ExternalizedContents::~ExternalizedContents() {
2567 390 : if (data_ != nullptr) {
2568 148 : deleter_(data_, length_, deleter_data_);
2569 : }
2570 0 : }
2571 :
2572 3248 : void SerializationDataQueue::Enqueue(std::unique_ptr<SerializationData> data) {
2573 3248 : base::MutexGuard lock_guard(&mutex_);
2574 3248 : data_.push_back(std::move(data));
2575 3248 : }
2576 :
2577 2751 : bool SerializationDataQueue::Dequeue(
2578 : std::unique_ptr<SerializationData>* out_data) {
2579 : out_data->reset();
2580 2751 : base::MutexGuard lock_guard(&mutex_);
2581 2750 : if (data_.empty()) return false;
2582 : *out_data = std::move(data_[0]);
2583 2115 : data_.erase(data_.begin());
2584 2116 : return true;
2585 : }
2586 :
2587 :
2588 0 : bool SerializationDataQueue::IsEmpty() {
2589 0 : base::MutexGuard lock_guard(&mutex_);
2590 0 : return data_.empty();
2591 : }
2592 :
2593 :
2594 1210 : void SerializationDataQueue::Clear() {
2595 1210 : base::MutexGuard lock_guard(&mutex_);
2596 : data_.clear();
2597 1210 : }
2598 :
2599 605 : Worker::Worker()
2600 : : in_semaphore_(0),
2601 : out_semaphore_(0),
2602 : thread_(nullptr),
2603 : script_(nullptr),
2604 1210 : running_(false) {}
2605 :
2606 1210 : Worker::~Worker() {
2607 605 : delete thread_;
2608 605 : thread_ = nullptr;
2609 605 : delete[] script_;
2610 605 : script_ = nullptr;
2611 605 : in_queue_.Clear();
2612 605 : out_queue_.Clear();
2613 605 : }
2614 :
2615 :
2616 605 : void Worker::StartExecuteInThread(const char* script) {
2617 605 : running_ = true;
2618 605 : script_ = i::StrDup(script);
2619 1210 : thread_ = new WorkerThread(this);
2620 605 : thread_->Start();
2621 605 : }
2622 :
2623 1663 : void Worker::PostMessage(std::unique_ptr<SerializationData> data) {
2624 3326 : in_queue_.Enqueue(std::move(data));
2625 1663 : in_semaphore_.Signal();
2626 1663 : }
2627 :
2628 1007 : std::unique_ptr<SerializationData> Worker::GetMessage() {
2629 1007 : std::unique_ptr<SerializationData> result;
2630 2640 : while (!out_queue_.Dequeue(&result)) {
2631 : // If the worker is no longer running, and there are no messages in the
2632 : // queue, don't expect any more messages from it.
2633 1270 : if (!base::Relaxed_Load(&running_)) break;
2634 626 : out_semaphore_.Wait();
2635 : }
2636 1007 : return result;
2637 : }
2638 :
2639 :
2640 1016 : void Worker::Terminate() {
2641 1016 : base::Relaxed_Store(&running_, false);
2642 : // Post nullptr to wake the Worker thread message loop, and tell it to stop
2643 : // running.
2644 2032 : PostMessage(nullptr);
2645 1016 : }
2646 :
2647 :
2648 0 : void Worker::WaitForThread() {
2649 605 : Terminate();
2650 605 : thread_->Join();
2651 0 : }
2652 :
2653 :
2654 605 : void Worker::ExecuteInThread() {
2655 : Isolate::CreateParams create_params;
2656 605 : create_params.array_buffer_allocator = Shell::array_buffer_allocator;
2657 605 : Isolate* isolate = Isolate::New(create_params);
2658 : isolate->SetHostImportModuleDynamicallyCallback(
2659 605 : Shell::HostImportModuleDynamically);
2660 : isolate->SetHostInitializeImportMetaObjectCallback(
2661 605 : Shell::HostInitializeImportMetaObject);
2662 605 : D8Console console(isolate);
2663 605 : debug::SetConsoleDelegate(isolate, &console);
2664 : {
2665 : Isolate::Scope iscope(isolate);
2666 : {
2667 605 : HandleScope scope(isolate);
2668 1210 : PerIsolateData data(isolate);
2669 605 : Local<Context> context = Shell::CreateEvaluationContext(isolate);
2670 : {
2671 : Context::Scope cscope(context);
2672 1210 : PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
2673 :
2674 605 : Local<Object> global = context->Global();
2675 605 : Local<Value> this_value = External::New(isolate, this);
2676 : Local<FunctionTemplate> postmessage_fun_template =
2677 605 : FunctionTemplate::New(isolate, PostMessageOut, this_value);
2678 :
2679 : Local<Function> postmessage_fun;
2680 605 : if (postmessage_fun_template->GetFunction(context)
2681 605 : .ToLocal(&postmessage_fun)) {
2682 : global->Set(context, String::NewFromUtf8(isolate, "postMessage",
2683 : NewStringType::kNormal)
2684 : .ToLocalChecked(),
2685 1815 : postmessage_fun).FromJust();
2686 : }
2687 :
2688 : // First run the script
2689 : Local<String> file_name =
2690 : String::NewFromUtf8(isolate, "unnamed", NewStringType::kNormal)
2691 605 : .ToLocalChecked();
2692 : Local<String> source =
2693 605 : String::NewFromUtf8(isolate, script_, NewStringType::kNormal)
2694 1210 : .ToLocalChecked();
2695 1210 : if (Shell::ExecuteString(
2696 : isolate, source, file_name, Shell::kNoPrintResult,
2697 605 : Shell::kReportExceptions, Shell::kProcessMessageQueue)) {
2698 : // Get the message handler
2699 : Local<Value> onmessage =
2700 : global->Get(context, String::NewFromUtf8(isolate, "onmessage",
2701 : NewStringType::kNormal)
2702 1815 : .ToLocalChecked()).ToLocalChecked();
2703 605 : if (onmessage->IsFunction()) {
2704 : Local<Function> onmessage_fun = Local<Function>::Cast(onmessage);
2705 : // Now wait for messages
2706 : while (true) {
2707 1117 : in_semaphore_.Wait();
2708 1118 : std::unique_ptr<SerializationData> data;
2709 1118 : if (!in_queue_.Dequeue(&data)) continue;
2710 1118 : if (!data) {
2711 : break;
2712 : }
2713 1203 : v8::TryCatch try_catch(isolate);
2714 : Local<Value> value;
2715 602 : if (Shell::DeserializeValue(isolate, std::move(data))
2716 602 : .ToLocal(&value)) {
2717 602 : Local<Value> argv[] = {value};
2718 : MaybeLocal<Value> result =
2719 602 : onmessage_fun->Call(context, global, 1, argv);
2720 : USE(result);
2721 : }
2722 600 : if (try_catch.HasCaught()) {
2723 9 : Shell::ReportException(isolate, &try_catch);
2724 : }
2725 : }
2726 : }
2727 : }
2728 : }
2729 1210 : DisposeModuleEmbedderData(context);
2730 : }
2731 605 : Shell::CollectGarbage(isolate);
2732 : }
2733 605 : isolate->Dispose();
2734 :
2735 : // Post nullptr to wake the thread waiting on GetMessage() if there is one.
2736 1210 : out_queue_.Enqueue(nullptr);
2737 605 : out_semaphore_.Signal();
2738 605 : }
2739 :
2740 :
2741 2928 : void Worker::PostMessageOut(const v8::FunctionCallbackInfo<v8::Value>& args) {
2742 : Isolate* isolate = args.GetIsolate();
2743 973 : HandleScope handle_scope(isolate);
2744 :
2745 975 : if (args.Length() < 1) {
2746 0 : Throw(isolate, "Invalid argument");
2747 978 : return;
2748 : }
2749 :
2750 975 : Local<Value> message = args[0];
2751 : Local<Value> transfer = Undefined(isolate);
2752 : std::unique_ptr<SerializationData> data =
2753 975 : Shell::SerializeValue(isolate, message, transfer);
2754 980 : if (data) {
2755 : DCHECK(args.Data()->IsExternal());
2756 : Local<External> this_value = Local<External>::Cast(args.Data());
2757 980 : Worker* worker = static_cast<Worker*>(this_value->Value());
2758 1960 : worker->out_queue_.Enqueue(std::move(data));
2759 980 : worker->out_semaphore_.Signal();
2760 979 : }
2761 : }
2762 :
2763 :
2764 88637 : void SetFlagsFromString(const char* flags) {
2765 88637 : v8::V8::SetFlagsFromString(flags, static_cast<int>(strlen(flags)));
2766 88637 : }
2767 :
2768 :
2769 29546 : bool Shell::SetOptions(int argc, char* argv[]) {
2770 : bool logfile_per_isolate = false;
2771 259705 : for (int i = 0; i < argc; i++) {
2772 230164 : if (strcmp(argv[i], "--") == 0) {
2773 5 : argv[i] = nullptr;
2774 20 : for (int j = i + 1; j < argc; j++) {
2775 30 : options.arguments.push_back(argv[j]);
2776 15 : argv[j] = nullptr;
2777 : }
2778 : break;
2779 230159 : } else if (strcmp(argv[i], "--no-arguments") == 0) {
2780 0 : options.include_arguments = false;
2781 0 : argv[i] = nullptr;
2782 230159 : } else if (strcmp(argv[i], "--stress-opt") == 0) {
2783 5733 : options.stress_opt = true;
2784 5733 : argv[i] = nullptr;
2785 448792 : } else if (strcmp(argv[i], "--nostress-opt") == 0 ||
2786 224366 : strcmp(argv[i], "--no-stress-opt") == 0) {
2787 364 : options.stress_opt = false;
2788 364 : argv[i] = nullptr;
2789 224062 : } else if (strcmp(argv[i], "--stress-deopt") == 0) {
2790 0 : options.stress_deopt = true;
2791 0 : argv[i] = nullptr;
2792 224062 : } else if (strcmp(argv[i], "--stress-background-compile") == 0) {
2793 5730 : options.stress_background_compile = true;
2794 5730 : argv[i] = nullptr;
2795 436664 : } else if (strcmp(argv[i], "--nostress-background-compile") == 0 ||
2796 218332 : strcmp(argv[i], "--no-stress-background-compile") == 0) {
2797 9 : options.stress_background_compile = false;
2798 9 : argv[i] = nullptr;
2799 436506 : } else if (strcmp(argv[i], "--noalways-opt") == 0 ||
2800 218183 : strcmp(argv[i], "--no-always-opt") == 0) {
2801 : // No support for stressing if we can't use --always-opt.
2802 559 : options.stress_opt = false;
2803 559 : options.stress_deopt = false;
2804 217764 : } else if (strcmp(argv[i], "--logfile-per-isolate") == 0) {
2805 : logfile_per_isolate = true;
2806 0 : argv[i] = nullptr;
2807 217764 : } else if (strcmp(argv[i], "--shell") == 0) {
2808 0 : options.interactive_shell = true;
2809 0 : argv[i] = nullptr;
2810 217764 : } else if (strcmp(argv[i], "--test") == 0) {
2811 29546 : options.test_shell = true;
2812 29546 : argv[i] = nullptr;
2813 376436 : } else if (strcmp(argv[i], "--notest") == 0 ||
2814 188218 : strcmp(argv[i], "--no-test") == 0) {
2815 10 : options.test_shell = false;
2816 10 : argv[i] = nullptr;
2817 188208 : } else if (strcmp(argv[i], "--send-idle-notification") == 0) {
2818 5 : options.send_idle_notification = true;
2819 5 : argv[i] = nullptr;
2820 188203 : } else if (strcmp(argv[i], "--invoke-weak-callbacks") == 0) {
2821 32 : options.invoke_weak_callbacks = true;
2822 : // TODO(jochen) See issue 3351
2823 32 : options.send_idle_notification = true;
2824 32 : argv[i] = nullptr;
2825 188171 : } else if (strcmp(argv[i], "--omit-quit") == 0) {
2826 18 : options.omit_quit = true;
2827 18 : argv[i] = nullptr;
2828 188153 : } else if (strcmp(argv[i], "--no-wait-for-wasm") == 0) {
2829 : // TODO(herhut) Remove this flag once wasm compilation is fully
2830 : // isolate-independent.
2831 8 : options.wait_for_wasm = false;
2832 8 : argv[i] = nullptr;
2833 188145 : } else if (strcmp(argv[i], "-f") == 0) {
2834 : // Ignore any -f flags for compatibility with other stand-alone
2835 : // JavaScript engines.
2836 : continue;
2837 188145 : } else if (strcmp(argv[i], "--isolate") == 0) {
2838 0 : options.num_isolates++;
2839 188145 : } else if (strcmp(argv[i], "--throws") == 0) {
2840 1350 : options.expected_to_throw = true;
2841 1350 : argv[i] = nullptr;
2842 186795 : } else if (strncmp(argv[i], "--icu-data-file=", 16) == 0) {
2843 0 : options.icu_data_file = argv[i] + 16;
2844 0 : argv[i] = nullptr;
2845 : #ifdef V8_USE_EXTERNAL_STARTUP_DATA
2846 186795 : } else if (strncmp(argv[i], "--natives_blob=", 15) == 0) {
2847 0 : options.natives_blob = argv[i] + 15;
2848 0 : argv[i] = nullptr;
2849 186795 : } else if (strncmp(argv[i], "--snapshot_blob=", 16) == 0) {
2850 0 : options.snapshot_blob = argv[i] + 16;
2851 0 : argv[i] = nullptr;
2852 : #endif // V8_USE_EXTERNAL_STARTUP_DATA
2853 373590 : } else if (strcmp(argv[i], "--cache") == 0 ||
2854 186795 : strncmp(argv[i], "--cache=", 8) == 0) {
2855 57 : const char* value = argv[i] + 7;
2856 57 : if (!*value || strncmp(value, "=code", 6) == 0) {
2857 52 : options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
2858 : options.code_cache_options =
2859 52 : ShellOptions::CodeCacheOptions::kProduceCache;
2860 5 : } else if (strncmp(value, "=none", 6) == 0) {
2861 0 : options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
2862 : options.code_cache_options =
2863 0 : ShellOptions::CodeCacheOptions::kNoProduceCache;
2864 5 : } else if (strncmp(value, "=after-execute", 15) == 0) {
2865 5 : options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
2866 : options.code_cache_options =
2867 5 : ShellOptions::CodeCacheOptions::kProduceCacheAfterExecute;
2868 0 : } else if (strncmp(value, "=full-code-cache", 17) == 0) {
2869 0 : options.compile_options = v8::ScriptCompiler::kEagerCompile;
2870 : options.code_cache_options =
2871 0 : ShellOptions::CodeCacheOptions::kProduceCache;
2872 : } else {
2873 : printf("Unknown option to --cache.\n");
2874 0 : return false;
2875 : }
2876 57 : argv[i] = nullptr;
2877 186738 : } else if (strcmp(argv[i], "--enable-tracing") == 0) {
2878 1 : options.trace_enabled = true;
2879 1 : argv[i] = nullptr;
2880 186737 : } else if (strncmp(argv[i], "--trace-path=", 13) == 0) {
2881 1 : options.trace_path = argv[i] + 13;
2882 1 : argv[i] = nullptr;
2883 186736 : } else if (strncmp(argv[i], "--trace-config=", 15) == 0) {
2884 0 : options.trace_config = argv[i] + 15;
2885 0 : argv[i] = nullptr;
2886 186736 : } else if (strcmp(argv[i], "--enable-inspector") == 0) {
2887 1467 : options.enable_inspector = true;
2888 1467 : argv[i] = nullptr;
2889 185269 : } else if (strncmp(argv[i], "--lcov=", 7) == 0) {
2890 0 : options.lcov_file = argv[i] + 7;
2891 0 : argv[i] = nullptr;
2892 185269 : } else if (strcmp(argv[i], "--disable-in-process-stack-traces") == 0) {
2893 10 : options.disable_in_process_stack_traces = true;
2894 10 : argv[i] = nullptr;
2895 : #ifdef V8_OS_POSIX
2896 185259 : } else if (strncmp(argv[i], "--read-from-tcp-port=", 21) == 0) {
2897 0 : options.read_from_tcp_port = atoi(argv[i] + 21);
2898 0 : argv[i] = nullptr;
2899 : #endif // V8_OS_POSIX
2900 185259 : } else if (strcmp(argv[i], "--enable-os-system") == 0) {
2901 0 : options.enable_os_system = true;
2902 0 : argv[i] = nullptr;
2903 185259 : } else if (strcmp(argv[i], "--quiet-load") == 0) {
2904 0 : options.quiet_load = true;
2905 0 : argv[i] = nullptr;
2906 185259 : } else if (strncmp(argv[i], "--thread-pool-size=", 19) == 0) {
2907 0 : options.thread_pool_size = atoi(argv[i] + 19);
2908 0 : argv[i] = nullptr;
2909 185259 : } else if (strcmp(argv[i], "--stress-delay-tasks") == 0) {
2910 : // Delay execution of tasks by 0-100ms randomly (based on --random-seed).
2911 0 : options.stress_delay_tasks = true;
2912 0 : argv[i] = nullptr;
2913 : }
2914 : }
2915 :
2916 29546 : v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
2917 29546 : options.mock_arraybuffer_allocator = i::FLAG_mock_arraybuffer_allocator;
2918 : options.mock_arraybuffer_allocator_limit =
2919 29546 : i::FLAG_mock_arraybuffer_allocator_limit;
2920 :
2921 : // Set up isolated source groups.
2922 29546 : options.isolate_sources = new SourceGroup[options.num_isolates];
2923 : SourceGroup* current = options.isolate_sources;
2924 : current->Begin(argv, 1);
2925 93671 : for (int i = 1; i < argc; i++) {
2926 64125 : const char* str = argv[i];
2927 64125 : if (strcmp(str, "--isolate") == 0) {
2928 : current->End(i);
2929 0 : current++;
2930 0 : current->Begin(argv, i + 1);
2931 64125 : } else if (strcmp(str, "--module") == 0) {
2932 : // Pass on to SourceGroup, which understands this option.
2933 63666 : } else if (strncmp(str, "--", 2) == 0) {
2934 : printf("Warning: unknown flag %s.\nTry --help for options\n", str);
2935 63134 : } else if (strcmp(str, "-e") == 0 && i + 1 < argc) {
2936 : set_script_executed();
2937 61643 : } else if (strncmp(str, "-", 1) != 0) {
2938 : // Not a flag, so it must be a script to execute.
2939 : set_script_executed();
2940 : }
2941 : }
2942 : current->End(argc);
2943 :
2944 29546 : if (!logfile_per_isolate && options.num_isolates) {
2945 29546 : SetFlagsFromString("--nologfile_per_isolate");
2946 : }
2947 :
2948 : return true;
2949 : }
2950 :
2951 50887 : int Shell::RunMain(Isolate* isolate, int argc, char* argv[], bool last_run) {
2952 50887 : for (int i = 1; i < options.num_isolates; ++i) {
2953 0 : options.isolate_sources[i].StartExecuteInThread();
2954 : }
2955 : bool success = true;
2956 : {
2957 50887 : SetWaitUntilDone(isolate, false);
2958 50887 : if (options.lcov_file) {
2959 0 : debug::Coverage::SelectMode(isolate, debug::Coverage::kBlockCount);
2960 : }
2961 50887 : HandleScope scope(isolate);
2962 50887 : Local<Context> context = CreateEvaluationContext(isolate);
2963 50887 : bool use_existing_context = last_run && use_interactive_shell();
2964 50887 : if (use_existing_context) {
2965 : // Keep using the same context in the interactive shell.
2966 : evaluation_context_.Reset(isolate, context);
2967 : }
2968 : {
2969 : Context::Scope cscope(context);
2970 101774 : InspectorClient inspector_client(context, options.enable_inspector);
2971 101774 : PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
2972 50887 : if (!options.isolate_sources[0].Execute(isolate)) success = false;
2973 50887 : if (!CompleteMessageLoop(isolate)) success = false;
2974 : }
2975 50887 : if (!use_existing_context) {
2976 50887 : DisposeModuleEmbedderData(context);
2977 : }
2978 50887 : WriteLcovData(isolate, options.lcov_file);
2979 : }
2980 50887 : CollectGarbage(isolate);
2981 50887 : for (int i = 1; i < options.num_isolates; ++i) {
2982 0 : if (last_run) {
2983 0 : options.isolate_sources[i].JoinThread();
2984 : } else {
2985 0 : options.isolate_sources[i].WaitForThread();
2986 : }
2987 : }
2988 50887 : CleanupWorkers();
2989 : // In order to finish successfully, success must be != expected_to_throw.
2990 50887 : return success == Shell::options.expected_to_throw ? 1 : 0;
2991 : }
2992 :
2993 :
2994 81038 : void Shell::CollectGarbage(Isolate* isolate) {
2995 81038 : if (options.send_idle_notification) {
2996 : const double kLongIdlePauseInSeconds = 1.0;
2997 142 : isolate->ContextDisposedNotification();
2998 : isolate->IdleNotificationDeadline(
2999 142 : g_platform->MonotonicallyIncreasingTime() + kLongIdlePauseInSeconds);
3000 : }
3001 81038 : if (options.invoke_weak_callbacks) {
3002 : // By sending a low memory notifications, we will try hard to collect all
3003 : // garbage and will therefore also invoke all weak callbacks of actually
3004 : // unreachable persistent handles.
3005 97 : isolate->LowMemoryNotification();
3006 : }
3007 81038 : }
3008 :
3009 53184 : void Shell::SetWaitUntilDone(Isolate* isolate, bool value) {
3010 : base::MutexGuard guard(isolate_status_lock_.Pointer());
3011 53184 : if (isolate_status_.count(isolate) == 0) {
3012 59182 : isolate_status_.insert(std::make_pair(isolate, value));
3013 : } else {
3014 23593 : isolate_status_[isolate] = value;
3015 : }
3016 53184 : }
3017 :
3018 : namespace {
3019 158004 : bool ProcessMessages(
3020 : Isolate* isolate,
3021 : const std::function<platform::MessageLoopBehavior()>& behavior) {
3022 : while (true) {
3023 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
3024 159240 : i::SaveAndSwitchContext saved_context(i_isolate, i::Context());
3025 160476 : SealHandleScope shs(isolate);
3026 359483 : while (v8::platform::PumpMessageLoop(g_default_platform, isolate,
3027 200243 : behavior())) {
3028 41003 : MicrotasksScope::PerformCheckpoint(isolate);
3029 : }
3030 159240 : if (g_default_platform->IdleTasksEnabled(isolate)) {
3031 : v8::platform::RunIdleTasks(g_default_platform, isolate,
3032 159240 : 50.0 / base::Time::kMillisecondsPerSecond);
3033 : }
3034 160476 : HandleScope handle_scope(isolate);
3035 : PerIsolateData* data = PerIsolateData::Get(isolate);
3036 : Local<Function> callback;
3037 318480 : if (!data->GetTimeoutCallback().ToLocal(&callback)) break;
3038 : Local<Context> context;
3039 2530 : if (!data->GetTimeoutContext().ToLocal(&context)) break;
3040 2501 : TryCatch try_catch(isolate);
3041 1265 : try_catch.SetVerbose(true);
3042 : Context::Scope context_scope(context);
3043 2530 : if (callback->Call(context, Undefined(isolate), 0, nullptr).IsEmpty()) {
3044 29 : Shell::ReportException(isolate, &try_catch);
3045 : return false;
3046 : }
3047 : }
3048 157975 : return true;
3049 : }
3050 : } // anonymous namespace
3051 :
3052 50887 : bool Shell::CompleteMessageLoop(Isolate* isolate) {
3053 54364 : auto get_waiting_behaviour = [isolate]() {
3054 : base::MutexGuard guard(isolate_status_lock_.Pointer());
3055 : DCHECK_GT(isolate_status_.count(isolate), 0);
3056 54364 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
3057 : i::wasm::WasmEngine* wasm_engine = i_isolate->wasm_engine();
3058 54345 : bool should_wait = (options.wait_for_wasm &&
3059 106621 : wasm_engine->HasRunningCompileJob(i_isolate)) ||
3060 52257 : isolate_status_[isolate];
3061 : return should_wait ? platform::MessageLoopBehavior::kWaitForWork
3062 108728 : : platform::MessageLoopBehavior::kDoNotWait;
3063 : };
3064 101774 : return ProcessMessages(isolate, get_waiting_behaviour);
3065 : }
3066 :
3067 107117 : bool Shell::EmptyMessageQueues(Isolate* isolate) {
3068 : return ProcessMessages(
3069 214234 : isolate, []() { return platform::MessageLoopBehavior::kDoNotWait; });
3070 : }
3071 :
3072 5448 : class Serializer : public ValueSerializer::Delegate {
3073 : public:
3074 1810 : explicit Serializer(Isolate* isolate)
3075 : : isolate_(isolate),
3076 : serializer_(isolate, this),
3077 3619 : current_memory_usage_(0) {}
3078 :
3079 1807 : Maybe<bool> WriteValue(Local<Context> context, Local<Value> value,
3080 : Local<Value> transfer) {
3081 : bool ok;
3082 : DCHECK(!data_);
3083 1807 : data_.reset(new SerializationData);
3084 3627 : if (!PrepareTransfer(context, transfer).To(&ok)) {
3085 : return Nothing<bool>();
3086 : }
3087 1650 : serializer_.WriteHeader();
3088 :
3089 3292 : if (!serializer_.WriteValue(context, value).To(&ok)) {
3090 : data_.reset();
3091 : return Nothing<bool>();
3092 : }
3093 :
3094 3256 : if (!FinalizeTransfer().To(&ok)) {
3095 : return Nothing<bool>();
3096 : }
3097 :
3098 1619 : std::pair<uint8_t*, size_t> pair = serializer_.Release();
3099 1622 : data_->data_.reset(pair.first);
3100 1622 : data_->size_ = pair.second;
3101 : return Just(true);
3102 : }
3103 :
3104 : std::unique_ptr<SerializationData> Release() { return std::move(data_); }
3105 :
3106 1816 : void AppendExternalizedContentsTo(std::vector<ExternalizedContents>* to) {
3107 : to->insert(to->end(),
3108 : std::make_move_iterator(externalized_contents_.begin()),
3109 1816 : std::make_move_iterator(externalized_contents_.end()));
3110 : externalized_contents_.clear();
3111 1816 : }
3112 :
3113 : protected:
3114 : // Implements ValueSerializer::Delegate.
3115 9 : void ThrowDataCloneError(Local<String> message) override {
3116 9 : isolate_->ThrowException(Exception::Error(message));
3117 9 : }
3118 :
3119 369 : Maybe<uint32_t> GetSharedArrayBufferId(
3120 : Isolate* isolate, Local<SharedArrayBuffer> shared_array_buffer) override {
3121 : DCHECK_NOT_NULL(data_);
3122 738 : for (size_t index = 0; index < shared_array_buffers_.size(); ++index) {
3123 369 : if (shared_array_buffers_[index] == shared_array_buffer) {
3124 0 : return Just<uint32_t>(static_cast<uint32_t>(index));
3125 : }
3126 : }
3127 :
3128 : size_t index = shared_array_buffers_.size();
3129 369 : shared_array_buffers_.emplace_back(isolate_, shared_array_buffer);
3130 : data_->shared_array_buffer_contents_.push_back(
3131 1107 : MaybeExternalize(shared_array_buffer));
3132 369 : return Just<uint32_t>(static_cast<uint32_t>(index));
3133 : }
3134 :
3135 92 : Maybe<uint32_t> GetWasmModuleTransferId(
3136 : Isolate* isolate, Local<WasmModuleObject> module) override {
3137 : DCHECK_NOT_NULL(data_);
3138 184 : for (size_t index = 0; index < wasm_modules_.size(); ++index) {
3139 92 : if (wasm_modules_[index] == module) {
3140 0 : return Just<uint32_t>(static_cast<uint32_t>(index));
3141 : }
3142 : }
3143 :
3144 : size_t index = wasm_modules_.size();
3145 92 : wasm_modules_.emplace_back(isolate_, module);
3146 276 : data_->transferrable_modules_.push_back(module->GetTransferrableModule());
3147 92 : return Just<uint32_t>(static_cast<uint32_t>(index));
3148 : }
3149 :
3150 1691 : void* ReallocateBufferMemory(void* old_buffer, size_t size,
3151 : size_t* actual_size) override {
3152 : // Not accurate, because we don't take into account reallocated buffers,
3153 : // but this is fine for testing.
3154 1691 : current_memory_usage_ += size;
3155 1691 : if (current_memory_usage_ > kMaxSerializerMemoryUsage) return nullptr;
3156 :
3157 1698 : void* result = realloc(old_buffer, size);
3158 1698 : *actual_size = result ? size : 0;
3159 1698 : return result;
3160 : }
3161 :
3162 27 : void FreeBufferMemory(void* buffer) override { free(buffer); }
3163 :
3164 : private:
3165 1814 : Maybe<bool> PrepareTransfer(Local<Context> context, Local<Value> transfer) {
3166 1814 : if (transfer->IsArray()) {
3167 : Local<Array> transfer_array = Local<Array>::Cast(transfer);
3168 198 : uint32_t length = transfer_array->Length();
3169 243 : for (uint32_t i = 0; i < length; ++i) {
3170 : Local<Value> element;
3171 414 : if (transfer_array->Get(context, i).ToLocal(&element)) {
3172 198 : if (!element->IsArrayBuffer()) {
3173 144 : Throw(isolate_, "Transfer array elements must be an ArrayBuffer");
3174 153 : return Nothing<bool>();
3175 : }
3176 :
3177 54 : Local<ArrayBuffer> array_buffer = Local<ArrayBuffer>::Cast(element);
3178 :
3179 54 : if (std::find(array_buffers_.begin(), array_buffers_.end(),
3180 45 : array_buffer) != array_buffers_.end()) {
3181 : Throw(isolate_,
3182 9 : "ArrayBuffer occurs in the transfer array more than once");
3183 : return Nothing<bool>();
3184 : }
3185 :
3186 : serializer_.TransferArrayBuffer(
3187 45 : static_cast<uint32_t>(array_buffers_.size()), array_buffer);
3188 45 : array_buffers_.emplace_back(isolate_, array_buffer);
3189 : } else {
3190 : return Nothing<bool>();
3191 : }
3192 : }
3193 : return Just(true);
3194 1614 : } else if (transfer->IsUndefined()) {
3195 : return Just(true);
3196 : } else {
3197 0 : Throw(isolate_, "Transfer list must be an Array or undefined");
3198 : return Nothing<bool>();
3199 : }
3200 : }
3201 :
3202 : template <typename T>
3203 396 : typename T::Contents MaybeExternalize(Local<T> array_buffer) {
3204 396 : if (array_buffer->IsExternal()) {
3205 230 : return array_buffer->GetContents();
3206 : } else {
3207 166 : typename T::Contents contents = array_buffer->Externalize();
3208 166 : externalized_contents_.emplace_back(contents);
3209 166 : return contents;
3210 : }
3211 : }
3212 :
3213 1626 : Maybe<bool> FinalizeTransfer() {
3214 3279 : for (const auto& global_array_buffer : array_buffers_) {
3215 : Local<ArrayBuffer> array_buffer =
3216 36 : Local<ArrayBuffer>::New(isolate_, global_array_buffer);
3217 36 : if (!array_buffer->IsDetachable()) {
3218 9 : Throw(isolate_, "ArrayBuffer could not be transferred");
3219 9 : return Nothing<bool>();
3220 : }
3221 :
3222 27 : ArrayBuffer::Contents contents = MaybeExternalize(array_buffer);
3223 27 : array_buffer->Detach();
3224 27 : data_->array_buffer_contents_.push_back(contents);
3225 : }
3226 :
3227 : return Just(true);
3228 : }
3229 :
3230 : Isolate* isolate_;
3231 : ValueSerializer serializer_;
3232 : std::unique_ptr<SerializationData> data_;
3233 : std::vector<Global<ArrayBuffer>> array_buffers_;
3234 : std::vector<Global<SharedArrayBuffer>> shared_array_buffers_;
3235 : std::vector<Global<WasmModuleObject>> wasm_modules_;
3236 : std::vector<ExternalizedContents> externalized_contents_;
3237 : size_t current_memory_usage_;
3238 :
3239 : DISALLOW_COPY_AND_ASSIGN(Serializer);
3240 : };
3241 :
3242 4746 : class Deserializer : public ValueDeserializer::Delegate {
3243 : public:
3244 1582 : Deserializer(Isolate* isolate, std::unique_ptr<SerializationData> data)
3245 : : isolate_(isolate),
3246 : deserializer_(isolate, data->data(), data->size(), this),
3247 4746 : data_(std::move(data)) {
3248 1582 : deserializer_.SetSupportsLegacyWireFormat(true);
3249 1582 : }
3250 :
3251 1582 : MaybeLocal<Value> ReadValue(Local<Context> context) {
3252 : bool read_header;
3253 3164 : if (!deserializer_.ReadHeader(context).To(&read_header)) {
3254 0 : return MaybeLocal<Value>();
3255 : }
3256 :
3257 : uint32_t index = 0;
3258 3191 : for (const auto& contents : data_->array_buffer_contents()) {
3259 : Local<ArrayBuffer> array_buffer =
3260 27 : ArrayBuffer::New(isolate_, contents.Data(), contents.ByteLength());
3261 27 : deserializer_.TransferArrayBuffer(index++, array_buffer);
3262 : }
3263 :
3264 1582 : return deserializer_.ReadValue(context);
3265 : }
3266 :
3267 369 : MaybeLocal<SharedArrayBuffer> GetSharedArrayBufferFromId(
3268 : Isolate* isolate, uint32_t clone_id) override {
3269 : DCHECK_NOT_NULL(data_);
3270 1107 : if (clone_id < data_->shared_array_buffer_contents().size()) {
3271 : const SharedArrayBuffer::Contents contents =
3272 369 : data_->shared_array_buffer_contents().at(clone_id);
3273 369 : return SharedArrayBuffer::New(isolate_, contents);
3274 : }
3275 0 : return MaybeLocal<SharedArrayBuffer>();
3276 : }
3277 :
3278 92 : MaybeLocal<WasmModuleObject> GetWasmModuleFromId(
3279 : Isolate* isolate, uint32_t transfer_id) override {
3280 : DCHECK_NOT_NULL(data_);
3281 276 : if (transfer_id < data_->transferrable_modules().size()) {
3282 : return WasmModuleObject::FromTransferrableModule(
3283 92 : isolate_, data_->transferrable_modules().at(transfer_id));
3284 : }
3285 0 : return MaybeLocal<WasmModuleObject>();
3286 : }
3287 :
3288 : private:
3289 : Isolate* isolate_;
3290 : ValueDeserializer deserializer_;
3291 : std::unique_ptr<SerializationData> data_;
3292 :
3293 : DISALLOW_COPY_AND_ASSIGN(Deserializer);
3294 : };
3295 :
3296 1808 : std::unique_ptr<SerializationData> Shell::SerializeValue(
3297 : Isolate* isolate, Local<Value> value, Local<Value> transfer) {
3298 : bool ok;
3299 1808 : Local<Context> context = isolate->GetCurrentContext();
3300 1810 : Serializer serializer(isolate);
3301 1809 : std::unique_ptr<SerializationData> data;
3302 3620 : if (serializer.WriteValue(context, value, transfer).To(&ok)) {
3303 : data = serializer.Release();
3304 : }
3305 : // Append externalized contents even when WriteValue fails.
3306 : base::MutexGuard lock_guard(workers_mutex_.Pointer());
3307 1816 : serializer.AppendExternalizedContentsTo(&externalized_contents_);
3308 1816 : return data;
3309 : }
3310 :
3311 1582 : MaybeLocal<Value> Shell::DeserializeValue(
3312 : Isolate* isolate, std::unique_ptr<SerializationData> data) {
3313 : Local<Value> value;
3314 1582 : Local<Context> context = isolate->GetCurrentContext();
3315 3164 : Deserializer deserializer(isolate, std::move(data));
3316 1582 : return deserializer.ReadValue(context);
3317 : }
3318 :
3319 :
3320 50887 : void Shell::CleanupWorkers() {
3321 : // Make a copy of workers_, because we don't want to call Worker::Terminate
3322 : // while holding the workers_mutex_ lock. Otherwise, if a worker is about to
3323 : // create a new Worker, it would deadlock.
3324 : std::vector<Worker*> workers_copy;
3325 : {
3326 : base::MutexGuard lock_guard(workers_mutex_.Pointer());
3327 50887 : allow_new_workers_ = false;
3328 : workers_copy.swap(workers_);
3329 : }
3330 :
3331 102379 : for (Worker* worker : workers_copy) {
3332 : worker->WaitForThread();
3333 605 : delete worker;
3334 : }
3335 :
3336 : // Now that all workers are terminated, we can re-enable Worker creation.
3337 : base::MutexGuard lock_guard(workers_mutex_.Pointer());
3338 50887 : allow_new_workers_ = true;
3339 : externalized_contents_.clear();
3340 50887 : }
3341 :
3342 29546 : int Shell::Main(int argc, char* argv[]) {
3343 29546 : std::ofstream trace_file;
3344 : v8::base::EnsureConsoleOutput();
3345 29546 : if (!SetOptions(argc, argv)) return 1;
3346 29546 : v8::V8::InitializeICUDefaultLocation(argv[0], options.icu_data_file);
3347 :
3348 : v8::platform::InProcessStackDumping in_process_stack_dumping =
3349 : options.disable_in_process_stack_traces
3350 : ? v8::platform::InProcessStackDumping::kDisabled
3351 29546 : : v8::platform::InProcessStackDumping::kEnabled;
3352 :
3353 : std::unique_ptr<platform::tracing::TracingController> tracing;
3354 29546 : if (options.trace_enabled && !i::FLAG_verify_predictable) {
3355 2 : tracing = base::make_unique<platform::tracing::TracingController>();
3356 :
3357 1 : trace_file.open(options.trace_path ? options.trace_path : "v8_trace.json");
3358 : platform::tracing::TraceBuffer* trace_buffer =
3359 : platform::tracing::TraceBuffer::CreateTraceBufferRingBuffer(
3360 : platform::tracing::TraceBuffer::kRingBufferChunks,
3361 1 : platform::tracing::TraceWriter::CreateJSONTraceWriter(trace_file));
3362 1 : tracing->Initialize(trace_buffer);
3363 : }
3364 :
3365 : platform::tracing::TracingController* tracing_controller = tracing.get();
3366 88638 : g_platform = v8::platform::NewDefaultPlatform(
3367 : options.thread_pool_size, v8::platform::IdleTaskSupport::kEnabled,
3368 : in_process_stack_dumping, std::move(tracing));
3369 29546 : g_default_platform = g_platform.get();
3370 : if (i::FLAG_verify_predictable) {
3371 : g_platform = MakePredictablePlatform(std::move(g_platform));
3372 : }
3373 29546 : if (options.stress_delay_tasks) {
3374 0 : int64_t random_seed = i::FLAG_fuzzer_random_seed;
3375 0 : if (!random_seed) random_seed = i::FLAG_random_seed;
3376 : // If random_seed is still 0 here, the {DelayedTasksPlatform} will choose a
3377 : // random seed.
3378 0 : g_platform = MakeDelayedTasksPlatform(std::move(g_platform), random_seed);
3379 : }
3380 :
3381 29546 : if (i::FLAG_trace_turbo_cfg_file == nullptr) {
3382 29545 : SetFlagsFromString("--trace-turbo-cfg-file=turbo.cfg");
3383 : }
3384 29546 : if (i::FLAG_redirect_code_traces_to == nullptr) {
3385 29546 : SetFlagsFromString("--redirect-code-traces-to=code.asm");
3386 : }
3387 29546 : v8::V8::InitializePlatform(g_platform.get());
3388 29546 : v8::V8::Initialize();
3389 29546 : if (options.natives_blob || options.snapshot_blob) {
3390 : v8::V8::InitializeExternalStartupData(options.natives_blob,
3391 0 : options.snapshot_blob);
3392 : } else {
3393 29546 : v8::V8::InitializeExternalStartupData(argv[0]);
3394 : }
3395 : int result = 0;
3396 : Isolate::CreateParams create_params;
3397 : ShellArrayBufferAllocator shell_array_buffer_allocator;
3398 : MockArrayBufferAllocator mock_arraybuffer_allocator;
3399 : const size_t memory_limit =
3400 29546 : options.mock_arraybuffer_allocator_limit * options.num_isolates;
3401 : MockArrayBufferAllocatiorWithLimit mock_arraybuffer_allocator_with_limit(
3402 : memory_limit >= options.mock_arraybuffer_allocator_limit
3403 : ? memory_limit
3404 29546 : : std::numeric_limits<size_t>::max());
3405 29546 : if (options.mock_arraybuffer_allocator) {
3406 21 : if (memory_limit) {
3407 0 : Shell::array_buffer_allocator = &mock_arraybuffer_allocator_with_limit;
3408 : } else {
3409 21 : Shell::array_buffer_allocator = &mock_arraybuffer_allocator;
3410 : }
3411 : } else {
3412 29525 : Shell::array_buffer_allocator = &shell_array_buffer_allocator;
3413 : }
3414 29546 : create_params.array_buffer_allocator = Shell::array_buffer_allocator;
3415 : #ifdef ENABLE_VTUNE_JIT_INTERFACE
3416 : create_params.code_event_handler = vTune::GetVtuneCodeEventHandler();
3417 : #endif
3418 : create_params.constraints.ConfigureDefaults(
3419 29546 : base::SysInfo::AmountOfPhysicalMemory(),
3420 59092 : base::SysInfo::AmountOfVirtualMemory());
3421 :
3422 59092 : Shell::counter_map_ = new CounterMap();
3423 29546 : if (i::FLAG_dump_counters || i::FLAG_dump_counters_nvp || i::FLAG_gc_stats) {
3424 0 : create_params.counter_lookup_callback = LookupCounter;
3425 0 : create_params.create_histogram_callback = CreateHistogram;
3426 0 : create_params.add_histogram_sample_callback = AddHistogramSample;
3427 : }
3428 :
3429 29546 : if (V8_TRAP_HANDLER_SUPPORTED && i::FLAG_wasm_trap_handler) {
3430 : constexpr bool use_default_trap_handler = true;
3431 29538 : if (!v8::V8::EnableWebAssemblyTrapHandler(use_default_trap_handler)) {
3432 0 : FATAL("Could not register trap handler");
3433 : }
3434 : }
3435 :
3436 29546 : Isolate* isolate = Isolate::New(create_params);
3437 : isolate->SetHostImportModuleDynamicallyCallback(
3438 29546 : Shell::HostImportModuleDynamically);
3439 : isolate->SetHostInitializeImportMetaObjectCallback(
3440 29546 : Shell::HostInitializeImportMetaObject);
3441 :
3442 29546 : D8Console console(isolate);
3443 : {
3444 : Isolate::Scope scope(isolate);
3445 29546 : Initialize(isolate);
3446 59092 : PerIsolateData data(isolate);
3447 29546 : debug::SetConsoleDelegate(isolate, &console);
3448 :
3449 29546 : if (options.trace_enabled) {
3450 : platform::tracing::TraceConfig* trace_config;
3451 1 : if (options.trace_config) {
3452 0 : int size = 0;
3453 0 : char* trace_config_json_str = ReadChars(options.trace_config, &size);
3454 : trace_config =
3455 0 : tracing::CreateTraceConfigFromJSON(isolate, trace_config_json_str);
3456 0 : delete[] trace_config_json_str;
3457 : } else {
3458 : trace_config =
3459 1 : platform::tracing::TraceConfig::CreateDefaultTraceConfig();
3460 : }
3461 1 : tracing_controller->StartTracing(trace_config);
3462 : }
3463 :
3464 29546 : if (options.stress_opt || options.stress_deopt) {
3465 : Testing::SetStressRunType(options.stress_opt
3466 : ? Testing::kStressTypeOpt
3467 5538 : : Testing::kStressTypeDeopt);
3468 5538 : options.stress_runs = Testing::GetStressRuns();
3469 37830 : for (int i = 0; i < options.stress_runs && result == 0; i++) {
3470 : printf("============ Stress %d/%d ============\n", i + 1,
3471 26754 : options.stress_runs);
3472 26754 : Testing::PrepareStressRun(i);
3473 26754 : bool last_run = i == options.stress_runs - 1;
3474 26754 : result = RunMain(isolate, argc, argv, last_run);
3475 : }
3476 : printf("======== Full Deoptimization =======\n");
3477 5538 : Testing::DeoptimizeAll(isolate);
3478 24008 : } else if (i::FLAG_stress_runs > 0) {
3479 24 : options.stress_runs = i::FLAG_stress_runs;
3480 152 : for (int i = 0; i < options.stress_runs && result == 0; i++) {
3481 : printf("============ Run %d/%d ============\n", i + 1,
3482 104 : options.stress_runs);
3483 104 : bool last_run = i == options.stress_runs - 1;
3484 104 : result = RunMain(isolate, argc, argv, last_run);
3485 : }
3486 23984 : } else if (options.code_cache_options !=
3487 : ShellOptions::CodeCacheOptions::kNoProduceCache) {
3488 : printf("============ Run: Produce code cache ============\n");
3489 : // First run to produce the cache
3490 : Isolate::CreateParams create_params;
3491 45 : create_params.array_buffer_allocator = Shell::array_buffer_allocator;
3492 45 : i::FLAG_hash_seed ^= 1337; // Use a different hash seed.
3493 45 : Isolate* isolate2 = Isolate::New(create_params);
3494 45 : i::FLAG_hash_seed ^= 1337; // Restore old hash seed.
3495 : isolate2->SetHostImportModuleDynamicallyCallback(
3496 45 : Shell::HostImportModuleDynamically);
3497 : isolate2->SetHostInitializeImportMetaObjectCallback(
3498 45 : Shell::HostInitializeImportMetaObject);
3499 : {
3500 45 : D8Console console(isolate2);
3501 45 : Initialize(isolate2);
3502 45 : debug::SetConsoleDelegate(isolate2, &console);
3503 90 : PerIsolateData data(isolate2);
3504 : Isolate::Scope isolate_scope(isolate2);
3505 :
3506 45 : result = RunMain(isolate2, argc, argv, false);
3507 : }
3508 45 : isolate2->Dispose();
3509 :
3510 : // Change the options to consume cache
3511 : DCHECK(options.compile_options == v8::ScriptCompiler::kEagerCompile ||
3512 : options.compile_options == v8::ScriptCompiler::kNoCompileOptions);
3513 45 : options.compile_options = v8::ScriptCompiler::kConsumeCodeCache;
3514 : options.code_cache_options =
3515 45 : ShellOptions::CodeCacheOptions::kNoProduceCache;
3516 :
3517 : printf("============ Run: Consume code cache ============\n");
3518 : // Second run to consume the cache in current isolate
3519 45 : result = RunMain(isolate, argc, argv, true);
3520 45 : options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
3521 : } else {
3522 : bool last_run = true;
3523 23939 : result = RunMain(isolate, argc, argv, last_run);
3524 : }
3525 :
3526 : // Run interactive shell if explicitly requested or if no script has been
3527 : // executed, but never on --test
3528 29546 : if (use_interactive_shell()) {
3529 0 : RunShell(isolate);
3530 : }
3531 :
3532 29551 : if (i::FLAG_trace_ignition_dispatches &&
3533 5 : i::FLAG_trace_ignition_dispatches_output_file != nullptr) {
3534 0 : WriteIgnitionDispatchCountersFile(isolate);
3535 : }
3536 :
3537 : // Shut down contexts and collect garbage.
3538 : cached_code_map_.clear();
3539 : evaluation_context_.Reset();
3540 : stringify_function_.Reset();
3541 29546 : CollectGarbage(isolate);
3542 : }
3543 29546 : OnExit(isolate);
3544 29546 : V8::Dispose();
3545 29546 : V8::ShutdownPlatform();
3546 :
3547 : // Delete the platform explicitly here to write the tracing output to the
3548 : // tracing file.
3549 : g_platform.reset();
3550 29546 : return result;
3551 : }
3552 :
3553 : } // namespace v8
3554 :
3555 :
3556 : #ifndef GOOGLE3
3557 29546 : int main(int argc, char* argv[]) {
3558 29546 : return v8::Shell::Main(argc, argv);
3559 88638 : }
3560 : #endif
3561 :
3562 : #undef CHECK
3563 : #undef DCHECK
|