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 "src/d8-console.h"
21 : #include "src/d8.h"
22 : #include "src/ostreams.h"
23 :
24 : #include "include/libplatform/libplatform.h"
25 : #include "include/libplatform/v8-tracing.h"
26 : #include "include/v8-inspector.h"
27 : #include "src/api.h"
28 : #include "src/base/cpu.h"
29 : #include "src/base/logging.h"
30 : #include "src/base/platform/platform.h"
31 : #include "src/base/platform/time.h"
32 : #include "src/base/sys-info.h"
33 : #include "src/basic-block-profiler.h"
34 : #include "src/debug/debug-interface.h"
35 : #include "src/interpreter/interpreter.h"
36 : #include "src/list-inl.h"
37 : #include "src/msan.h"
38 : #include "src/objects-inl.h"
39 : #include "src/objects.h"
40 : #include "src/snapshot/natives.h"
41 : #include "src/trap-handler/trap-handler.h"
42 : #include "src/utils.h"
43 : #include "src/v8.h"
44 :
45 : #if !defined(_WIN32) && !defined(_WIN64)
46 : #include <unistd.h> // NOLINT
47 : #else
48 : #include <windows.h> // NOLINT
49 : #if defined(_MSC_VER)
50 : #include <crtdbg.h> // NOLINT
51 : #endif // defined(_MSC_VER)
52 : #endif // !defined(_WIN32) && !defined(_WIN64)
53 :
54 : #ifndef DCHECK
55 : #define DCHECK(condition) assert(condition)
56 : #endif
57 :
58 : #ifndef CHECK
59 : #define CHECK(condition) assert(condition)
60 : #endif
61 :
62 : namespace v8 {
63 :
64 : namespace {
65 :
66 : const int MB = 1024 * 1024;
67 : const int kMaxWorkers = 50;
68 : const int kMaxSerializerMemoryUsage = 1 * MB; // Arbitrary maximum for testing.
69 :
70 : #define USE_VM 1
71 : #define VM_THRESHOLD 65536
72 : // TODO(titzer): allocations should fail if >= 2gb because of
73 : // array buffers storing the lengths as a SMI internally.
74 : #define TWO_GB (2u * 1024u * 1024u * 1024u)
75 :
76 28618 : class ShellArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
77 : public:
78 111847 : virtual void* Allocate(size_t length) {
79 : #if USE_VM
80 111847 : if (RoundToPageSize(&length)) {
81 8248 : void* data = VirtualMemoryAllocate(length);
82 : #if DEBUG
83 : if (data) {
84 : // In debug mode, check the memory is zero-initialized.
85 : size_t limit = length / sizeof(uint64_t);
86 : uint64_t* ptr = reinterpret_cast<uint64_t*>(data);
87 : for (size_t i = 0; i < limit; i++) {
88 : DCHECK_EQ(0u, ptr[i]);
89 : }
90 : }
91 : #endif
92 8248 : return data;
93 : }
94 : #endif
95 103599 : void* data = AllocateUninitialized(length);
96 207198 : return data == NULL ? data : memset(data, 0, length);
97 : }
98 120557 : virtual void* AllocateUninitialized(size_t length) {
99 : #if USE_VM
100 120557 : if (RoundToPageSize(&length)) return VirtualMemoryAllocate(length);
101 : #endif
102 : // Work around for GCC bug on AIX
103 : // See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79839
104 : #if V8_OS_AIX && _LINUX_SOURCE_COMPAT
105 : return __linux_malloc(length);
106 : #else
107 120557 : return malloc(length);
108 : #endif
109 : }
110 128945 : virtual void Free(void* data, size_t length) {
111 : #if USE_VM
112 128945 : if (RoundToPageSize(&length)) {
113 8248 : base::VirtualMemory::ReleaseRegion(data, length);
114 137197 : return;
115 : }
116 : #endif
117 120701 : free(data);
118 : }
119 : // If {length} is at least {VM_THRESHOLD}, round up to next page size
120 : // and return {true}. Otherwise return {false}.
121 361350 : bool RoundToPageSize(size_t* length) {
122 361350 : const size_t kPageSize = base::OS::CommitPageSize();
123 361349 : if (*length >= VM_THRESHOLD && *length < TWO_GB) {
124 16496 : *length = ((*length + kPageSize - 1) / kPageSize) * kPageSize;
125 : return true;
126 : }
127 : return false;
128 : }
129 : #if USE_VM
130 8248 : void* VirtualMemoryAllocate(size_t length) {
131 8248 : void* data = base::VirtualMemory::ReserveRegion(length);
132 8248 : if (data && !base::VirtualMemory::CommitRegion(data, length, false)) {
133 0 : base::VirtualMemory::ReleaseRegion(data, length);
134 : return nullptr;
135 : }
136 : MSAN_MEMORY_IS_INITIALIZED(data, length);
137 : return data;
138 : }
139 : #endif
140 : };
141 :
142 :
143 28618 : class MockArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
144 : public:
145 22 : void* Allocate(size_t length) override {
146 22 : size_t actual_length = length > 10 * MB ? 1 : length;
147 22 : void* data = AllocateUninitialized(actual_length);
148 44 : return data == NULL ? data : memset(data, 0, actual_length);
149 : }
150 22 : void* AllocateUninitialized(size_t length) override {
151 22 : return length > 10 * MB ? malloc(1) : malloc(length);
152 : }
153 22 : void Free(void* p, size_t) override { free(p); }
154 : };
155 :
156 :
157 : // Predictable v8::Platform implementation. All background and foreground
158 : // tasks are run immediately, delayed tasks are not executed at all.
159 : class PredictablePlatform : public Platform {
160 : public:
161 : PredictablePlatform() {}
162 :
163 : void CallOnBackgroundThread(Task* task,
164 : ExpectedRuntime expected_runtime) override {
165 : task->Run();
166 : delete task;
167 : }
168 :
169 : void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override {
170 : task->Run();
171 : delete task;
172 : }
173 :
174 : void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task,
175 : double delay_in_seconds) override {
176 : delete task;
177 : }
178 :
179 : void CallIdleOnForegroundThread(v8::Isolate* isolate,
180 : IdleTask* task) override {
181 : UNREACHABLE();
182 : }
183 :
184 : bool IdleTasksEnabled(v8::Isolate* isolate) override { return false; }
185 :
186 : double MonotonicallyIncreasingTime() override {
187 : return synthetic_time_in_sec_ += 0.00001;
188 : }
189 :
190 : using Platform::AddTraceEvent;
191 : uint64_t AddTraceEvent(char phase, const uint8_t* categoryEnabledFlag,
192 : const char* name, const char* scope, uint64_t id,
193 : uint64_t bind_id, int numArgs, const char** argNames,
194 : const uint8_t* argTypes, const uint64_t* argValues,
195 : unsigned int flags) override {
196 : return 0;
197 : }
198 :
199 : void UpdateTraceEventDuration(const uint8_t* categoryEnabledFlag,
200 : const char* name, uint64_t handle) override {}
201 :
202 : const uint8_t* GetCategoryGroupEnabled(const char* name) override {
203 : static uint8_t no = 0;
204 : return &no;
205 : }
206 :
207 : const char* GetCategoryGroupName(
208 : const uint8_t* categoryEnabledFlag) override {
209 : static const char* dummy = "dummy";
210 : return dummy;
211 : }
212 :
213 : private:
214 : double synthetic_time_in_sec_ = 0.0;
215 :
216 : DISALLOW_COPY_AND_ASSIGN(PredictablePlatform);
217 : };
218 :
219 :
220 : v8::Platform* g_platform = NULL;
221 :
222 10412 : static Local<Value> Throw(Isolate* isolate, const char* message) {
223 : return isolate->ThrowException(
224 : String::NewFromUtf8(isolate, message, NewStringType::kNormal)
225 20824 : .ToLocalChecked());
226 : }
227 :
228 1476 : Worker* GetWorkerFromInternalField(Isolate* isolate, Local<Object> object) {
229 1476 : if (object->InternalFieldCount() != 1) {
230 0 : Throw(isolate, "this is not a Worker");
231 0 : return NULL;
232 : }
233 :
234 : Worker* worker =
235 : static_cast<Worker*>(object->GetAlignedPointerFromInternalField(0));
236 1476 : if (worker == NULL) {
237 14 : Throw(isolate, "Worker is defunct because main thread is terminating");
238 14 : return NULL;
239 : }
240 :
241 : return worker;
242 : }
243 :
244 :
245 : } // namespace
246 :
247 : namespace tracing {
248 :
249 : namespace {
250 :
251 : // String options that can be used to initialize TraceOptions.
252 : const char kRecordUntilFull[] = "record-until-full";
253 : const char kRecordContinuously[] = "record-continuously";
254 : const char kRecordAsMuchAsPossible[] = "record-as-much-as-possible";
255 :
256 : const char kRecordModeParam[] = "record_mode";
257 : const char kEnableSystraceParam[] = "enable_systrace";
258 : const char kEnableArgumentFilterParam[] = "enable_argument_filter";
259 : const char kIncludedCategoriesParam[] = "included_categories";
260 :
261 : class TraceConfigParser {
262 : public:
263 0 : static void FillTraceConfig(v8::Isolate* isolate,
264 : platform::tracing::TraceConfig* trace_config,
265 : const char* json_str) {
266 0 : HandleScope outer_scope(isolate);
267 0 : Local<Context> context = Context::New(isolate);
268 : Context::Scope context_scope(context);
269 0 : HandleScope inner_scope(isolate);
270 :
271 : Local<String> source =
272 : String::NewFromUtf8(isolate, json_str, NewStringType::kNormal)
273 0 : .ToLocalChecked();
274 0 : Local<Value> result = JSON::Parse(context, source).ToLocalChecked();
275 0 : Local<v8::Object> trace_config_object = Local<v8::Object>::Cast(result);
276 :
277 : trace_config->SetTraceRecordMode(
278 0 : GetTraceRecordMode(isolate, context, trace_config_object));
279 0 : if (GetBoolean(isolate, context, trace_config_object,
280 : kEnableSystraceParam)) {
281 : trace_config->EnableSystrace();
282 : }
283 0 : if (GetBoolean(isolate, context, trace_config_object,
284 : kEnableArgumentFilterParam)) {
285 : trace_config->EnableArgumentFilter();
286 : }
287 : UpdateIncludedCategoriesList(isolate, context, trace_config_object,
288 0 : trace_config);
289 0 : }
290 :
291 : private:
292 0 : static bool GetBoolean(v8::Isolate* isolate, Local<Context> context,
293 : Local<v8::Object> object, const char* property) {
294 0 : Local<Value> value = GetValue(isolate, context, object, property);
295 0 : if (value->IsNumber()) {
296 0 : Local<Boolean> v8_boolean = value->ToBoolean(context).ToLocalChecked();
297 0 : return v8_boolean->Value();
298 : }
299 : return false;
300 : }
301 :
302 0 : static int UpdateIncludedCategoriesList(
303 : v8::Isolate* isolate, Local<Context> context, Local<v8::Object> object,
304 : platform::tracing::TraceConfig* trace_config) {
305 : Local<Value> value =
306 0 : GetValue(isolate, context, object, kIncludedCategoriesParam);
307 0 : if (value->IsArray()) {
308 : Local<Array> v8_array = Local<Array>::Cast(value);
309 0 : for (int i = 0, length = v8_array->Length(); i < length; ++i) {
310 0 : Local<Value> v = v8_array->Get(context, i)
311 0 : .ToLocalChecked()
312 : ->ToString(context)
313 0 : .ToLocalChecked();
314 0 : String::Utf8Value str(v->ToString(context).ToLocalChecked());
315 0 : trace_config->AddIncludedCategory(*str);
316 0 : }
317 0 : return v8_array->Length();
318 : }
319 : return 0;
320 : }
321 :
322 0 : static platform::tracing::TraceRecordMode GetTraceRecordMode(
323 : v8::Isolate* isolate, Local<Context> context, Local<v8::Object> object) {
324 0 : Local<Value> value = GetValue(isolate, context, object, kRecordModeParam);
325 0 : if (value->IsString()) {
326 0 : Local<String> v8_string = value->ToString(context).ToLocalChecked();
327 0 : String::Utf8Value str(v8_string);
328 0 : if (strcmp(kRecordUntilFull, *str) == 0) {
329 0 : return platform::tracing::TraceRecordMode::RECORD_UNTIL_FULL;
330 0 : } else if (strcmp(kRecordContinuously, *str) == 0) {
331 : return platform::tracing::TraceRecordMode::RECORD_CONTINUOUSLY;
332 0 : } else if (strcmp(kRecordAsMuchAsPossible, *str) == 0) {
333 : return platform::tracing::TraceRecordMode::RECORD_AS_MUCH_AS_POSSIBLE;
334 0 : }
335 : }
336 : return platform::tracing::TraceRecordMode::RECORD_UNTIL_FULL;
337 : }
338 :
339 0 : static Local<Value> GetValue(v8::Isolate* isolate, Local<Context> context,
340 : Local<v8::Object> object, const char* property) {
341 : Local<String> v8_str =
342 : String::NewFromUtf8(isolate, property, NewStringType::kNormal)
343 0 : .ToLocalChecked();
344 0 : return object->Get(context, v8_str).ToLocalChecked();
345 : }
346 : };
347 :
348 : } // namespace
349 :
350 0 : static platform::tracing::TraceConfig* CreateTraceConfigFromJSON(
351 : v8::Isolate* isolate, const char* json_str) {
352 : platform::tracing::TraceConfig* trace_config =
353 0 : new platform::tracing::TraceConfig();
354 0 : TraceConfigParser::FillTraceConfig(isolate, trace_config, json_str);
355 0 : return trace_config;
356 : }
357 :
358 : } // namespace tracing
359 :
360 : class PerIsolateData {
361 : public:
362 29145 : explicit PerIsolateData(Isolate* isolate) : isolate_(isolate), realms_(NULL) {
363 29145 : HandleScope scope(isolate);
364 29145 : isolate->SetData(0, this);
365 29145 : }
366 :
367 : ~PerIsolateData() {
368 29145 : isolate_->SetData(0, NULL); // Not really needed, just to be sure...
369 : }
370 :
371 : inline static PerIsolateData* Get(Isolate* isolate) {
372 : return reinterpret_cast<PerIsolateData*>(isolate->GetData(0));
373 : }
374 :
375 : class RealmScope {
376 : public:
377 : explicit RealmScope(PerIsolateData* data);
378 : ~RealmScope();
379 : private:
380 : PerIsolateData* data_;
381 : };
382 :
383 : private:
384 : friend class Shell;
385 : friend class RealmScope;
386 : Isolate* isolate_;
387 : int realm_count_;
388 : int realm_current_;
389 : int realm_switch_;
390 : Global<Context>* realms_;
391 : Global<Value> realm_shared_;
392 :
393 : int RealmIndexOrThrow(const v8::FunctionCallbackInfo<v8::Value>& args,
394 : int arg_offset);
395 : int RealmFind(Local<Context> context);
396 : };
397 :
398 0 : class ExternalOwningOneByteStringResource
399 : : public String::ExternalOneByteStringResource {
400 : public:
401 : ExternalOwningOneByteStringResource() : length_(0) {}
402 : ExternalOwningOneByteStringResource(std::unique_ptr<const char[]> data,
403 : size_t length)
404 0 : : data_(std::move(data)), length_(length) {}
405 0 : const char* data() const override { return data_.get(); }
406 0 : size_t length() const override { return length_; }
407 :
408 : private:
409 : std::unique_ptr<const char[]> data_;
410 : size_t length_;
411 : };
412 :
413 : CounterMap* Shell::counter_map_;
414 : base::OS::MemoryMappedFile* Shell::counters_file_ = NULL;
415 : CounterCollection Shell::local_counters_;
416 : CounterCollection* Shell::counters_ = &local_counters_;
417 : base::LazyMutex Shell::context_mutex_;
418 28618 : const base::TimeTicks Shell::kInitialTicks =
419 : base::TimeTicks::HighResolutionNow();
420 28618 : Global<Function> Shell::stringify_function_;
421 : base::LazyMutex Shell::workers_mutex_;
422 : bool Shell::allow_new_workers_ = true;
423 28618 : i::List<Worker*> Shell::workers_;
424 28618 : std::vector<ExternalizedContents> Shell::externalized_contents_;
425 :
426 28618 : Global<Context> Shell::evaluation_context_;
427 : ArrayBuffer::Allocator* Shell::array_buffer_allocator;
428 28618 : ShellOptions Shell::options;
429 : base::OnceType Shell::quit_once_ = V8_ONCE_INIT;
430 :
431 0 : bool CounterMap::Match(void* key1, void* key2) {
432 : const char* name1 = reinterpret_cast<const char*>(key1);
433 : const char* name2 = reinterpret_cast<const char*>(key2);
434 0 : return strcmp(name1, name2) == 0;
435 : }
436 :
437 :
438 312 : ScriptCompiler::CachedData* CompileForCachedData(
439 : Local<String> source, Local<Value> name,
440 : ScriptCompiler::CompileOptions compile_options) {
441 312 : int source_length = source->Length();
442 312 : uint16_t* source_buffer = new uint16_t[source_length];
443 312 : source->Write(source_buffer, 0, source_length);
444 : int name_length = 0;
445 : uint16_t* name_buffer = NULL;
446 312 : if (name->IsString()) {
447 : Local<String> name_string = Local<String>::Cast(name);
448 312 : name_length = name_string->Length();
449 312 : name_buffer = new uint16_t[name_length];
450 312 : name_string->Write(name_buffer, 0, name_length);
451 : }
452 : Isolate::CreateParams create_params;
453 312 : create_params.array_buffer_allocator = Shell::array_buffer_allocator;
454 : create_params.host_import_module_dynamically_callback_ =
455 312 : Shell::HostImportModuleDynamically;
456 312 : Isolate* temp_isolate = Isolate::New(create_params);
457 : ScriptCompiler::CachedData* result = NULL;
458 : {
459 : Isolate::Scope isolate_scope(temp_isolate);
460 624 : HandleScope handle_scope(temp_isolate);
461 624 : Context::Scope context_scope(Context::New(temp_isolate));
462 : Local<String> source_copy =
463 : v8::String::NewFromTwoByte(temp_isolate, source_buffer,
464 : v8::NewStringType::kNormal, source_length)
465 312 : .ToLocalChecked();
466 : Local<Value> name_copy;
467 312 : if (name_buffer) {
468 : name_copy =
469 : v8::String::NewFromTwoByte(temp_isolate, name_buffer,
470 : v8::NewStringType::kNormal, name_length)
471 312 : .ToLocalChecked();
472 : } else {
473 : name_copy = v8::Undefined(temp_isolate);
474 : }
475 : ScriptCompiler::Source script_source(source_copy, ScriptOrigin(name_copy));
476 312 : if (!ScriptCompiler::CompileUnboundScript(temp_isolate, &script_source,
477 : compile_options)
478 936 : .IsEmpty() &&
479 312 : script_source.GetCachedData()) {
480 297 : int length = script_source.GetCachedData()->length;
481 297 : uint8_t* cache = new uint8_t[length];
482 297 : memcpy(cache, script_source.GetCachedData()->data, length);
483 : result = new ScriptCompiler::CachedData(
484 297 : cache, length, ScriptCompiler::CachedData::BufferOwned);
485 : }
486 : }
487 312 : temp_isolate->Dispose();
488 312 : delete[] source_buffer;
489 312 : delete[] name_buffer;
490 312 : return result;
491 : }
492 :
493 :
494 : // Compile a string within the current v8 context.
495 146467 : MaybeLocal<Script> Shell::CompileString(
496 : Isolate* isolate, Local<String> source, Local<Value> name,
497 : ScriptCompiler::CompileOptions compile_options) {
498 146467 : Local<Context> context(isolate->GetCurrentContext());
499 : ScriptOrigin origin(name);
500 146467 : if (compile_options == ScriptCompiler::kNoCompileOptions) {
501 : ScriptCompiler::Source script_source(source, origin);
502 146155 : return ScriptCompiler::Compile(context, &script_source, compile_options);
503 : }
504 :
505 : ScriptCompiler::CachedData* data =
506 312 : CompileForCachedData(source, name, compile_options);
507 : ScriptCompiler::Source cached_source(source, origin, data);
508 312 : if (compile_options == ScriptCompiler::kProduceCodeCache) {
509 : compile_options = ScriptCompiler::kConsumeCodeCache;
510 0 : } else if (compile_options == ScriptCompiler::kProduceParserCache) {
511 : compile_options = ScriptCompiler::kConsumeParserCache;
512 : } else {
513 : DCHECK(false); // A new compile option?
514 : }
515 312 : if (data == NULL) compile_options = ScriptCompiler::kNoCompileOptions;
516 : MaybeLocal<Script> result =
517 312 : ScriptCompiler::Compile(context, &cached_source, compile_options);
518 312 : CHECK(data == NULL || !data->rejected);
519 312 : return result;
520 : }
521 :
522 :
523 : // Executes a string within the current v8 context.
524 146467 : bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
525 : Local<Value> name, bool print_result,
526 : bool report_exceptions) {
527 146467 : HandleScope handle_scope(isolate);
528 292934 : TryCatch try_catch(isolate);
529 146467 : try_catch.SetVerbose(true);
530 :
531 : MaybeLocal<Value> maybe_result;
532 : {
533 : PerIsolateData* data = PerIsolateData::Get(isolate);
534 : Local<Context> realm =
535 146467 : Local<Context>::New(isolate, data->realms_[data->realm_current_]);
536 : Context::Scope context_scope(realm);
537 : Local<Script> script;
538 292934 : if (!Shell::CompileString(isolate, source, name, options.compile_options)
539 292934 : .ToLocal(&script)) {
540 : // Print errors that happened during compilation.
541 1341 : if (report_exceptions) ReportException(isolate, &try_catch);
542 : return false;
543 : }
544 145126 : maybe_result = script->Run(realm);
545 145126 : EmptyMessageQueues(isolate);
546 145126 : data->realm_current_ = data->realm_switch_;
547 : }
548 : Local<Value> result;
549 145126 : if (!maybe_result.ToLocal(&result)) {
550 : DCHECK(try_catch.HasCaught());
551 : // Print errors that happened during execution.
552 10127 : if (report_exceptions) ReportException(isolate, &try_catch);
553 : return false;
554 : }
555 : DCHECK(!try_catch.HasCaught());
556 134999 : if (print_result) {
557 0 : if (options.test_shell) {
558 0 : if (!result->IsUndefined()) {
559 : // If all went well and the result wasn't undefined then print
560 : // the returned value.
561 0 : v8::String::Utf8Value str(result);
562 0 : fwrite(*str, sizeof(**str), str.length(), stdout);
563 0 : printf("\n");
564 : }
565 : } else {
566 0 : v8::String::Utf8Value str(Stringify(isolate, result));
567 0 : fwrite(*str, sizeof(**str), str.length(), stdout);
568 0 : printf("\n");
569 : }
570 : }
571 146467 : return true;
572 : }
573 :
574 : namespace {
575 :
576 2788 : std::string ToSTLString(Local<String> v8_str) {
577 2788 : String::Utf8Value utf8(v8_str);
578 : // Should not be able to fail since the input is a String.
579 2788 : CHECK(*utf8);
580 2788 : return *utf8;
581 : }
582 :
583 : bool IsAbsolutePath(const std::string& path) {
584 : #if defined(_WIN32) || defined(_WIN64)
585 : // TODO(adamk): This is an incorrect approximation, but should
586 : // work for all our test-running cases.
587 : return path.find(':') != std::string::npos;
588 : #else
589 3416 : return path[0] == '/';
590 : #endif
591 : }
592 :
593 628 : std::string GetWorkingDirectory() {
594 : #if defined(_WIN32) || defined(_WIN64)
595 : char system_buffer[MAX_PATH];
596 : // TODO(adamk): Support Unicode paths.
597 : DWORD len = GetCurrentDirectoryA(MAX_PATH, system_buffer);
598 : CHECK(len > 0);
599 : return system_buffer;
600 : #else
601 : char curdir[PATH_MAX];
602 628 : CHECK_NOT_NULL(getcwd(curdir, PATH_MAX));
603 628 : return curdir;
604 : #endif
605 : }
606 :
607 : // Returns the directory part of path, without the trailing '/'.
608 1889 : std::string DirName(const std::string& path) {
609 : DCHECK(IsAbsolutePath(path));
610 : size_t last_slash = path.find_last_of('/');
611 : DCHECK(last_slash != std::string::npos);
612 1889 : return path.substr(0, last_slash);
613 : }
614 :
615 : // Resolves path to an absolute path if necessary, and does some
616 : // normalization (eliding references to the current directory
617 : // and replacing backslashes with slashes).
618 3094 : std::string NormalizePath(const std::string& path,
619 : const std::string& dir_name) {
620 : std::string result;
621 3094 : if (IsAbsolutePath(path)) {
622 : result = path;
623 : } else {
624 4932 : result = dir_name + '/' + path;
625 : }
626 3094 : std::replace(result.begin(), result.end(), '\\', '/');
627 : size_t i;
628 3180 : while ((i = result.find("/./")) != std::string::npos) {
629 86 : result.erase(i, 2);
630 : }
631 3094 : return result;
632 : }
633 :
634 : // Per-context Module data, allowing sharing of module maps
635 : // across top-level module loads.
636 124922 : class ModuleEmbedderData {
637 : private:
638 : class ModuleGlobalHash {
639 : public:
640 62461 : explicit ModuleGlobalHash(Isolate* isolate) : isolate_(isolate) {}
641 2639 : size_t operator()(const Global<Module>& module) const {
642 5278 : return module.Get(isolate_)->GetIdentityHash();
643 : }
644 :
645 : private:
646 : Isolate* isolate_;
647 : };
648 :
649 : public:
650 62461 : explicit ModuleEmbedderData(Isolate* isolate)
651 124922 : : module_to_directory_map(10, ModuleGlobalHash(isolate)) {}
652 :
653 : // Map from normalized module specifier to Module.
654 : std::unordered_map<std::string, Global<Module>> specifier_to_module_map;
655 : // Map from Module to the directory that Module was loaded from.
656 : std::unordered_map<Global<Module>, std::string, ModuleGlobalHash>
657 : module_to_directory_map;
658 : };
659 :
660 : enum {
661 : kModuleEmbedderDataIndex,
662 : kInspectorClientIndex
663 : };
664 :
665 62461 : void InitializeModuleEmbedderData(Local<Context> context) {
666 : context->SetAlignedPointerInEmbedderData(
667 62461 : kModuleEmbedderDataIndex, new ModuleEmbedderData(context->GetIsolate()));
668 62461 : }
669 :
670 : ModuleEmbedderData* GetModuleDataFromContext(Local<Context> context) {
671 : return static_cast<ModuleEmbedderData*>(
672 : context->GetAlignedPointerFromEmbedderData(kModuleEmbedderDataIndex));
673 : }
674 :
675 62461 : void DisposeModuleEmbedderData(Local<Context> context) {
676 62461 : delete GetModuleDataFromContext(context);
677 62461 : context->SetAlignedPointerInEmbedderData(kModuleEmbedderDataIndex, nullptr);
678 62461 : }
679 :
680 1072 : MaybeLocal<Module> ResolveModuleCallback(Local<Context> context,
681 : Local<String> specifier,
682 : Local<Module> referrer) {
683 1072 : Isolate* isolate = context->GetIsolate();
684 : ModuleEmbedderData* d = GetModuleDataFromContext(context);
685 : auto dir_name_it =
686 1072 : d->module_to_directory_map.find(Global<Module>(isolate, referrer));
687 1072 : CHECK(dir_name_it != d->module_to_directory_map.end());
688 : std::string absolute_path =
689 2144 : NormalizePath(ToSTLString(specifier), dir_name_it->second);
690 : auto module_it = d->specifier_to_module_map.find(absolute_path);
691 1072 : CHECK(module_it != d->specifier_to_module_map.end());
692 2144 : return module_it->second.Get(isolate);
693 : }
694 :
695 : } // anonymous namespace
696 :
697 1595 : MaybeLocal<Module> Shell::FetchModuleTree(Local<Context> context,
698 : const std::string& file_name) {
699 : DCHECK(IsAbsolutePath(file_name));
700 1595 : Isolate* isolate = context->GetIsolate();
701 1595 : Local<String> source_text = ReadFile(isolate, file_name.c_str());
702 1595 : if (source_text.IsEmpty()) {
703 14 : std::string msg = "Error reading: " + file_name;
704 14 : Throw(isolate, msg.c_str());
705 14 : return MaybeLocal<Module>();
706 : }
707 : ScriptOrigin origin(
708 : String::NewFromUtf8(isolate, file_name.c_str(), NewStringType::kNormal)
709 : .ToLocalChecked(),
710 : Local<Integer>(), Local<Integer>(), Local<Boolean>(), Local<Integer>(),
711 1581 : Local<Value>(), Local<Boolean>(), Local<Boolean>(), True(isolate));
712 : ScriptCompiler::Source source(source_text, origin);
713 : Local<Module> module;
714 3162 : if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)) {
715 14 : return MaybeLocal<Module>();
716 : }
717 :
718 : ModuleEmbedderData* d = GetModuleDataFromContext(context);
719 1567 : CHECK(d->specifier_to_module_map
720 : .insert(std::make_pair(file_name, Global<Module>(isolate, module)))
721 : .second);
722 :
723 1567 : std::string dir_name = DirName(file_name);
724 1567 : CHECK(d->module_to_directory_map
725 : .insert(std::make_pair(Global<Module>(isolate, module), dir_name))
726 : .second);
727 :
728 2639 : for (int i = 0, length = module->GetModuleRequestsLength(); i < length; ++i) {
729 1072 : Local<String> name = module->GetModuleRequest(i);
730 2144 : std::string absolute_path = NormalizePath(ToSTLString(name), dir_name);
731 1072 : if (!d->specifier_to_module_map.count(absolute_path)) {
732 1402 : if (FetchModuleTree(context, absolute_path).IsEmpty()) {
733 0 : return MaybeLocal<Module>();
734 : }
735 : }
736 : }
737 :
738 1567 : return module;
739 : }
740 :
741 : namespace {
742 :
743 644 : struct DynamicImportData {
744 322 : DynamicImportData(Isolate* isolate_, Local<String> referrer_,
745 : Local<String> specifier_,
746 : Local<DynamicImportResult> result_)
747 322 : : isolate(isolate_) {
748 : referrer.Reset(isolate, referrer_);
749 322 : specifier.Reset(isolate, specifier_);
750 322 : result.Reset(isolate, result_);
751 322 : }
752 :
753 : Isolate* isolate;
754 : Global<String> referrer;
755 : Global<String> specifier;
756 : Global<DynamicImportResult> result;
757 : };
758 :
759 : } // namespace
760 322 : void Shell::HostImportModuleDynamically(Isolate* isolate,
761 : Local<String> referrer,
762 : Local<String> specifier,
763 : Local<DynamicImportResult> result) {
764 : DynamicImportData* data =
765 322 : new DynamicImportData(isolate, referrer, specifier, result);
766 322 : isolate->EnqueueMicrotask(Shell::DoHostImportModuleDynamically, data);
767 322 : }
768 :
769 322 : void Shell::DoHostImportModuleDynamically(void* import_data) {
770 : std::unique_ptr<DynamicImportData> import_data_(
771 : static_cast<DynamicImportData*>(import_data));
772 322 : Isolate* isolate(import_data_->isolate);
773 574 : HandleScope handle_scope(isolate);
774 :
775 322 : Local<String> referrer(import_data_->referrer.Get(isolate));
776 322 : Local<String> specifier(import_data_->specifier.Get(isolate));
777 : Local<DynamicImportResult> result(import_data_->result.Get(isolate));
778 :
779 : PerIsolateData* data = PerIsolateData::Get(isolate);
780 322 : Local<Context> realm = data->realms_[data->realm_current_].Get(isolate);
781 : Context::Scope context_scope(realm);
782 :
783 322 : std::string source_url = ToSTLString(referrer);
784 : std::string dir_name =
785 322 : IsAbsolutePath(source_url) ? DirName(source_url) : GetWorkingDirectory();
786 322 : std::string file_name = ToSTLString(specifier);
787 644 : std::string absolute_path = NormalizePath(file_name.c_str(), dir_name);
788 :
789 574 : TryCatch try_catch(isolate);
790 322 : try_catch.SetVerbose(true);
791 :
792 : ModuleEmbedderData* d = GetModuleDataFromContext(realm);
793 : Local<Module> root_module;
794 : auto module_it = d->specifier_to_module_map.find(absolute_path);
795 322 : if (module_it != d->specifier_to_module_map.end()) {
796 : root_module = module_it->second.Get(isolate);
797 532 : } else if (!FetchModuleTree(realm, absolute_path).ToLocal(&root_module)) {
798 28 : CHECK(try_catch.HasCaught());
799 28 : CHECK(result->FinishDynamicImportFailure(realm, try_catch.Exception()));
800 70 : return;
801 : }
802 :
803 : MaybeLocal<Value> maybe_result;
804 294 : if (root_module->Instantiate(realm, ResolveModuleCallback)) {
805 280 : maybe_result = root_module->Evaluate(realm);
806 280 : EmptyMessageQueues(isolate);
807 : }
808 :
809 : Local<Value> module;
810 294 : if (!maybe_result.ToLocal(&module)) {
811 : DCHECK(try_catch.HasCaught());
812 42 : CHECK(result->FinishDynamicImportFailure(realm, try_catch.Exception()));
813 : return;
814 : }
815 :
816 : DCHECK(!try_catch.HasCaught());
817 252 : CHECK(result->FinishDynamicImportSuccess(realm, root_module));
818 : }
819 :
820 628 : bool Shell::ExecuteModule(Isolate* isolate, const char* file_name) {
821 628 : HandleScope handle_scope(isolate);
822 :
823 : PerIsolateData* data = PerIsolateData::Get(isolate);
824 628 : Local<Context> realm = data->realms_[data->realm_current_].Get(isolate);
825 : Context::Scope context_scope(realm);
826 :
827 1884 : std::string absolute_path = NormalizePath(file_name, GetWorkingDirectory());
828 :
829 1256 : TryCatch try_catch(isolate);
830 628 : try_catch.SetVerbose(true);
831 :
832 : Local<Module> root_module;
833 : MaybeLocal<Value> maybe_exception;
834 :
835 1256 : if (!FetchModuleTree(realm, absolute_path).ToLocal(&root_module)) {
836 0 : CHECK(try_catch.HasCaught());
837 0 : ReportException(isolate, &try_catch);
838 0 : return false;
839 : }
840 :
841 : MaybeLocal<Value> maybe_result;
842 628 : if (root_module->Instantiate(realm, ResolveModuleCallback)) {
843 628 : maybe_result = root_module->Evaluate(realm);
844 628 : EmptyMessageQueues(isolate);
845 : }
846 : Local<Value> result;
847 628 : if (!maybe_result.ToLocal(&result)) {
848 : DCHECK(try_catch.HasCaught());
849 : // Print errors that happened during execution.
850 0 : ReportException(isolate, &try_catch);
851 0 : return false;
852 : }
853 : DCHECK(!try_catch.HasCaught());
854 628 : return true;
855 : }
856 :
857 61481 : PerIsolateData::RealmScope::RealmScope(PerIsolateData* data) : data_(data) {
858 61481 : data_->realm_count_ = 1;
859 61481 : data_->realm_current_ = 0;
860 61481 : data_->realm_switch_ = 0;
861 122962 : data_->realms_ = new Global<Context>[1];
862 61481 : data_->realms_[0].Reset(data_->isolate_,
863 122962 : data_->isolate_->GetEnteredContext());
864 61481 : }
865 :
866 :
867 61481 : PerIsolateData::RealmScope::~RealmScope() {
868 : // Drop realms to avoid keeping them alive. We don't dispose the
869 : // module embedder data for the first realm here, but instead do
870 : // it in RunShell or in RunMain, if not running in interactive mode
871 62476 : for (int i = 1; i < data_->realm_count_; ++i) {
872 995 : Global<Context>& realm = data_->realms_[i];
873 995 : if (realm.IsEmpty()) continue;
874 970 : DisposeModuleEmbedderData(realm.Get(data_->isolate_));
875 : // TODO(adamk): No need to reset manually, Globals reset when destructed.
876 : realm.Reset();
877 : }
878 122962 : delete[] data_->realms_;
879 : // TODO(adamk): No need to reset manually, Globals reset when destructed.
880 122962 : if (!data_->realm_shared_.IsEmpty())
881 : data_->realm_shared_.Reset();
882 61481 : }
883 :
884 :
885 449 : int PerIsolateData::RealmFind(Local<Context> context) {
886 3629 : for (int i = 0; i < realm_count_; ++i) {
887 7258 : if (realms_[i] == context) return i;
888 : }
889 : return -1;
890 : }
891 :
892 :
893 15869 : int PerIsolateData::RealmIndexOrThrow(
894 31753 : const v8::FunctionCallbackInfo<v8::Value>& args,
895 : int arg_offset) {
896 31738 : if (args.Length() < arg_offset || !args[arg_offset]->IsNumber()) {
897 0 : Throw(args.GetIsolate(), "Invalid argument");
898 0 : return -1;
899 : }
900 : int index = args[arg_offset]
901 15869 : ->Int32Value(args.GetIsolate()->GetCurrentContext())
902 31738 : .FromMaybe(-1);
903 31723 : if (index < 0 || index >= realm_count_ || realms_[index].IsEmpty()) {
904 15 : Throw(args.GetIsolate(), "Invalid realm index");
905 15 : return -1;
906 : }
907 : return index;
908 : }
909 :
910 :
911 : // performance.now() returns a time stamp as double, measured in milliseconds.
912 : // When FLAG_verify_predictable mode is enabled it returns result of
913 : // v8::Platform::MonotonicallyIncreasingTime().
914 6 : void Shell::PerformanceNow(const v8::FunctionCallbackInfo<v8::Value>& args) {
915 : if (i::FLAG_verify_predictable) {
916 : args.GetReturnValue().Set(g_platform->MonotonicallyIncreasingTime());
917 : } else {
918 : base::TimeDelta delta =
919 6 : base::TimeTicks::HighResolutionNow() - kInitialTicks;
920 3 : args.GetReturnValue().Set(delta.InMillisecondsF());
921 : }
922 3 : }
923 :
924 :
925 : // Realm.current() returns the index of the currently active realm.
926 898 : void Shell::RealmCurrent(const v8::FunctionCallbackInfo<v8::Value>& args) {
927 : Isolate* isolate = args.GetIsolate();
928 : PerIsolateData* data = PerIsolateData::Get(isolate);
929 449 : int index = data->RealmFind(isolate->GetEnteredContext());
930 898 : if (index == -1) return;
931 : args.GetReturnValue().Set(index);
932 : }
933 :
934 :
935 : // Realm.owner(o) returns the index of the realm that created o.
936 0 : void Shell::RealmOwner(const v8::FunctionCallbackInfo<v8::Value>& args) {
937 : Isolate* isolate = args.GetIsolate();
938 : PerIsolateData* data = PerIsolateData::Get(isolate);
939 0 : if (args.Length() < 1 || !args[0]->IsObject()) {
940 0 : Throw(args.GetIsolate(), "Invalid argument");
941 0 : return;
942 : }
943 : int index = data->RealmFind(args[0]
944 0 : ->ToObject(isolate->GetCurrentContext())
945 0 : .ToLocalChecked()
946 0 : ->CreationContext());
947 0 : if (index == -1) return;
948 : args.GetReturnValue().Set(index);
949 : }
950 :
951 :
952 : // Realm.global(i) returns the global object of realm i.
953 : // (Note that properties of global objects cannot be read/written cross-realm.)
954 2157 : void Shell::RealmGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
955 : PerIsolateData* data = PerIsolateData::Get(args.GetIsolate());
956 719 : int index = data->RealmIndexOrThrow(args, 0);
957 1438 : if (index == -1) return;
958 : args.GetReturnValue().Set(
959 1438 : Local<Context>::New(args.GetIsolate(), data->realms_[index])->Global());
960 : }
961 :
962 1010 : MaybeLocal<Context> Shell::CreateRealm(
963 1990 : const v8::FunctionCallbackInfo<v8::Value>& args, int index,
964 : v8::MaybeLocal<Value> global_object) {
965 : Isolate* isolate = args.GetIsolate();
966 1010 : TryCatch try_catch(isolate);
967 : PerIsolateData* data = PerIsolateData::Get(isolate);
968 1010 : if (index < 0) {
969 995 : Global<Context>* old_realms = data->realms_;
970 995 : index = data->realm_count_;
971 7037 : data->realms_ = new Global<Context>[++data->realm_count_];
972 6042 : for (int i = 0; i < index; ++i) {
973 5047 : data->realms_[i].Reset(isolate, old_realms[i]);
974 : old_realms[i].Reset();
975 : }
976 1990 : delete[] old_realms;
977 : }
978 1010 : Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
979 : Local<Context> context =
980 1010 : Context::New(isolate, NULL, global_template, global_object);
981 : DCHECK(!try_catch.HasCaught());
982 1010 : if (context.IsEmpty()) return MaybeLocal<Context>();
983 980 : InitializeModuleEmbedderData(context);
984 980 : data->realms_[index].Reset(isolate, context);
985 : args.GetReturnValue().Set(index);
986 980 : return context;
987 : }
988 :
989 495 : void Shell::DisposeRealm(const v8::FunctionCallbackInfo<v8::Value>& args,
990 : int index) {
991 : Isolate* isolate = args.GetIsolate();
992 : PerIsolateData* data = PerIsolateData::Get(isolate);
993 990 : DisposeModuleEmbedderData(data->realms_[index].Get(isolate));
994 495 : data->realms_[index].Reset();
995 495 : isolate->ContextDisposedNotification();
996 495 : isolate->IdleNotificationDeadline(g_platform->MonotonicallyIncreasingTime());
997 495 : }
998 :
999 : // Realm.create() creates a new realm with a distinct security token
1000 : // and returns its index.
1001 980 : void Shell::RealmCreate(const v8::FunctionCallbackInfo<v8::Value>& args) {
1002 980 : CreateRealm(args, -1, v8::MaybeLocal<Value>());
1003 980 : }
1004 :
1005 : // Realm.createAllowCrossRealmAccess() creates a new realm with the same
1006 : // security token as the current realm.
1007 15 : void Shell::RealmCreateAllowCrossRealmAccess(
1008 15 : const v8::FunctionCallbackInfo<v8::Value>& args) {
1009 : Local<Context> context;
1010 30 : if (CreateRealm(args, -1, v8::MaybeLocal<Value>()).ToLocal(&context)) {
1011 : context->SetSecurityToken(
1012 30 : args.GetIsolate()->GetEnteredContext()->GetSecurityToken());
1013 : }
1014 15 : }
1015 :
1016 : // Realm.navigate(i) creates a new realm with a distinct security token
1017 : // in place of realm i.
1018 45 : void Shell::RealmNavigate(const v8::FunctionCallbackInfo<v8::Value>& args) {
1019 : Isolate* isolate = args.GetIsolate();
1020 : PerIsolateData* data = PerIsolateData::Get(isolate);
1021 30 : int index = data->RealmIndexOrThrow(args, 0);
1022 45 : if (index == -1) return;
1023 45 : if (index == 0 || index == data->realm_current_ ||
1024 15 : index == data->realm_switch_) {
1025 15 : Throw(args.GetIsolate(), "Invalid realm index");
1026 15 : return;
1027 : }
1028 :
1029 15 : Local<Context> context = Local<Context>::New(isolate, data->realms_[index]);
1030 15 : v8::MaybeLocal<Value> global_object = context->Global();
1031 15 : DisposeRealm(args, index);
1032 15 : CreateRealm(args, index, global_object);
1033 : }
1034 :
1035 : // Realm.dispose(i) disposes the reference to the realm i.
1036 480 : void Shell::RealmDispose(const v8::FunctionCallbackInfo<v8::Value>& args) {
1037 : Isolate* isolate = args.GetIsolate();
1038 : PerIsolateData* data = PerIsolateData::Get(isolate);
1039 480 : int index = data->RealmIndexOrThrow(args, 0);
1040 480 : if (index == -1) return;
1041 960 : if (index == 0 ||
1042 960 : index == data->realm_current_ || index == data->realm_switch_) {
1043 0 : Throw(args.GetIsolate(), "Invalid realm index");
1044 0 : return;
1045 : }
1046 480 : DisposeRealm(args, index);
1047 : }
1048 :
1049 :
1050 : // Realm.switch(i) switches to the realm i for consecutive interactive inputs.
1051 0 : void Shell::RealmSwitch(const v8::FunctionCallbackInfo<v8::Value>& args) {
1052 : Isolate* isolate = args.GetIsolate();
1053 : PerIsolateData* data = PerIsolateData::Get(isolate);
1054 0 : int index = data->RealmIndexOrThrow(args, 0);
1055 0 : if (index == -1) return;
1056 0 : data->realm_switch_ = index;
1057 : }
1058 :
1059 :
1060 : // Realm.eval(i, s) evaluates s in realm i and returns the result.
1061 43249 : void Shell::RealmEval(const v8::FunctionCallbackInfo<v8::Value>& args) {
1062 : Isolate* isolate = args.GetIsolate();
1063 : PerIsolateData* data = PerIsolateData::Get(isolate);
1064 14640 : int index = data->RealmIndexOrThrow(args, 0);
1065 15296 : if (index == -1) return;
1066 29250 : if (args.Length() < 2 || !args[1]->IsString()) {
1067 0 : Throw(args.GetIsolate(), "Invalid argument");
1068 0 : return;
1069 : }
1070 : ScriptCompiler::Source script_source(
1071 29250 : args[1]->ToString(isolate->GetCurrentContext()).ToLocalChecked());
1072 : Local<UnboundScript> script;
1073 14625 : if (!ScriptCompiler::CompileUnboundScript(isolate, &script_source)
1074 29250 : .ToLocal(&script)) {
1075 : return;
1076 : }
1077 14610 : Local<Context> realm = Local<Context>::New(isolate, data->realms_[index]);
1078 14610 : realm->Enter();
1079 14610 : int previous_index = data->realm_current_;
1080 14610 : data->realm_current_ = data->realm_switch_ = index;
1081 : Local<Value> result;
1082 43830 : if (!script->BindToCurrentContext()->Run(realm).ToLocal(&result)) {
1083 626 : realm->Exit();
1084 626 : data->realm_current_ = data->realm_switch_ = previous_index;
1085 626 : return;
1086 : }
1087 13984 : realm->Exit();
1088 13984 : data->realm_current_ = data->realm_switch_ = previous_index;
1089 : args.GetReturnValue().Set(result);
1090 : }
1091 :
1092 :
1093 : // Realm.shared is an accessor for a single shared value across realms.
1094 8572 : void Shell::RealmSharedGet(Local<String> property,
1095 : const PropertyCallbackInfo<Value>& info) {
1096 : Isolate* isolate = info.GetIsolate();
1097 : PerIsolateData* data = PerIsolateData::Get(isolate);
1098 17144 : if (data->realm_shared_.IsEmpty()) return;
1099 : info.GetReturnValue().Set(data->realm_shared_);
1100 : }
1101 :
1102 417 : void Shell::RealmSharedSet(Local<String> property,
1103 : Local<Value> value,
1104 : const PropertyCallbackInfo<void>& info) {
1105 : Isolate* isolate = info.GetIsolate();
1106 : PerIsolateData* data = PerIsolateData::Get(isolate);
1107 : data->realm_shared_.Reset(isolate, value);
1108 417 : }
1109 :
1110 17922460 : void WriteToFile(FILE* file, const v8::FunctionCallbackInfo<v8::Value>& args) {
1111 10883580 : for (int i = 0; i < args.Length(); i++) {
1112 3519440 : HandleScope handle_scope(args.GetIsolate());
1113 3519440 : if (i != 0) {
1114 : fprintf(file, " ");
1115 : }
1116 :
1117 : // Explicitly catch potential exceptions in toString().
1118 7038880 : v8::TryCatch try_catch(args.GetIsolate());
1119 : Local<Value> arg = args[i];
1120 : Local<String> str_obj;
1121 :
1122 3519440 : if (arg->IsSymbol()) {
1123 0 : arg = Local<Symbol>::Cast(arg)->Name();
1124 : }
1125 7038880 : if (!arg->ToString(args.GetIsolate()->GetCurrentContext())
1126 7038880 : .ToLocal(&str_obj)) {
1127 0 : try_catch.ReThrow();
1128 1922350 : return;
1129 : }
1130 :
1131 7038880 : v8::String::Utf8Value str(str_obj);
1132 3519440 : int n = static_cast<int>(fwrite(*str, sizeof(**str), str.length(), file));
1133 3519440 : if (n != str.length()) {
1134 : printf("Error in fwrite\n");
1135 0 : Shell::Exit(1);
1136 : }
1137 3519440 : }
1138 : }
1139 :
1140 1922350 : void WriteAndFlush(FILE* file,
1141 : const v8::FunctionCallbackInfo<v8::Value>& args) {
1142 1922350 : WriteToFile(file, args);
1143 : fprintf(file, "\n");
1144 1922350 : fflush(file);
1145 1922350 : }
1146 :
1147 1922350 : void Shell::Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
1148 1922350 : WriteAndFlush(stdout, args);
1149 1922350 : }
1150 :
1151 0 : void Shell::PrintErr(const v8::FunctionCallbackInfo<v8::Value>& args) {
1152 0 : WriteAndFlush(stderr, args);
1153 0 : }
1154 :
1155 0 : void Shell::Write(const v8::FunctionCallbackInfo<v8::Value>& args) {
1156 0 : WriteToFile(stdout, args);
1157 0 : }
1158 :
1159 120 : void Shell::Read(const v8::FunctionCallbackInfo<v8::Value>& args) {
1160 40 : String::Utf8Value file(args[0]);
1161 40 : if (*file == NULL) {
1162 0 : Throw(args.GetIsolate(), "Error loading file");
1163 0 : return;
1164 : }
1165 40 : Local<String> source = ReadFile(args.GetIsolate(), *file);
1166 40 : if (source.IsEmpty()) {
1167 0 : Throw(args.GetIsolate(), "Error loading file");
1168 0 : return;
1169 : }
1170 40 : args.GetReturnValue().Set(source);
1171 : }
1172 :
1173 :
1174 0 : Local<String> Shell::ReadFromStdin(Isolate* isolate) {
1175 : static const int kBufferSize = 256;
1176 : char buffer[kBufferSize];
1177 : Local<String> accumulator =
1178 0 : String::NewFromUtf8(isolate, "", NewStringType::kNormal).ToLocalChecked();
1179 : int length;
1180 : while (true) {
1181 : // Continue reading if the line ends with an escape '\\' or the line has
1182 : // not been fully read into the buffer yet (does not end with '\n').
1183 : // If fgets gets an error, just give up.
1184 : char* input = NULL;
1185 0 : input = fgets(buffer, kBufferSize, stdin);
1186 0 : if (input == NULL) return Local<String>();
1187 0 : length = static_cast<int>(strlen(buffer));
1188 0 : if (length == 0) {
1189 0 : return accumulator;
1190 0 : } else if (buffer[length-1] != '\n') {
1191 : accumulator = String::Concat(
1192 : accumulator,
1193 : String::NewFromUtf8(isolate, buffer, NewStringType::kNormal, length)
1194 0 : .ToLocalChecked());
1195 0 : } else if (length > 1 && buffer[length-2] == '\\') {
1196 0 : buffer[length-2] = '\n';
1197 : accumulator = String::Concat(
1198 : accumulator,
1199 : String::NewFromUtf8(isolate, buffer, NewStringType::kNormal,
1200 0 : length - 1).ToLocalChecked());
1201 : } else {
1202 : return String::Concat(
1203 : accumulator,
1204 : String::NewFromUtf8(isolate, buffer, NewStringType::kNormal,
1205 0 : length - 1).ToLocalChecked());
1206 : }
1207 : }
1208 : }
1209 :
1210 :
1211 87242 : void Shell::Load(const v8::FunctionCallbackInfo<v8::Value>& args) {
1212 29644 : for (int i = 0; i < args.Length(); i++) {
1213 12468 : HandleScope handle_scope(args.GetIsolate());
1214 14822 : String::Utf8Value file(args[i]);
1215 12468 : if (*file == NULL) {
1216 0 : Throw(args.GetIsolate(), "Error loading file");
1217 0 : return;
1218 : }
1219 12468 : Local<String> source = ReadFile(args.GetIsolate(), *file);
1220 12468 : if (source.IsEmpty()) {
1221 17 : Throw(args.GetIsolate(), "Error loading file");
1222 17 : return;
1223 : }
1224 12451 : if (!ExecuteString(
1225 : args.GetIsolate(), source,
1226 12451 : String::NewFromUtf8(args.GetIsolate(), *file,
1227 : NewStringType::kNormal).ToLocalChecked(),
1228 24902 : false, true)) {
1229 10097 : Throw(args.GetIsolate(), "Error executing file");
1230 10097 : return;
1231 : }
1232 2354 : }
1233 : }
1234 :
1235 :
1236 2225 : void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) {
1237 : Isolate* isolate = args.GetIsolate();
1238 571 : HandleScope handle_scope(isolate);
1239 1127 : if (args.Length() < 1 || !args[0]->IsString()) {
1240 15 : Throw(args.GetIsolate(), "1st argument must be string");
1241 59 : return;
1242 : }
1243 :
1244 556 : if (!args.IsConstructCall()) {
1245 15 : Throw(args.GetIsolate(), "Worker must be constructed with new");
1246 15 : return;
1247 : }
1248 :
1249 : {
1250 : base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer());
1251 541 : if (workers_.length() >= kMaxWorkers) {
1252 0 : Throw(args.GetIsolate(), "Too many workers, I won't let you create more");
1253 0 : return;
1254 : }
1255 :
1256 : // Initialize the embedder field to NULL; if we return early without
1257 : // creating a new Worker (because the main thread is terminating) we can
1258 : // early-out from the instance calls.
1259 541 : args.Holder()->SetAlignedPointerInInternalField(0, NULL);
1260 :
1261 541 : if (!allow_new_workers_) return;
1262 :
1263 527 : Worker* worker = new Worker;
1264 527 : args.Holder()->SetAlignedPointerInInternalField(0, worker);
1265 527 : workers_.Add(worker);
1266 :
1267 1054 : String::Utf8Value script(args[0]);
1268 527 : if (!*script) {
1269 0 : Throw(args.GetIsolate(), "Can't get worker script");
1270 0 : return;
1271 : }
1272 527 : worker->StartExecuteInThread(*script);
1273 527 : }
1274 : }
1275 :
1276 :
1277 2481 : void Shell::WorkerPostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
1278 : Isolate* isolate = args.GetIsolate();
1279 827 : HandleScope handle_scope(isolate);
1280 :
1281 827 : if (args.Length() < 1) {
1282 0 : Throw(isolate, "Invalid argument");
1283 0 : return;
1284 : }
1285 :
1286 827 : Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
1287 827 : if (!worker) {
1288 : return;
1289 : }
1290 :
1291 813 : Local<Value> message = args[0];
1292 : Local<Value> transfer =
1293 1626 : args.Length() >= 2 ? args[1] : Local<Value>::Cast(Undefined(isolate));
1294 : std::unique_ptr<SerializationData> data =
1295 813 : Shell::SerializeValue(isolate, message, transfer);
1296 813 : if (data) {
1297 1116 : worker->PostMessage(std::move(data));
1298 813 : }
1299 : }
1300 :
1301 :
1302 996 : void Shell::WorkerGetMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
1303 : Isolate* isolate = args.GetIsolate();
1304 347 : HandleScope handle_scope(isolate);
1305 347 : Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
1306 347 : if (!worker) {
1307 0 : return;
1308 : }
1309 :
1310 347 : std::unique_ptr<SerializationData> data = worker->GetMessage();
1311 347 : if (data) {
1312 : Local<Value> value;
1313 906 : if (Shell::DeserializeValue(isolate, std::move(data)).ToLocal(&value)) {
1314 : args.GetReturnValue().Set(value);
1315 : }
1316 347 : }
1317 : }
1318 :
1319 :
1320 604 : void Shell::WorkerTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
1321 : Isolate* isolate = args.GetIsolate();
1322 302 : HandleScope handle_scope(isolate);
1323 302 : Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
1324 302 : if (!worker) {
1325 0 : return;
1326 : }
1327 :
1328 302 : worker->Terminate();
1329 : }
1330 :
1331 :
1332 0 : void Shell::QuitOnce(v8::FunctionCallbackInfo<v8::Value>* args) {
1333 : int exit_code = (*args)[0]
1334 0 : ->Int32Value(args->GetIsolate()->GetCurrentContext())
1335 0 : .FromMaybe(0);
1336 0 : CleanupWorkers();
1337 0 : args->GetIsolate()->Exit();
1338 0 : OnExit(args->GetIsolate());
1339 0 : Exit(exit_code);
1340 0 : }
1341 :
1342 :
1343 0 : void Shell::Quit(const v8::FunctionCallbackInfo<v8::Value>& args) {
1344 : base::CallOnce(&quit_once_, &QuitOnce,
1345 0 : const_cast<v8::FunctionCallbackInfo<v8::Value>*>(&args));
1346 0 : }
1347 :
1348 :
1349 135 : void Shell::Version(const v8::FunctionCallbackInfo<v8::Value>& args) {
1350 : args.GetReturnValue().Set(
1351 : String::NewFromUtf8(args.GetIsolate(), V8::GetVersion(),
1352 90 : NewStringType::kNormal).ToLocalChecked());
1353 45 : }
1354 :
1355 :
1356 11504 : void Shell::ReportException(Isolate* isolate, v8::TryCatch* try_catch) {
1357 11504 : HandleScope handle_scope(isolate);
1358 11504 : Local<Context> context = isolate->GetCurrentContext();
1359 : bool enter_context = context.IsEmpty();
1360 11504 : if (enter_context) {
1361 : context = Local<Context>::New(isolate, evaluation_context_);
1362 0 : context->Enter();
1363 : }
1364 : // Converts a V8 value to a C string.
1365 35890 : auto ToCString = [](const v8::String::Utf8Value& value) {
1366 : return *value ? *value : "<string conversion failed>";
1367 35890 : };
1368 :
1369 23008 : v8::String::Utf8Value exception(try_catch->Exception());
1370 : const char* exception_string = ToCString(exception);
1371 11504 : Local<Message> message = try_catch->Message();
1372 11504 : if (message.IsEmpty()) {
1373 : // V8 didn't provide any extra information about this error; just
1374 : // print the exception.
1375 : printf("%s\n", exception_string);
1376 23008 : } else if (message->GetScriptOrigin().Options().IsWasm()) {
1377 : // Print <WASM>[(function index)]((function name))+(offset): (message).
1378 0 : int function_index = message->GetLineNumber(context).FromJust() - 1;
1379 0 : int offset = message->GetStartColumn(context).FromJust();
1380 : printf("<WASM>[%d]+%d: %s\n", function_index, offset, exception_string);
1381 : } else {
1382 : // Print (filename):(line number): (message).
1383 11504 : v8::String::Utf8Value filename(message->GetScriptOrigin().ResourceName());
1384 : const char* filename_string = ToCString(filename);
1385 23008 : int linenum = message->GetLineNumber(context).FromMaybe(-1);
1386 : printf("%s:%i: %s\n", filename_string, linenum, exception_string);
1387 : Local<String> sourceline;
1388 23008 : if (message->GetSourceLine(context).ToLocal(&sourceline)) {
1389 : // Print line of source code.
1390 11504 : v8::String::Utf8Value sourcelinevalue(sourceline);
1391 : const char* sourceline_string = ToCString(sourcelinevalue);
1392 : printf("%s\n", sourceline_string);
1393 : // Print wavy underline (GetUnderline is deprecated).
1394 23008 : int start = message->GetStartColumn(context).FromJust();
1395 70311 : for (int i = 0; i < start; i++) {
1396 : printf(" ");
1397 : }
1398 23008 : int end = message->GetEndColumn(context).FromJust();
1399 29921 : for (int i = start; i < end; i++) {
1400 : printf("^");
1401 : }
1402 11504 : printf("\n");
1403 11504 : }
1404 : }
1405 : Local<Value> stack_trace_string;
1406 24401 : if (try_catch->StackTrace(context).ToLocal(&stack_trace_string) &&
1407 : stack_trace_string->IsString()) {
1408 1378 : v8::String::Utf8Value stack_trace(Local<String>::Cast(stack_trace_string));
1409 1378 : printf("%s\n", ToCString(stack_trace));
1410 : }
1411 : printf("\n");
1412 23008 : if (enter_context) context->Exit();
1413 11504 : }
1414 :
1415 :
1416 0 : int32_t* Counter::Bind(const char* name, bool is_histogram) {
1417 : int i;
1418 0 : for (i = 0; i < kMaxNameSize - 1 && name[i]; i++)
1419 0 : name_[i] = static_cast<char>(name[i]);
1420 0 : name_[i] = '\0';
1421 0 : is_histogram_ = is_histogram;
1422 0 : return ptr();
1423 : }
1424 :
1425 :
1426 0 : void Counter::AddSample(int32_t sample) {
1427 0 : count_++;
1428 0 : sample_total_ += sample;
1429 0 : }
1430 :
1431 :
1432 0 : CounterCollection::CounterCollection() {
1433 28618 : magic_number_ = 0xDEADFACE;
1434 28618 : max_counters_ = kMaxCounters;
1435 28618 : max_name_size_ = Counter::kMaxNameSize;
1436 28618 : counters_in_use_ = 0;
1437 0 : }
1438 :
1439 :
1440 0 : Counter* CounterCollection::GetNextCounter() {
1441 0 : if (counters_in_use_ == kMaxCounters) return NULL;
1442 0 : return &counters_[counters_in_use_++];
1443 : }
1444 :
1445 :
1446 0 : void Shell::MapCounters(v8::Isolate* isolate, const char* name) {
1447 : counters_file_ = base::OS::MemoryMappedFile::create(
1448 0 : name, sizeof(CounterCollection), &local_counters_);
1449 : void* memory = (counters_file_ == NULL) ?
1450 0 : NULL : counters_file_->memory();
1451 0 : if (memory == NULL) {
1452 : printf("Could not map counters file %s\n", name);
1453 0 : Exit(1);
1454 : }
1455 0 : counters_ = static_cast<CounterCollection*>(memory);
1456 0 : isolate->SetCounterFunction(LookupCounter);
1457 0 : isolate->SetCreateHistogramFunction(CreateHistogram);
1458 0 : isolate->SetAddHistogramSampleFunction(AddHistogramSample);
1459 0 : }
1460 :
1461 :
1462 0 : int CounterMap::Hash(const char* name) {
1463 : int h = 0;
1464 : int c;
1465 0 : while ((c = *name++) != 0) {
1466 0 : h += h << 5;
1467 0 : h += c;
1468 : }
1469 0 : return h;
1470 : }
1471 :
1472 :
1473 0 : Counter* Shell::GetCounter(const char* name, bool is_histogram) {
1474 0 : Counter* counter = counter_map_->Lookup(name);
1475 :
1476 0 : if (counter == NULL) {
1477 0 : counter = counters_->GetNextCounter();
1478 0 : if (counter != NULL) {
1479 0 : counter_map_->Set(name, counter);
1480 : counter->Bind(name, is_histogram);
1481 : }
1482 : } else {
1483 : DCHECK(counter->is_histogram() == is_histogram);
1484 : }
1485 0 : return counter;
1486 : }
1487 :
1488 :
1489 0 : int* Shell::LookupCounter(const char* name) {
1490 0 : Counter* counter = GetCounter(name, false);
1491 :
1492 0 : if (counter != NULL) {
1493 0 : return counter->ptr();
1494 : } else {
1495 : return NULL;
1496 : }
1497 : }
1498 :
1499 :
1500 0 : void* Shell::CreateHistogram(const char* name,
1501 : int min,
1502 : int max,
1503 : size_t buckets) {
1504 0 : return GetCounter(name, true);
1505 : }
1506 :
1507 :
1508 0 : void Shell::AddHistogramSample(void* histogram, int sample) {
1509 : Counter* counter = reinterpret_cast<Counter*>(histogram);
1510 : counter->AddSample(sample);
1511 0 : }
1512 :
1513 : // Turn a value into a human-readable string.
1514 0 : Local<String> Shell::Stringify(Isolate* isolate, Local<Value> value) {
1515 : v8::Local<v8::Context> context =
1516 0 : v8::Local<v8::Context>::New(isolate, evaluation_context_);
1517 0 : if (stringify_function_.IsEmpty()) {
1518 0 : int source_index = i::NativesCollection<i::D8>::GetIndex("d8");
1519 : i::Vector<const char> source_string =
1520 0 : i::NativesCollection<i::D8>::GetScriptSource(source_index);
1521 : i::Vector<const char> source_name =
1522 0 : i::NativesCollection<i::D8>::GetScriptName(source_index);
1523 : Local<String> source =
1524 : String::NewFromUtf8(isolate, source_string.start(),
1525 0 : NewStringType::kNormal, source_string.length())
1526 0 : .ToLocalChecked();
1527 : Local<String> name =
1528 : String::NewFromUtf8(isolate, source_name.start(),
1529 0 : NewStringType::kNormal, source_name.length())
1530 0 : .ToLocalChecked();
1531 : ScriptOrigin origin(name);
1532 : Local<Script> script =
1533 0 : Script::Compile(context, source, &origin).ToLocalChecked();
1534 : stringify_function_.Reset(
1535 0 : isolate, script->Run(context).ToLocalChecked().As<Function>());
1536 : }
1537 : Local<Function> fun = Local<Function>::New(isolate, stringify_function_);
1538 0 : Local<Value> argv[1] = {value};
1539 0 : v8::TryCatch try_catch(isolate);
1540 0 : MaybeLocal<Value> result = fun->Call(context, Undefined(isolate), 1, argv);
1541 0 : if (result.IsEmpty()) return String::Empty(isolate);
1542 0 : return result.ToLocalChecked().As<String>();
1543 : }
1544 :
1545 :
1546 62491 : Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
1547 62491 : Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate);
1548 : global_template->Set(
1549 : String::NewFromUtf8(isolate, "print", NewStringType::kNormal)
1550 : .ToLocalChecked(),
1551 187473 : FunctionTemplate::New(isolate, Print));
1552 : global_template->Set(
1553 : String::NewFromUtf8(isolate, "printErr", NewStringType::kNormal)
1554 : .ToLocalChecked(),
1555 187473 : FunctionTemplate::New(isolate, PrintErr));
1556 : global_template->Set(
1557 : String::NewFromUtf8(isolate, "write", NewStringType::kNormal)
1558 : .ToLocalChecked(),
1559 187473 : FunctionTemplate::New(isolate, Write));
1560 : global_template->Set(
1561 : String::NewFromUtf8(isolate, "read", NewStringType::kNormal)
1562 : .ToLocalChecked(),
1563 187473 : FunctionTemplate::New(isolate, Read));
1564 : global_template->Set(
1565 : String::NewFromUtf8(isolate, "readbuffer", NewStringType::kNormal)
1566 : .ToLocalChecked(),
1567 187473 : FunctionTemplate::New(isolate, ReadBuffer));
1568 : global_template->Set(
1569 : String::NewFromUtf8(isolate, "readline", NewStringType::kNormal)
1570 : .ToLocalChecked(),
1571 187473 : FunctionTemplate::New(isolate, ReadLine));
1572 : global_template->Set(
1573 : String::NewFromUtf8(isolate, "load", NewStringType::kNormal)
1574 : .ToLocalChecked(),
1575 187473 : FunctionTemplate::New(isolate, Load));
1576 : // Some Emscripten-generated code tries to call 'quit', which in turn would
1577 : // call C's exit(). This would lead to memory leaks, because there is no way
1578 : // we can terminate cleanly then, so we need a way to hide 'quit'.
1579 62491 : if (!options.omit_quit) {
1580 : global_template->Set(
1581 : String::NewFromUtf8(isolate, "quit", NewStringType::kNormal)
1582 : .ToLocalChecked(),
1583 187341 : FunctionTemplate::New(isolate, Quit));
1584 : }
1585 : global_template->Set(
1586 : String::NewFromUtf8(isolate, "version", NewStringType::kNormal)
1587 : .ToLocalChecked(),
1588 187473 : FunctionTemplate::New(isolate, Version));
1589 : global_template->Set(
1590 : Symbol::GetToStringTag(isolate),
1591 : String::NewFromUtf8(isolate, "global", NewStringType::kNormal)
1592 187473 : .ToLocalChecked());
1593 :
1594 : // Bind the Realm object.
1595 62491 : Local<ObjectTemplate> realm_template = ObjectTemplate::New(isolate);
1596 : realm_template->Set(
1597 : String::NewFromUtf8(isolate, "current", NewStringType::kNormal)
1598 : .ToLocalChecked(),
1599 187473 : FunctionTemplate::New(isolate, RealmCurrent));
1600 : realm_template->Set(
1601 : String::NewFromUtf8(isolate, "owner", NewStringType::kNormal)
1602 : .ToLocalChecked(),
1603 187473 : FunctionTemplate::New(isolate, RealmOwner));
1604 : realm_template->Set(
1605 : String::NewFromUtf8(isolate, "global", NewStringType::kNormal)
1606 : .ToLocalChecked(),
1607 187473 : FunctionTemplate::New(isolate, RealmGlobal));
1608 : realm_template->Set(
1609 : String::NewFromUtf8(isolate, "create", NewStringType::kNormal)
1610 : .ToLocalChecked(),
1611 187473 : FunctionTemplate::New(isolate, RealmCreate));
1612 : realm_template->Set(
1613 : String::NewFromUtf8(isolate, "createAllowCrossRealmAccess",
1614 : NewStringType::kNormal)
1615 : .ToLocalChecked(),
1616 187473 : FunctionTemplate::New(isolate, RealmCreateAllowCrossRealmAccess));
1617 : realm_template->Set(
1618 : String::NewFromUtf8(isolate, "navigate", NewStringType::kNormal)
1619 : .ToLocalChecked(),
1620 187473 : FunctionTemplate::New(isolate, RealmNavigate));
1621 : realm_template->Set(
1622 : String::NewFromUtf8(isolate, "dispose", NewStringType::kNormal)
1623 : .ToLocalChecked(),
1624 187473 : FunctionTemplate::New(isolate, RealmDispose));
1625 : realm_template->Set(
1626 : String::NewFromUtf8(isolate, "switch", NewStringType::kNormal)
1627 : .ToLocalChecked(),
1628 187473 : FunctionTemplate::New(isolate, RealmSwitch));
1629 : realm_template->Set(
1630 : String::NewFromUtf8(isolate, "eval", NewStringType::kNormal)
1631 : .ToLocalChecked(),
1632 187473 : FunctionTemplate::New(isolate, RealmEval));
1633 : realm_template->SetAccessor(
1634 : String::NewFromUtf8(isolate, "shared", NewStringType::kNormal)
1635 : .ToLocalChecked(),
1636 124982 : RealmSharedGet, RealmSharedSet);
1637 : global_template->Set(
1638 : String::NewFromUtf8(isolate, "Realm", NewStringType::kNormal)
1639 : .ToLocalChecked(),
1640 124982 : realm_template);
1641 :
1642 62491 : Local<ObjectTemplate> performance_template = ObjectTemplate::New(isolate);
1643 : performance_template->Set(
1644 : String::NewFromUtf8(isolate, "now", NewStringType::kNormal)
1645 : .ToLocalChecked(),
1646 187473 : FunctionTemplate::New(isolate, PerformanceNow));
1647 : global_template->Set(
1648 : String::NewFromUtf8(isolate, "performance", NewStringType::kNormal)
1649 : .ToLocalChecked(),
1650 124982 : performance_template);
1651 :
1652 : Local<FunctionTemplate> worker_fun_template =
1653 62491 : FunctionTemplate::New(isolate, WorkerNew);
1654 : Local<Signature> worker_signature =
1655 62491 : Signature::New(isolate, worker_fun_template);
1656 : worker_fun_template->SetClassName(
1657 : String::NewFromUtf8(isolate, "Worker", NewStringType::kNormal)
1658 124982 : .ToLocalChecked());
1659 62491 : worker_fun_template->ReadOnlyPrototype();
1660 124982 : worker_fun_template->PrototypeTemplate()->Set(
1661 : String::NewFromUtf8(isolate, "terminate", NewStringType::kNormal)
1662 : .ToLocalChecked(),
1663 : FunctionTemplate::New(isolate, WorkerTerminate, Local<Value>(),
1664 249964 : worker_signature));
1665 124982 : worker_fun_template->PrototypeTemplate()->Set(
1666 : String::NewFromUtf8(isolate, "postMessage", NewStringType::kNormal)
1667 : .ToLocalChecked(),
1668 : FunctionTemplate::New(isolate, WorkerPostMessage, Local<Value>(),
1669 249964 : worker_signature));
1670 124982 : worker_fun_template->PrototypeTemplate()->Set(
1671 : String::NewFromUtf8(isolate, "getMessage", NewStringType::kNormal)
1672 : .ToLocalChecked(),
1673 : FunctionTemplate::New(isolate, WorkerGetMessage, Local<Value>(),
1674 249964 : worker_signature));
1675 124982 : worker_fun_template->InstanceTemplate()->SetInternalFieldCount(1);
1676 : global_template->Set(
1677 : String::NewFromUtf8(isolate, "Worker", NewStringType::kNormal)
1678 : .ToLocalChecked(),
1679 124982 : worker_fun_template);
1680 :
1681 62491 : Local<ObjectTemplate> os_templ = ObjectTemplate::New(isolate);
1682 62491 : AddOSMethods(isolate, os_templ);
1683 : global_template->Set(
1684 : String::NewFromUtf8(isolate, "os", NewStringType::kNormal)
1685 : .ToLocalChecked(),
1686 124982 : os_templ);
1687 :
1688 62491 : return global_template;
1689 : }
1690 :
1691 12127 : static void PrintNonErrorsMessageCallback(Local<Message> message,
1692 : Local<Value> error) {
1693 : // Nothing to do here for errors, exceptions thrown up to the shell will be
1694 : // reported
1695 : // separately by {Shell::ReportException} after they are caught.
1696 : // Do print other kinds of messages.
1697 12127 : switch (message->ErrorLevel()) {
1698 : case v8::Isolate::kMessageWarning:
1699 : case v8::Isolate::kMessageLog:
1700 : case v8::Isolate::kMessageInfo:
1701 : case v8::Isolate::kMessageDebug: {
1702 : break;
1703 : }
1704 :
1705 : case v8::Isolate::kMessageError: {
1706 : // Ignore errors, printed elsewhere.
1707 11542 : return;
1708 : }
1709 :
1710 : default: {
1711 0 : UNREACHABLE();
1712 : break;
1713 : }
1714 : }
1715 : // Converts a V8 value to a C string.
1716 1170 : auto ToCString = [](const v8::String::Utf8Value& value) {
1717 : return *value ? *value : "<string conversion failed>";
1718 1170 : };
1719 585 : Isolate* isolate = Isolate::GetCurrent();
1720 1170 : v8::String::Utf8Value msg(message->Get());
1721 : const char* msg_string = ToCString(msg);
1722 : // Print (filename):(line number): (message).
1723 1170 : v8::String::Utf8Value filename(message->GetScriptOrigin().ResourceName());
1724 : const char* filename_string = ToCString(filename);
1725 585 : Maybe<int> maybeline = message->GetLineNumber(isolate->GetCurrentContext());
1726 585 : int linenum = maybeline.IsJust() ? maybeline.FromJust() : -1;
1727 585 : printf("%s:%i: %s\n", filename_string, linenum, msg_string);
1728 : }
1729 :
1730 28618 : void Shell::Initialize(Isolate* isolate) {
1731 : // Set up counters
1732 57236 : if (i::StrLength(i::FLAG_map_counters) != 0)
1733 0 : MapCounters(isolate, i::FLAG_map_counters);
1734 : // Disable default message reporting.
1735 : isolate->AddMessageListenerWithErrorLevel(
1736 : PrintNonErrorsMessageCallback,
1737 : v8::Isolate::kMessageError | v8::Isolate::kMessageWarning |
1738 : v8::Isolate::kMessageInfo | v8::Isolate::kMessageDebug |
1739 28618 : v8::Isolate::kMessageLog);
1740 28618 : }
1741 :
1742 :
1743 61481 : Local<Context> Shell::CreateEvaluationContext(Isolate* isolate) {
1744 : // This needs to be a critical section since this is not thread-safe
1745 : base::LockGuard<base::Mutex> lock_guard(context_mutex_.Pointer());
1746 : // Initialize the global objects
1747 61481 : Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
1748 61481 : EscapableHandleScope handle_scope(isolate);
1749 61481 : Local<Context> context = Context::New(isolate, NULL, global_template);
1750 : DCHECK(!context.IsEmpty());
1751 61481 : InitializeModuleEmbedderData(context);
1752 : Context::Scope scope(context);
1753 :
1754 : i::Factory* factory = reinterpret_cast<i::Isolate*>(isolate)->factory();
1755 61481 : i::JSArguments js_args = i::FLAG_js_arguments;
1756 : i::Handle<i::FixedArray> arguments_array =
1757 61481 : factory->NewFixedArray(js_args.argc);
1758 61481 : for (int j = 0; j < js_args.argc; j++) {
1759 : i::Handle<i::String> arg =
1760 0 : factory->NewStringFromUtf8(i::CStrVector(js_args[j])).ToHandleChecked();
1761 0 : arguments_array->set(j, *arg);
1762 : }
1763 : i::Handle<i::JSArray> arguments_jsarray =
1764 : factory->NewJSArrayWithElements(arguments_array);
1765 : context->Global()
1766 : ->Set(context,
1767 : String::NewFromUtf8(isolate, "arguments", NewStringType::kNormal)
1768 : .ToLocalChecked(),
1769 184443 : Utils::ToLocal(arguments_jsarray))
1770 122962 : .FromJust();
1771 61481 : return handle_scope.Escape(context);
1772 : }
1773 :
1774 : struct CounterAndKey {
1775 : Counter* counter;
1776 : const char* key;
1777 : };
1778 :
1779 :
1780 : inline bool operator<(const CounterAndKey& lhs, const CounterAndKey& rhs) {
1781 0 : return strcmp(lhs.key, rhs.key) < 0;
1782 : }
1783 :
1784 0 : void Shell::WriteIgnitionDispatchCountersFile(v8::Isolate* isolate) {
1785 0 : HandleScope handle_scope(isolate);
1786 0 : Local<Context> context = Context::New(isolate);
1787 : Context::Scope context_scope(context);
1788 :
1789 : Local<Object> dispatch_counters = reinterpret_cast<i::Isolate*>(isolate)
1790 : ->interpreter()
1791 0 : ->GetDispatchCountersObject();
1792 : std::ofstream dispatch_counters_stream(
1793 0 : i::FLAG_trace_ignition_dispatches_output_file);
1794 : dispatch_counters_stream << *String::Utf8Value(
1795 0 : JSON::Stringify(context, dispatch_counters).ToLocalChecked());
1796 0 : }
1797 :
1798 : // Write coverage data in LCOV format. See man page for geninfo(1).
1799 60954 : void Shell::WriteLcovData(v8::Isolate* isolate, const char* file) {
1800 121908 : if (!file) return;
1801 0 : HandleScope handle_scope(isolate);
1802 0 : debug::Coverage coverage = debug::Coverage::CollectPrecise(isolate);
1803 0 : std::ofstream sink(file, std::ofstream::app);
1804 0 : for (size_t i = 0; i < coverage.ScriptCount(); i++) {
1805 0 : debug::Coverage::ScriptData script_data = coverage.GetScriptData(i);
1806 0 : Local<debug::Script> script = script_data.GetScript();
1807 : // Skip unnamed scripts.
1808 : Local<String> name;
1809 0 : if (!script->Name().ToLocal(&name)) continue;
1810 0 : std::string file_name = ToSTLString(name);
1811 : // Skip scripts not backed by a file.
1812 0 : if (!std::ifstream(file_name).good()) continue;
1813 0 : sink << "SF:";
1814 0 : sink << NormalizePath(file_name, GetWorkingDirectory()) << std::endl;
1815 : std::vector<uint32_t> lines;
1816 0 : for (size_t j = 0; j < script_data.FunctionCount(); j++) {
1817 : debug::Coverage::FunctionData function_data =
1818 0 : script_data.GetFunctionData(j);
1819 : debug::Location start =
1820 0 : script->GetSourceLocation(function_data.StartOffset());
1821 : debug::Location end =
1822 0 : script->GetSourceLocation(function_data.EndOffset());
1823 0 : int start_line = start.GetLineNumber();
1824 0 : int end_line = end.GetLineNumber();
1825 0 : uint32_t count = function_data.Count();
1826 : // Ensure space in the array.
1827 0 : lines.resize(std::max(static_cast<size_t>(end_line + 1), lines.size()),
1828 0 : 0);
1829 : // Boundary lines could be shared between two functions with different
1830 : // invocation counts. Take the maximum.
1831 0 : lines[start_line] = std::max(lines[start_line], count);
1832 0 : lines[end_line] = std::max(lines[end_line], count);
1833 : // Invocation counts for non-boundary lines are overwritten.
1834 0 : for (int k = start_line + 1; k < end_line; k++) lines[k] = count;
1835 : // Write function stats.
1836 : Local<String> name;
1837 0 : std::stringstream name_stream;
1838 0 : if (function_data.Name().ToLocal(&name)) {
1839 0 : name_stream << ToSTLString(name);
1840 : } else {
1841 0 : name_stream << "<" << start_line + 1 << "-";
1842 0 : name_stream << start.GetColumnNumber() << ">";
1843 : }
1844 0 : sink << "FN:" << start_line + 1 << "," << name_stream.str() << std::endl;
1845 0 : sink << "FNDA:" << count << "," << name_stream.str() << std::endl;
1846 0 : }
1847 : // Write per-line coverage. LCOV uses 1-based line numbers.
1848 0 : for (size_t i = 0; i < lines.size(); i++) {
1849 0 : sink << "DA:" << (i + 1) << "," << lines[i] << std::endl;
1850 : }
1851 0 : sink << "end_of_record" << std::endl;
1852 0 : }
1853 : }
1854 :
1855 28618 : void Shell::OnExit(v8::Isolate* isolate) {
1856 : // Dump basic block profiling data.
1857 28618 : if (i::BasicBlockProfiler* profiler =
1858 : reinterpret_cast<i::Isolate*>(isolate)->basic_block_profiler()) {
1859 0 : i::OFStream os(stdout);
1860 0 : os << *profiler;
1861 : }
1862 28618 : isolate->Dispose();
1863 :
1864 28618 : if (i::FLAG_dump_counters || i::FLAG_dump_counters_nvp) {
1865 : int number_of_counters = 0;
1866 0 : for (CounterMap::Iterator i(counter_map_); i.More(); i.Next()) {
1867 0 : number_of_counters++;
1868 : }
1869 0 : CounterAndKey* counters = new CounterAndKey[number_of_counters];
1870 : int j = 0;
1871 0 : for (CounterMap::Iterator i(counter_map_); i.More(); i.Next(), j++) {
1872 0 : counters[j].counter = i.CurrentValue();
1873 0 : counters[j].key = i.CurrentKey();
1874 : }
1875 0 : std::sort(counters, counters + number_of_counters);
1876 :
1877 0 : if (i::FLAG_dump_counters_nvp) {
1878 : // Dump counters as name-value pairs.
1879 0 : for (j = 0; j < number_of_counters; j++) {
1880 0 : Counter* counter = counters[j].counter;
1881 0 : const char* key = counters[j].key;
1882 0 : if (counter->is_histogram()) {
1883 : printf("\"c:%s\"=%i\n", key, counter->count());
1884 : printf("\"t:%s\"=%i\n", key, counter->sample_total());
1885 : } else {
1886 : printf("\"%s\"=%i\n", key, counter->count());
1887 : }
1888 : }
1889 : } else {
1890 : // Dump counters in formatted boxes.
1891 : printf(
1892 : "+----------------------------------------------------------------+"
1893 : "-------------+\n");
1894 : printf(
1895 : "| Name |"
1896 : " Value |\n");
1897 : printf(
1898 : "+----------------------------------------------------------------+"
1899 : "-------------+\n");
1900 0 : for (j = 0; j < number_of_counters; j++) {
1901 0 : Counter* counter = counters[j].counter;
1902 0 : const char* key = counters[j].key;
1903 0 : if (counter->is_histogram()) {
1904 : printf("| c:%-60s | %11i |\n", key, counter->count());
1905 : printf("| t:%-60s | %11i |\n", key, counter->sample_total());
1906 : } else {
1907 : printf("| %-62s | %11i |\n", key, counter->count());
1908 : }
1909 : }
1910 : printf(
1911 : "+----------------------------------------------------------------+"
1912 : "-------------+\n");
1913 : }
1914 0 : delete [] counters;
1915 : }
1916 :
1917 28618 : delete counters_file_;
1918 57236 : delete counter_map_;
1919 28618 : }
1920 :
1921 :
1922 146112 : static FILE* FOpen(const char* path, const char* mode) {
1923 : #if defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64))
1924 : FILE* result;
1925 : if (fopen_s(&result, path, mode) == 0) {
1926 : return result;
1927 : } else {
1928 : return NULL;
1929 : }
1930 : #else
1931 146112 : FILE* file = fopen(path, mode);
1932 146112 : if (file == NULL) return NULL;
1933 : struct stat file_stat;
1934 292162 : if (fstat(fileno(file), &file_stat) != 0) return NULL;
1935 146081 : bool is_regular_file = ((file_stat.st_mode & S_IFREG) != 0);
1936 146081 : if (is_regular_file) return file;
1937 0 : fclose(file);
1938 0 : return NULL;
1939 : #endif
1940 : }
1941 :
1942 146112 : static char* ReadChars(const char* name, int* size_out) {
1943 146112 : FILE* file = FOpen(name, "rb");
1944 146112 : if (file == NULL) return NULL;
1945 :
1946 146081 : fseek(file, 0, SEEK_END);
1947 146081 : size_t size = ftell(file);
1948 146081 : rewind(file);
1949 :
1950 146081 : char* chars = new char[size + 1];
1951 146081 : chars[size] = '\0';
1952 438243 : for (size_t i = 0; i < size;) {
1953 292162 : i += fread(&chars[i], 1, size - i, file);
1954 146081 : if (ferror(file)) {
1955 0 : fclose(file);
1956 0 : delete[] chars;
1957 : return nullptr;
1958 : }
1959 : }
1960 146081 : fclose(file);
1961 146081 : *size_out = static_cast<int>(size);
1962 146081 : return chars;
1963 : }
1964 :
1965 :
1966 : struct DataAndPersistent {
1967 : uint8_t* data;
1968 : int byte_length;
1969 : Global<ArrayBuffer> handle;
1970 : };
1971 :
1972 :
1973 2 : static void ReadBufferWeakCallback(
1974 10 : const v8::WeakCallbackInfo<DataAndPersistent>& data) {
1975 2 : int byte_length = data.GetParameter()->byte_length;
1976 : data.GetIsolate()->AdjustAmountOfExternalAllocatedMemory(
1977 2 : -static_cast<intptr_t>(byte_length));
1978 :
1979 2 : delete[] data.GetParameter()->data;
1980 : data.GetParameter()->handle.Reset();
1981 4 : delete data.GetParameter();
1982 2 : }
1983 :
1984 :
1985 90 : void Shell::ReadBuffer(const v8::FunctionCallbackInfo<v8::Value>& args) {
1986 : DCHECK(sizeof(char) == sizeof(uint8_t)); // NOLINT
1987 30 : String::Utf8Value filename(args[0]);
1988 : int length;
1989 : Isolate* isolate = args.GetIsolate();
1990 30 : if (*filename == NULL) {
1991 0 : Throw(isolate, "Error loading file");
1992 0 : return;
1993 : }
1994 :
1995 30 : DataAndPersistent* data = new DataAndPersistent;
1996 30 : data->data = reinterpret_cast<uint8_t*>(ReadChars(*filename, &length));
1997 30 : if (data->data == NULL) {
1998 0 : delete data;
1999 0 : Throw(isolate, "Error reading file");
2000 0 : return;
2001 : }
2002 30 : data->byte_length = length;
2003 30 : Local<v8::ArrayBuffer> buffer = ArrayBuffer::New(isolate, data->data, length);
2004 : data->handle.Reset(isolate, buffer);
2005 : data->handle.SetWeak(data, ReadBufferWeakCallback,
2006 : v8::WeakCallbackType::kParameter);
2007 : data->handle.MarkIndependent();
2008 30 : isolate->AdjustAmountOfExternalAllocatedMemory(length);
2009 :
2010 30 : args.GetReturnValue().Set(buffer);
2011 : }
2012 :
2013 : // Reads a file into a v8 string.
2014 146082 : Local<String> Shell::ReadFile(Isolate* isolate, const char* name) {
2015 146082 : int size = 0;
2016 146082 : char* chars = ReadChars(name, &size);
2017 146082 : if (chars == NULL) return Local<String>();
2018 : Local<String> result;
2019 146051 : if (i::FLAG_use_external_strings && internal::String::IsAscii(chars, size)) {
2020 : String::ExternalOneByteStringResource* resource =
2021 : new ExternalOwningOneByteStringResource(
2022 0 : std::unique_ptr<const char[]>(chars), size);
2023 0 : result = String::NewExternalOneByte(isolate, resource).ToLocalChecked();
2024 : } else {
2025 146051 : result = String::NewFromUtf8(isolate, chars, NewStringType::kNormal, size)
2026 146051 : .ToLocalChecked();
2027 146051 : delete[] chars;
2028 : }
2029 146051 : return result;
2030 : }
2031 :
2032 :
2033 0 : void Shell::RunShell(Isolate* isolate) {
2034 0 : HandleScope outer_scope(isolate);
2035 : v8::Local<v8::Context> context =
2036 : v8::Local<v8::Context>::New(isolate, evaluation_context_);
2037 : v8::Context::Scope context_scope(context);
2038 0 : PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
2039 : Local<String> name =
2040 : String::NewFromUtf8(isolate, "(d8)", NewStringType::kNormal)
2041 0 : .ToLocalChecked();
2042 0 : printf("V8 version %s\n", V8::GetVersion());
2043 : while (true) {
2044 0 : HandleScope inner_scope(isolate);
2045 : printf("d8> ");
2046 0 : Local<String> input = Shell::ReadFromStdin(isolate);
2047 0 : if (input.IsEmpty()) break;
2048 0 : ExecuteString(isolate, input, name, true, true);
2049 0 : }
2050 : printf("\n");
2051 : // We need to explicitly clean up the module embedder data for
2052 : // the interative shell context.
2053 0 : DisposeModuleEmbedderData(context);
2054 0 : }
2055 :
2056 : class InspectorFrontend final : public v8_inspector::V8Inspector::Channel {
2057 : public:
2058 7592 : explicit InspectorFrontend(Local<Context> context) {
2059 3796 : isolate_ = context->GetIsolate();
2060 : context_.Reset(isolate_, context);
2061 3796 : }
2062 7592 : virtual ~InspectorFrontend() = default;
2063 :
2064 : private:
2065 193374 : void sendResponse(
2066 : int callId,
2067 : std::unique_ptr<v8_inspector::StringBuffer> message) override {
2068 193374 : Send(message->string());
2069 193374 : }
2070 176281 : void sendNotification(
2071 : std::unique_ptr<v8_inspector::StringBuffer> message) override {
2072 176281 : Send(message->string());
2073 176281 : }
2074 0 : void flushProtocolNotifications() override {}
2075 :
2076 1108965 : void Send(const v8_inspector::StringView& string) {
2077 369655 : int length = static_cast<int>(string.length());
2078 : DCHECK(length < v8::String::kMaxLength);
2079 : Local<String> message =
2080 : (string.is8Bit()
2081 : ? v8::String::NewFromOneByte(
2082 : isolate_,
2083 : reinterpret_cast<const uint8_t*>(string.characters8()),
2084 0 : v8::NewStringType::kNormal, length)
2085 : : v8::String::NewFromTwoByte(
2086 : isolate_,
2087 : reinterpret_cast<const uint16_t*>(string.characters16()),
2088 369655 : v8::NewStringType::kNormal, length))
2089 739310 : .ToLocalChecked();
2090 : Local<String> callback_name =
2091 369655 : v8::String::NewFromUtf8(isolate_, "receive", v8::NewStringType::kNormal)
2092 369655 : .ToLocalChecked();
2093 369655 : Local<Context> context = context_.Get(isolate_);
2094 : Local<Value> callback =
2095 1108965 : context->Global()->Get(context, callback_name).ToLocalChecked();
2096 369655 : if (callback->IsFunction()) {
2097 369655 : v8::TryCatch try_catch(isolate_);
2098 : Local<Value> args[] = {message};
2099 : USE(Local<Function>::Cast(callback)->Call(context, Undefined(isolate_), 1,
2100 739310 : args));
2101 : #ifdef DEBUG
2102 : if (try_catch.HasCaught()) {
2103 : Local<Object> exception = Local<Object>::Cast(try_catch.Exception());
2104 : Local<String> key = v8::String::NewFromUtf8(isolate_, "message",
2105 : v8::NewStringType::kNormal)
2106 : .ToLocalChecked();
2107 : Local<String> expected =
2108 : v8::String::NewFromUtf8(isolate_,
2109 : "Maximum call stack size exceeded",
2110 : v8::NewStringType::kNormal)
2111 : .ToLocalChecked();
2112 : Local<Value> value = exception->Get(context, key).ToLocalChecked();
2113 : CHECK(value->StrictEquals(expected));
2114 : }
2115 : #endif
2116 : }
2117 369655 : }
2118 :
2119 : Isolate* isolate_;
2120 : Global<Context> context_;
2121 : };
2122 :
2123 121908 : class InspectorClient : public v8_inspector::V8InspectorClient {
2124 : public:
2125 121908 : InspectorClient(Local<Context> context, bool connect) {
2126 118112 : if (!connect) return;
2127 3796 : isolate_ = context->GetIsolate();
2128 7592 : channel_.reset(new InspectorFrontend(context));
2129 7592 : inspector_ = v8_inspector::V8Inspector::create(isolate_, this);
2130 7592 : session_ =
2131 3796 : inspector_->connect(1, channel_.get(), v8_inspector::StringView());
2132 3796 : context->SetAlignedPointerInEmbedderData(kInspectorClientIndex, this);
2133 : inspector_->contextCreated(v8_inspector::V8ContextInfo(
2134 7592 : context, kContextGroupId, v8_inspector::StringView()));
2135 :
2136 : Local<Value> function =
2137 7592 : FunctionTemplate::New(isolate_, SendInspectorMessage)
2138 7592 : ->GetFunction(context)
2139 3796 : .ToLocalChecked();
2140 : Local<String> function_name =
2141 3796 : String::NewFromUtf8(isolate_, "send", NewStringType::kNormal)
2142 3796 : .ToLocalChecked();
2143 11388 : CHECK(context->Global()->Set(context, function_name, function).FromJust());
2144 :
2145 3796 : v8::debug::SetLiveEditEnabled(isolate_, true);
2146 :
2147 3796 : context_.Reset(isolate_, context);
2148 : }
2149 :
2150 : private:
2151 : static v8_inspector::V8InspectorSession* GetSession(Local<Context> context) {
2152 : InspectorClient* inspector_client = static_cast<InspectorClient*>(
2153 : context->GetAlignedPointerFromEmbedderData(kInspectorClientIndex));
2154 : return inspector_client->session_.get();
2155 : }
2156 :
2157 56 : Local<Context> ensureDefaultContextInGroup(int group_id) override {
2158 : DCHECK(isolate_);
2159 : DCHECK_EQ(kContextGroupId, group_id);
2160 112 : return context_.Get(isolate_);
2161 : }
2162 :
2163 193374 : static void SendInspectorMessage(
2164 580122 : const v8::FunctionCallbackInfo<v8::Value>& args) {
2165 : Isolate* isolate = args.GetIsolate();
2166 193374 : v8::HandleScope handle_scope(isolate);
2167 193374 : Local<Context> context = isolate->GetCurrentContext();
2168 : args.GetReturnValue().Set(Undefined(isolate));
2169 193374 : Local<String> message = args[0]->ToString(context).ToLocalChecked();
2170 : v8_inspector::V8InspectorSession* session =
2171 : InspectorClient::GetSession(context);
2172 193374 : int length = message->Length();
2173 193374 : std::unique_ptr<uint16_t[]> buffer(new uint16_t[length]);
2174 193374 : message->Write(buffer.get(), 0, length);
2175 : v8_inspector::StringView message_view(buffer.get(), length);
2176 193374 : session->dispatchProtocolMessage(message_view);
2177 193374 : args.GetReturnValue().Set(True(isolate));
2178 193374 : }
2179 :
2180 : static const int kContextGroupId = 1;
2181 :
2182 : std::unique_ptr<v8_inspector::V8Inspector> inspector_;
2183 : std::unique_ptr<v8_inspector::V8InspectorSession> session_;
2184 : std::unique_ptr<v8_inspector::V8Inspector::Channel> channel_;
2185 : Global<Context> context_;
2186 : Isolate* isolate_;
2187 : };
2188 :
2189 0 : SourceGroup::~SourceGroup() {
2190 0 : delete thread_;
2191 0 : thread_ = NULL;
2192 0 : }
2193 :
2194 :
2195 60954 : void SourceGroup::Execute(Isolate* isolate) {
2196 : bool exception_was_thrown = false;
2197 194636 : for (int i = begin_offset_; i < end_offset_; ++i) {
2198 135053 : const char* arg = argv_[i];
2199 135053 : if (strcmp(arg, "-e") == 0 && i + 1 < end_offset_) {
2200 : // Execute argument given to -e option directly.
2201 1510 : HandleScope handle_scope(isolate);
2202 : Local<String> file_name =
2203 : String::NewFromUtf8(isolate, "unnamed", NewStringType::kNormal)
2204 1510 : .ToLocalChecked();
2205 : Local<String> source =
2206 1510 : String::NewFromUtf8(isolate, argv_[i + 1], NewStringType::kNormal)
2207 3020 : .ToLocalChecked();
2208 1510 : Shell::options.script_executed = true;
2209 1510 : if (!Shell::ExecuteString(isolate, source, file_name, false, true)) {
2210 : exception_was_thrown = true;
2211 1341 : break;
2212 : }
2213 : ++i;
2214 169 : continue;
2215 133543 : } else if (strcmp(arg, "--module") == 0 && i + 1 < end_offset_) {
2216 : // Treat the next file as a module.
2217 628 : arg = argv_[++i];
2218 628 : Shell::options.script_executed = true;
2219 628 : if (!Shell::ExecuteModule(isolate, arg)) {
2220 : exception_was_thrown = true;
2221 : break;
2222 : }
2223 : continue;
2224 132915 : } else if (arg[0] == '-') {
2225 : // Ignore other options. They have been parsed already.
2226 : continue;
2227 : }
2228 :
2229 : // Use all other arguments as names of files to load and run.
2230 131979 : HandleScope handle_scope(isolate);
2231 : Local<String> file_name =
2232 : String::NewFromUtf8(isolate, arg, NewStringType::kNormal)
2233 131979 : .ToLocalChecked();
2234 : Local<String> source = ReadFile(isolate, arg);
2235 131979 : if (source.IsEmpty()) {
2236 : printf("Error reading '%s'\n", arg);
2237 0 : Shell::Exit(1);
2238 : }
2239 131979 : Shell::options.script_executed = true;
2240 131979 : if (!Shell::ExecuteString(isolate, source, file_name, false, true)) {
2241 : exception_was_thrown = true;
2242 30 : break;
2243 : }
2244 131949 : }
2245 60954 : if (exception_was_thrown != Shell::options.expected_to_throw) {
2246 0 : Shell::Exit(1);
2247 : }
2248 60954 : }
2249 :
2250 0 : Local<String> SourceGroup::ReadFile(Isolate* isolate, const char* name) {
2251 131979 : return Shell::ReadFile(isolate, name);
2252 : }
2253 :
2254 :
2255 0 : base::Thread::Options SourceGroup::GetThreadOptions() {
2256 : // On some systems (OSX 10.6) the stack size default is 0.5Mb or less
2257 : // which is not enough to parse the big literal expressions used in tests.
2258 : // The stack size should be at least StackGuard::kLimitSize + some
2259 : // OS-specific padding for thread startup code. 2Mbytes seems to be enough.
2260 0 : return base::Thread::Options("IsolateThread", 2 * MB);
2261 : }
2262 :
2263 0 : void SourceGroup::ExecuteInThread() {
2264 : Isolate::CreateParams create_params;
2265 0 : create_params.array_buffer_allocator = Shell::array_buffer_allocator;
2266 : create_params.host_import_module_dynamically_callback_ =
2267 0 : Shell::HostImportModuleDynamically;
2268 0 : Isolate* isolate = Isolate::New(create_params);
2269 0 : D8Console console(isolate);
2270 0 : debug::SetConsoleDelegate(isolate, &console);
2271 0 : for (int i = 0; i < Shell::options.stress_runs; ++i) {
2272 0 : next_semaphore_.Wait();
2273 : {
2274 : Isolate::Scope iscope(isolate);
2275 : {
2276 0 : HandleScope scope(isolate);
2277 0 : PerIsolateData data(isolate);
2278 0 : Local<Context> context = Shell::CreateEvaluationContext(isolate);
2279 : {
2280 : Context::Scope cscope(context);
2281 : InspectorClient inspector_client(context,
2282 0 : Shell::options.enable_inspector);
2283 0 : PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
2284 0 : Execute(isolate);
2285 : }
2286 0 : DisposeModuleEmbedderData(context);
2287 : }
2288 0 : Shell::CollectGarbage(isolate);
2289 : }
2290 0 : done_semaphore_.Signal();
2291 : }
2292 :
2293 0 : isolate->Dispose();
2294 0 : }
2295 :
2296 :
2297 0 : void SourceGroup::StartExecuteInThread() {
2298 0 : if (thread_ == NULL) {
2299 0 : thread_ = new IsolateThread(this);
2300 0 : thread_->Start();
2301 : }
2302 0 : next_semaphore_.Signal();
2303 0 : }
2304 :
2305 :
2306 0 : void SourceGroup::WaitForThread() {
2307 0 : if (thread_ == NULL) return;
2308 0 : done_semaphore_.Wait();
2309 : }
2310 :
2311 :
2312 0 : void SourceGroup::JoinThread() {
2313 0 : if (thread_ == NULL) return;
2314 0 : thread_->Join();
2315 : }
2316 :
2317 0 : ExternalizedContents::~ExternalizedContents() {
2318 257 : Shell::array_buffer_allocator->Free(data_, size_);
2319 0 : }
2320 :
2321 2216 : void SerializationDataQueue::Enqueue(std::unique_ptr<SerializationData> data) {
2322 2216 : base::LockGuard<base::Mutex> lock_guard(&mutex_);
2323 2217 : data_.push_back(std::move(data));
2324 2217 : }
2325 :
2326 1446 : bool SerializationDataQueue::Dequeue(
2327 : std::unique_ptr<SerializationData>* out_data) {
2328 : out_data->reset();
2329 1446 : base::LockGuard<base::Mutex> lock_guard(&mutex_);
2330 1446 : if (data_.empty()) return false;
2331 : *out_data = std::move(data_[0]);
2332 1205 : data_.erase(data_.begin());
2333 1205 : return true;
2334 : }
2335 :
2336 :
2337 0 : bool SerializationDataQueue::IsEmpty() {
2338 0 : base::LockGuard<base::Mutex> lock_guard(&mutex_);
2339 0 : return data_.empty();
2340 : }
2341 :
2342 :
2343 1054 : void SerializationDataQueue::Clear() {
2344 1054 : base::LockGuard<base::Mutex> lock_guard(&mutex_);
2345 : data_.clear();
2346 1054 : }
2347 :
2348 :
2349 527 : Worker::Worker()
2350 : : in_semaphore_(0),
2351 : out_semaphore_(0),
2352 : thread_(NULL),
2353 : script_(NULL),
2354 1054 : running_(false) {}
2355 :
2356 :
2357 1054 : Worker::~Worker() {
2358 527 : delete thread_;
2359 527 : thread_ = NULL;
2360 527 : delete[] script_;
2361 527 : script_ = NULL;
2362 527 : in_queue_.Clear();
2363 527 : out_queue_.Clear();
2364 527 : }
2365 :
2366 :
2367 527 : void Worker::StartExecuteInThread(const char* script) {
2368 527 : running_ = true;
2369 527 : script_ = i::StrDup(script);
2370 1054 : thread_ = new WorkerThread(this);
2371 527 : thread_->Start();
2372 527 : }
2373 :
2374 1387 : void Worker::PostMessage(std::unique_ptr<SerializationData> data) {
2375 2774 : in_queue_.Enqueue(std::move(data));
2376 1387 : in_semaphore_.Signal();
2377 1387 : }
2378 :
2379 347 : std::unique_ptr<SerializationData> Worker::GetMessage() {
2380 347 : std::unique_ptr<SerializationData> result;
2381 920 : while (!out_queue_.Dequeue(&result)) {
2382 : // If the worker is no longer running, and there are no messages in the
2383 : // queue, don't expect any more messages from it.
2384 482 : if (!base::NoBarrier_Load(&running_)) break;
2385 226 : out_semaphore_.Wait();
2386 : }
2387 347 : return result;
2388 : }
2389 :
2390 :
2391 829 : void Worker::Terminate() {
2392 829 : base::NoBarrier_Store(&running_, false);
2393 : // Post NULL to wake the Worker thread message loop, and tell it to stop
2394 : // running.
2395 1658 : PostMessage(NULL);
2396 829 : }
2397 :
2398 :
2399 0 : void Worker::WaitForThread() {
2400 527 : Terminate();
2401 527 : thread_->Join();
2402 0 : }
2403 :
2404 :
2405 527 : void Worker::ExecuteInThread() {
2406 : Isolate::CreateParams create_params;
2407 527 : create_params.array_buffer_allocator = Shell::array_buffer_allocator;
2408 : create_params.host_import_module_dynamically_callback_ =
2409 527 : Shell::HostImportModuleDynamically;
2410 527 : Isolate* isolate = Isolate::New(create_params);
2411 527 : D8Console console(isolate);
2412 527 : debug::SetConsoleDelegate(isolate, &console);
2413 : {
2414 : Isolate::Scope iscope(isolate);
2415 : {
2416 527 : HandleScope scope(isolate);
2417 527 : PerIsolateData data(isolate);
2418 527 : Local<Context> context = Shell::CreateEvaluationContext(isolate);
2419 : {
2420 : Context::Scope cscope(context);
2421 1054 : PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
2422 :
2423 527 : Local<Object> global = context->Global();
2424 527 : Local<Value> this_value = External::New(isolate, this);
2425 : Local<FunctionTemplate> postmessage_fun_template =
2426 527 : FunctionTemplate::New(isolate, PostMessageOut, this_value);
2427 :
2428 : Local<Function> postmessage_fun;
2429 527 : if (postmessage_fun_template->GetFunction(context)
2430 1054 : .ToLocal(&postmessage_fun)) {
2431 : global->Set(context, String::NewFromUtf8(isolate, "postMessage",
2432 : NewStringType::kNormal)
2433 : .ToLocalChecked(),
2434 1581 : postmessage_fun).FromJust();
2435 : }
2436 :
2437 : // First run the script
2438 : Local<String> file_name =
2439 : String::NewFromUtf8(isolate, "unnamed", NewStringType::kNormal)
2440 527 : .ToLocalChecked();
2441 : Local<String> source =
2442 527 : String::NewFromUtf8(isolate, script_, NewStringType::kNormal)
2443 1054 : .ToLocalChecked();
2444 527 : if (Shell::ExecuteString(isolate, source, file_name, false, true)) {
2445 : // Get the message handler
2446 : Local<Value> onmessage =
2447 : global->Get(context, String::NewFromUtf8(isolate, "onmessage",
2448 : NewStringType::kNormal)
2449 1581 : .ToLocalChecked()).ToLocalChecked();
2450 527 : if (onmessage->IsFunction()) {
2451 : Local<Function> onmessage_fun = Local<Function>::Cast(onmessage);
2452 : // Now wait for messages
2453 : while (true) {
2454 873 : in_semaphore_.Wait();
2455 873 : std::unique_ptr<SerializationData> data;
2456 873 : if (!in_queue_.Dequeue(&data)) continue;
2457 873 : if (!data) {
2458 : break;
2459 : }
2460 964 : v8::TryCatch try_catch(isolate);
2461 : Local<Value> value;
2462 482 : if (Shell::DeserializeValue(isolate, std::move(data))
2463 964 : .ToLocal(&value)) {
2464 482 : Local<Value> argv[] = {value};
2465 482 : (void)onmessage_fun->Call(context, global, 1, argv);
2466 : }
2467 482 : if (try_catch.HasCaught()) {
2468 14 : Shell::ReportException(isolate, &try_catch);
2469 : }
2470 : }
2471 : }
2472 : }
2473 : }
2474 1054 : DisposeModuleEmbedderData(context);
2475 : }
2476 527 : Shell::CollectGarbage(isolate);
2477 : }
2478 527 : isolate->Dispose();
2479 :
2480 : // Post NULL to wake the thread waiting on GetMessage() if there is one.
2481 1053 : out_queue_.Enqueue(NULL);
2482 527 : out_semaphore_.Signal();
2483 527 : }
2484 :
2485 :
2486 909 : void Worker::PostMessageOut(const v8::FunctionCallbackInfo<v8::Value>& args) {
2487 : Isolate* isolate = args.GetIsolate();
2488 303 : HandleScope handle_scope(isolate);
2489 :
2490 303 : if (args.Length() < 1) {
2491 0 : Throw(isolate, "Invalid argument");
2492 303 : return;
2493 : }
2494 :
2495 303 : Local<Value> message = args[0];
2496 : Local<Value> transfer = Undefined(isolate);
2497 : std::unique_ptr<SerializationData> data =
2498 303 : Shell::SerializeValue(isolate, message, transfer);
2499 303 : if (data) {
2500 : DCHECK(args.Data()->IsExternal());
2501 : Local<External> this_value = Local<External>::Cast(args.Data());
2502 303 : Worker* worker = static_cast<Worker*>(this_value->Value());
2503 606 : worker->out_queue_.Enqueue(std::move(data));
2504 303 : worker->out_semaphore_.Signal();
2505 303 : }
2506 : }
2507 :
2508 :
2509 114472 : void SetFlagsFromString(const char* flags) {
2510 114472 : v8::V8::SetFlagsFromString(flags, static_cast<int>(strlen(flags)));
2511 114472 : }
2512 :
2513 :
2514 28618 : bool Shell::SetOptions(int argc, char* argv[]) {
2515 : bool logfile_per_isolate = false;
2516 334682 : for (int i = 0; i < argc; i++) {
2517 306064 : if (strcmp(argv[i], "--stress-opt") == 0) {
2518 8249 : options.stress_opt = true;
2519 8249 : argv[i] = NULL;
2520 595533 : } else if (strcmp(argv[i], "--nostress-opt") == 0 ||
2521 297718 : strcmp(argv[i], "--no-stress-opt") == 0) {
2522 209 : options.stress_opt = false;
2523 209 : argv[i] = NULL;
2524 297606 : } else if (strcmp(argv[i], "--stress-deopt") == 0) {
2525 0 : options.stress_deopt = true;
2526 0 : argv[i] = NULL;
2527 297606 : } else if (strcmp(argv[i], "--mock-arraybuffer-allocator") == 0) {
2528 14 : options.mock_arraybuffer_allocator = true;
2529 14 : argv[i] = NULL;
2530 595091 : } else if (strcmp(argv[i], "--noalways-opt") == 0 ||
2531 297499 : strcmp(argv[i], "--no-always-opt") == 0) {
2532 : // No support for stressing if we can't use --always-opt.
2533 468 : options.stress_opt = false;
2534 468 : options.stress_deopt = false;
2535 297124 : } else if (strcmp(argv[i], "--logfile-per-isolate") == 0) {
2536 : logfile_per_isolate = true;
2537 0 : argv[i] = NULL;
2538 297124 : } else if (strcmp(argv[i], "--shell") == 0) {
2539 0 : options.interactive_shell = true;
2540 0 : argv[i] = NULL;
2541 297124 : } else if (strcmp(argv[i], "--test") == 0) {
2542 28618 : options.test_shell = true;
2543 28618 : argv[i] = NULL;
2544 537012 : } else if (strcmp(argv[i], "--notest") == 0 ||
2545 268506 : strcmp(argv[i], "--no-test") == 0) {
2546 14 : options.test_shell = false;
2547 14 : argv[i] = NULL;
2548 268492 : } else if (strcmp(argv[i], "--send-idle-notification") == 0) {
2549 13 : options.send_idle_notification = true;
2550 13 : argv[i] = NULL;
2551 268479 : } else if (strcmp(argv[i], "--invoke-weak-callbacks") == 0) {
2552 2393 : options.invoke_weak_callbacks = true;
2553 : // TODO(jochen) See issue 3351
2554 2393 : options.send_idle_notification = true;
2555 2393 : argv[i] = NULL;
2556 266086 : } else if (strcmp(argv[i], "--omit-quit") == 0) {
2557 20 : options.omit_quit = true;
2558 20 : argv[i] = NULL;
2559 266066 : } else if (strcmp(argv[i], "-f") == 0) {
2560 : // Ignore any -f flags for compatibility with other stand-alone
2561 : // JavaScript engines.
2562 : continue;
2563 266066 : } else if (strcmp(argv[i], "--isolate") == 0) {
2564 0 : options.num_isolates++;
2565 266066 : } else if (strcmp(argv[i], "--throws") == 0) {
2566 1355 : options.expected_to_throw = true;
2567 1355 : argv[i] = NULL;
2568 264711 : } else if (strncmp(argv[i], "--icu-data-file=", 16) == 0) {
2569 0 : options.icu_data_file = argv[i] + 16;
2570 0 : argv[i] = NULL;
2571 : #ifdef V8_USE_EXTERNAL_STARTUP_DATA
2572 264711 : } else if (strncmp(argv[i], "--natives_blob=", 15) == 0) {
2573 0 : options.natives_blob = argv[i] + 15;
2574 0 : argv[i] = NULL;
2575 264711 : } else if (strncmp(argv[i], "--snapshot_blob=", 16) == 0) {
2576 0 : options.snapshot_blob = argv[i] + 16;
2577 0 : argv[i] = NULL;
2578 : #endif // V8_USE_EXTERNAL_STARTUP_DATA
2579 529422 : } else if (strcmp(argv[i], "--cache") == 0 ||
2580 264711 : strncmp(argv[i], "--cache=", 8) == 0) {
2581 69 : const char* value = argv[i] + 7;
2582 69 : if (!*value || strncmp(value, "=code", 6) == 0) {
2583 69 : options.compile_options = v8::ScriptCompiler::kProduceCodeCache;
2584 0 : } else if (strncmp(value, "=parse", 7) == 0) {
2585 0 : options.compile_options = v8::ScriptCompiler::kProduceParserCache;
2586 0 : } else if (strncmp(value, "=none", 6) == 0) {
2587 0 : options.compile_options = v8::ScriptCompiler::kNoCompileOptions;
2588 : } else {
2589 : printf("Unknown option to --cache.\n");
2590 0 : return false;
2591 : }
2592 69 : argv[i] = NULL;
2593 264642 : } else if (strcmp(argv[i], "--enable-tracing") == 0) {
2594 0 : options.trace_enabled = true;
2595 0 : argv[i] = NULL;
2596 264642 : } else if (strncmp(argv[i], "--trace-config=", 15) == 0) {
2597 0 : options.trace_config = argv[i] + 15;
2598 0 : argv[i] = NULL;
2599 264642 : } else if (strcmp(argv[i], "--enable-inspector") == 0) {
2600 1676 : options.enable_inspector = true;
2601 1676 : argv[i] = NULL;
2602 262966 : } else if (strncmp(argv[i], "--lcov=", 7) == 0) {
2603 0 : options.lcov_file = argv[i] + 7;
2604 0 : argv[i] = NULL;
2605 : }
2606 : }
2607 :
2608 28618 : v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
2609 :
2610 : // Set up isolated source groups.
2611 28618 : options.isolate_sources = new SourceGroup[options.num_isolates];
2612 : SourceGroup* current = options.isolate_sources;
2613 : current->Begin(argv, 1);
2614 92473 : for (int i = 1; i < argc; i++) {
2615 63855 : const char* str = argv[i];
2616 63855 : if (strcmp(str, "--isolate") == 0) {
2617 : current->End(i);
2618 0 : current++;
2619 0 : current->Begin(argv, i + 1);
2620 63855 : } else if (strcmp(str, "--module") == 0) {
2621 : // Pass on to SourceGroup, which understands this option.
2622 63563 : } else if (strncmp(argv[i], "--", 2) == 0) {
2623 : printf("Warning: unknown flag %s.\nTry --help for options\n", argv[i]);
2624 63115 : } else if (strcmp(str, "-e") == 0 && i + 1 < argc) {
2625 1494 : options.script_executed = true;
2626 61621 : } else if (strncmp(str, "-", 1) != 0) {
2627 : // Not a flag, so it must be a script to execute.
2628 61621 : options.script_executed = true;
2629 : }
2630 : }
2631 : current->End(argc);
2632 :
2633 28618 : if (!logfile_per_isolate && options.num_isolates) {
2634 28618 : SetFlagsFromString("--nologfile_per_isolate");
2635 : }
2636 :
2637 : return true;
2638 : }
2639 :
2640 :
2641 60954 : int Shell::RunMain(Isolate* isolate, int argc, char* argv[], bool last_run) {
2642 60954 : for (int i = 1; i < options.num_isolates; ++i) {
2643 0 : options.isolate_sources[i].StartExecuteInThread();
2644 : }
2645 : {
2646 60954 : if (options.lcov_file) {
2647 0 : debug::Coverage::SelectMode(isolate, debug::Coverage::kPreciseCount);
2648 : }
2649 60954 : HandleScope scope(isolate);
2650 60954 : Local<Context> context = CreateEvaluationContext(isolate);
2651 89572 : bool use_existing_context = last_run && options.use_interactive_shell();
2652 60954 : if (use_existing_context) {
2653 : // Keep using the same context in the interactive shell.
2654 : evaluation_context_.Reset(isolate, context);
2655 : }
2656 : {
2657 : Context::Scope cscope(context);
2658 121908 : InspectorClient inspector_client(context, options.enable_inspector);
2659 121908 : PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
2660 60954 : options.isolate_sources[0].Execute(isolate);
2661 : }
2662 60954 : if (!use_existing_context) {
2663 60954 : DisposeModuleEmbedderData(context);
2664 : }
2665 60954 : WriteLcovData(isolate, options.lcov_file);
2666 : }
2667 60954 : CollectGarbage(isolate);
2668 60954 : for (int i = 1; i < options.num_isolates; ++i) {
2669 0 : if (last_run) {
2670 0 : options.isolate_sources[i].JoinThread();
2671 : } else {
2672 0 : options.isolate_sources[i].WaitForThread();
2673 : }
2674 : }
2675 60954 : CleanupWorkers();
2676 60954 : return 0;
2677 : }
2678 :
2679 :
2680 90099 : void Shell::CollectGarbage(Isolate* isolate) {
2681 90099 : if (options.send_idle_notification) {
2682 : const double kLongIdlePauseInSeconds = 1.0;
2683 4937 : isolate->ContextDisposedNotification();
2684 : isolate->IdleNotificationDeadline(
2685 4937 : g_platform->MonotonicallyIncreasingTime() + kLongIdlePauseInSeconds);
2686 : }
2687 90099 : if (options.invoke_weak_callbacks) {
2688 : // By sending a low memory notifications, we will try hard to collect all
2689 : // garbage and will therefore also invoke all weak callbacks of actually
2690 : // unreachable persistent handles.
2691 4871 : isolate->LowMemoryNotification();
2692 : }
2693 90099 : }
2694 :
2695 292282 : void Shell::EmptyMessageQueues(Isolate* isolate) {
2696 146034 : if (i::FLAG_verify_predictable) return;
2697 : while (true) {
2698 : // Pump the message loop until it is empty.
2699 155026 : while (v8::platform::PumpMessageLoop(g_platform, isolate)) {
2700 8885 : isolate->RunMicrotasks();
2701 : }
2702 : // Run the idle tasks.
2703 : v8::platform::RunIdleTasks(g_platform, isolate,
2704 146141 : 50.0 / base::Time::kMillisecondsPerSecond);
2705 : // If there are still outstanding waiters, sleep a little (to wait for
2706 : // background tasks) and then try everything again.
2707 146141 : if (reinterpret_cast<i::Isolate*>(isolate)->GetWaitCountForTesting() > 0) {
2708 107 : base::OS::Sleep(base::TimeDelta::FromMilliseconds(1));
2709 : } else {
2710 : break;
2711 : }
2712 : }
2713 : }
2714 :
2715 3348 : class Serializer : public ValueSerializer::Delegate {
2716 : public:
2717 1116 : explicit Serializer(Isolate* isolate)
2718 : : isolate_(isolate),
2719 : serializer_(isolate, this),
2720 2232 : current_memory_usage_(0) {}
2721 :
2722 1116 : Maybe<bool> WriteValue(Local<Context> context, Local<Value> value,
2723 : Local<Value> transfer) {
2724 : bool ok;
2725 : DCHECK(!data_);
2726 1116 : data_.reset(new SerializationData);
2727 2232 : if (!PrepareTransfer(context, transfer).To(&ok)) {
2728 : return Nothing<bool>();
2729 : }
2730 1101 : serializer_.WriteHeader();
2731 :
2732 2202 : if (!serializer_.WriteValue(context, value).To(&ok)) {
2733 : data_.reset();
2734 : return Nothing<bool>();
2735 : }
2736 :
2737 1722 : if (!FinalizeTransfer().To(&ok)) {
2738 : return Nothing<bool>();
2739 : }
2740 :
2741 861 : std::pair<uint8_t*, size_t> pair = serializer_.Release();
2742 861 : data_->data_.reset(pair.first);
2743 861 : data_->size_ = pair.second;
2744 : return Just(true);
2745 : }
2746 :
2747 : std::unique_ptr<SerializationData> Release() { return std::move(data_); }
2748 :
2749 : protected:
2750 : // Implements ValueSerializer::Delegate.
2751 15 : void ThrowDataCloneError(Local<String> message) override {
2752 15 : isolate_->ThrowException(Exception::Error(message));
2753 15 : }
2754 :
2755 467 : Maybe<uint32_t> GetSharedArrayBufferId(
2756 : Isolate* isolate, Local<SharedArrayBuffer> shared_array_buffer) override {
2757 : DCHECK(data_ != nullptr);
2758 934 : for (size_t index = 0; index < shared_array_buffers_.size(); ++index) {
2759 467 : if (shared_array_buffers_[index] == shared_array_buffer) {
2760 0 : return Just<uint32_t>(static_cast<uint32_t>(index));
2761 : }
2762 : }
2763 :
2764 : size_t index = shared_array_buffers_.size();
2765 467 : shared_array_buffers_.emplace_back(isolate_, shared_array_buffer);
2766 467 : return Just<uint32_t>(static_cast<uint32_t>(index));
2767 : }
2768 :
2769 1116 : void* ReallocateBufferMemory(void* old_buffer, size_t size,
2770 : size_t* actual_size) override {
2771 : // Not accurate, because we don't take into account reallocated buffers,
2772 : // but this is fine for testing.
2773 1116 : current_memory_usage_ += size;
2774 1116 : if (current_memory_usage_ > kMaxSerializerMemoryUsage) return nullptr;
2775 :
2776 1101 : void* result = realloc(old_buffer, size);
2777 1101 : *actual_size = result ? size : 0;
2778 1101 : return result;
2779 : }
2780 :
2781 240 : void FreeBufferMemory(void* buffer) override { free(buffer); }
2782 :
2783 : private:
2784 1116 : Maybe<bool> PrepareTransfer(Local<Context> context, Local<Value> transfer) {
2785 1116 : if (transfer->IsArray()) {
2786 : Local<Array> transfer_array = Local<Array>::Cast(transfer);
2787 255 : uint32_t length = transfer_array->Length();
2788 30 : for (uint32_t i = 0; i < length; ++i) {
2789 : Local<Value> element;
2790 510 : if (transfer_array->Get(context, i).ToLocal(&element)) {
2791 240 : if (!element->IsArrayBuffer()) {
2792 210 : Throw(isolate_, "Transfer array elements must be an ArrayBuffer");
2793 210 : break;
2794 : }
2795 :
2796 30 : Local<ArrayBuffer> array_buffer = Local<ArrayBuffer>::Cast(element);
2797 : serializer_.TransferArrayBuffer(
2798 60 : static_cast<uint32_t>(array_buffers_.size()), array_buffer);
2799 30 : array_buffers_.emplace_back(isolate_, array_buffer);
2800 : } else {
2801 : return Nothing<bool>();
2802 : }
2803 : }
2804 : return Just(true);
2805 861 : } else if (transfer->IsUndefined()) {
2806 : return Just(true);
2807 : } else {
2808 0 : Throw(isolate_, "Transfer list must be an Array or undefined");
2809 : return Nothing<bool>();
2810 : }
2811 : }
2812 :
2813 : template <typename T>
2814 287 : typename T::Contents MaybeExternalize(Local<T> array_buffer) {
2815 287 : if (array_buffer->IsExternal()) {
2816 171 : return array_buffer->GetContents();
2817 : } else {
2818 116 : typename T::Contents contents = array_buffer->Externalize();
2819 116 : data_->externalized_contents_.emplace_back(contents);
2820 116 : return contents;
2821 : }
2822 : }
2823 :
2824 861 : Maybe<bool> FinalizeTransfer() {
2825 1752 : for (const auto& global_array_buffer : array_buffers_) {
2826 : Local<ArrayBuffer> array_buffer =
2827 30 : Local<ArrayBuffer>::New(isolate_, global_array_buffer);
2828 30 : if (!array_buffer->IsNeuterable()) {
2829 0 : Throw(isolate_, "ArrayBuffer could not be transferred");
2830 0 : return Nothing<bool>();
2831 : }
2832 :
2833 30 : ArrayBuffer::Contents contents = MaybeExternalize(array_buffer);
2834 30 : array_buffer->Neuter();
2835 30 : data_->array_buffer_contents_.push_back(contents);
2836 : }
2837 :
2838 1979 : for (const auto& global_shared_array_buffer : shared_array_buffers_) {
2839 : Local<SharedArrayBuffer> shared_array_buffer =
2840 514 : Local<SharedArrayBuffer>::New(isolate_, global_shared_array_buffer);
2841 : data_->shared_array_buffer_contents_.push_back(
2842 771 : MaybeExternalize(shared_array_buffer));
2843 : }
2844 :
2845 : return Just(true);
2846 : }
2847 :
2848 : Isolate* isolate_;
2849 : ValueSerializer serializer_;
2850 : std::unique_ptr<SerializationData> data_;
2851 : std::vector<Global<ArrayBuffer>> array_buffers_;
2852 : std::vector<Global<SharedArrayBuffer>> shared_array_buffers_;
2853 : size_t current_memory_usage_;
2854 :
2855 : DISALLOW_COPY_AND_ASSIGN(Serializer);
2856 : };
2857 :
2858 2352 : class Deserializer : public ValueDeserializer::Delegate {
2859 : public:
2860 784 : Deserializer(Isolate* isolate, std::unique_ptr<SerializationData> data)
2861 : : isolate_(isolate),
2862 : deserializer_(isolate, data->data(), data->size(), this),
2863 2352 : data_(std::move(data)) {
2864 784 : deserializer_.SetSupportsLegacyWireFormat(true);
2865 784 : }
2866 :
2867 784 : MaybeLocal<Value> ReadValue(Local<Context> context) {
2868 : bool read_header;
2869 1568 : if (!deserializer_.ReadHeader(context).To(&read_header)) {
2870 0 : return MaybeLocal<Value>();
2871 : }
2872 :
2873 : uint32_t index = 0;
2874 1598 : for (const auto& contents : data_->array_buffer_contents()) {
2875 : Local<ArrayBuffer> array_buffer =
2876 30 : ArrayBuffer::New(isolate_, contents.Data(), contents.ByteLength());
2877 30 : deserializer_.TransferArrayBuffer(index++, array_buffer);
2878 : }
2879 :
2880 : index = 0;
2881 1825 : for (const auto& contents : data_->shared_array_buffer_contents()) {
2882 : Local<SharedArrayBuffer> shared_array_buffer = SharedArrayBuffer::New(
2883 257 : isolate_, contents.Data(), contents.ByteLength());
2884 257 : deserializer_.TransferSharedArrayBuffer(index++, shared_array_buffer);
2885 : }
2886 :
2887 784 : return deserializer_.ReadValue(context);
2888 : }
2889 :
2890 : private:
2891 : Isolate* isolate_;
2892 : ValueDeserializer deserializer_;
2893 : std::unique_ptr<SerializationData> data_;
2894 :
2895 : DISALLOW_COPY_AND_ASSIGN(Deserializer);
2896 : };
2897 :
2898 1116 : std::unique_ptr<SerializationData> Shell::SerializeValue(
2899 : Isolate* isolate, Local<Value> value, Local<Value> transfer) {
2900 : bool ok;
2901 1116 : Local<Context> context = isolate->GetCurrentContext();
2902 1116 : Serializer serializer(isolate);
2903 2232 : if (serializer.WriteValue(context, value, transfer).To(&ok)) {
2904 : std::unique_ptr<SerializationData> data = serializer.Release();
2905 : base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer());
2906 861 : data->AppendExternalizedContentsTo(&externalized_contents_);
2907 : return data;
2908 : }
2909 1116 : return nullptr;
2910 : }
2911 :
2912 784 : MaybeLocal<Value> Shell::DeserializeValue(
2913 : Isolate* isolate, std::unique_ptr<SerializationData> data) {
2914 : Local<Value> value;
2915 784 : Local<Context> context = isolate->GetCurrentContext();
2916 1568 : Deserializer deserializer(isolate, std::move(data));
2917 784 : return deserializer.ReadValue(context);
2918 : }
2919 :
2920 :
2921 60954 : void Shell::CleanupWorkers() {
2922 : // Make a copy of workers_, because we don't want to call Worker::Terminate
2923 : // while holding the workers_mutex_ lock. Otherwise, if a worker is about to
2924 : // create a new Worker, it would deadlock.
2925 : i::List<Worker*> workers_copy;
2926 : {
2927 : base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer());
2928 60954 : allow_new_workers_ = false;
2929 : workers_copy.AddAll(workers_);
2930 : workers_.Clear();
2931 : }
2932 :
2933 61481 : for (int i = 0; i < workers_copy.length(); ++i) {
2934 1054 : Worker* worker = workers_copy[i];
2935 : worker->WaitForThread();
2936 527 : delete worker;
2937 : }
2938 :
2939 : // Now that all workers are terminated, we can re-enable Worker creation.
2940 : base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer());
2941 60954 : allow_new_workers_ = true;
2942 : externalized_contents_.clear();
2943 60954 : }
2944 :
2945 28618 : int Shell::Main(int argc, char* argv[]) {
2946 28618 : std::ofstream trace_file;
2947 : #if (defined(_WIN32) || defined(_WIN64))
2948 : UINT new_flags =
2949 : SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX;
2950 : UINT existing_flags = SetErrorMode(new_flags);
2951 : SetErrorMode(existing_flags | new_flags);
2952 : #if defined(_MSC_VER)
2953 : _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
2954 : _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
2955 : _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
2956 : _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
2957 : _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
2958 : _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
2959 : _set_error_mode(_OUT_TO_STDERR);
2960 : #endif // defined(_MSC_VER)
2961 : #endif // defined(_WIN32) || defined(_WIN64)
2962 28618 : if (!SetOptions(argc, argv)) return 1;
2963 28618 : v8::V8::InitializeICUDefaultLocation(argv[0], options.icu_data_file);
2964 : g_platform = i::FLAG_verify_predictable
2965 : ? new PredictablePlatform()
2966 : : v8::platform::CreateDefaultPlatform(
2967 28618 : 0, v8::platform::IdleTaskSupport::kEnabled);
2968 :
2969 : platform::tracing::TracingController* tracing_controller;
2970 28618 : if (options.trace_enabled) {
2971 0 : trace_file.open("v8_trace.json");
2972 0 : tracing_controller = new platform::tracing::TracingController();
2973 : platform::tracing::TraceBuffer* trace_buffer =
2974 : platform::tracing::TraceBuffer::CreateTraceBufferRingBuffer(
2975 : platform::tracing::TraceBuffer::kRingBufferChunks,
2976 0 : platform::tracing::TraceWriter::CreateJSONTraceWriter(trace_file));
2977 0 : tracing_controller->Initialize(trace_buffer);
2978 : if (!i::FLAG_verify_predictable) {
2979 0 : platform::SetTracingController(g_platform, tracing_controller);
2980 : }
2981 : }
2982 :
2983 28618 : v8::V8::InitializePlatform(g_platform);
2984 28618 : v8::V8::Initialize();
2985 28618 : if (options.natives_blob || options.snapshot_blob) {
2986 : v8::V8::InitializeExternalStartupData(options.natives_blob,
2987 0 : options.snapshot_blob);
2988 : } else {
2989 28618 : v8::V8::InitializeExternalStartupData(argv[0]);
2990 : }
2991 28618 : SetFlagsFromString("--trace-hydrogen-file=hydrogen.cfg");
2992 28618 : SetFlagsFromString("--trace-turbo-cfg-file=turbo.cfg");
2993 28618 : SetFlagsFromString("--redirect-code-traces-to=code.asm");
2994 : int result = 0;
2995 : Isolate::CreateParams create_params;
2996 28618 : ShellArrayBufferAllocator shell_array_buffer_allocator;
2997 28618 : MockArrayBufferAllocator mock_arraybuffer_allocator;
2998 28618 : if (options.mock_arraybuffer_allocator) {
2999 14 : Shell::array_buffer_allocator = &mock_arraybuffer_allocator;
3000 : } else {
3001 28604 : Shell::array_buffer_allocator = &shell_array_buffer_allocator;
3002 : }
3003 28618 : create_params.array_buffer_allocator = Shell::array_buffer_allocator;
3004 : #ifdef ENABLE_VTUNE_JIT_INTERFACE
3005 : create_params.code_event_handler = vTune::GetVtuneCodeEventHandler();
3006 : #endif
3007 : create_params.constraints.ConfigureDefaults(
3008 28618 : base::SysInfo::AmountOfPhysicalMemory(),
3009 57236 : base::SysInfo::AmountOfVirtualMemory());
3010 :
3011 57236 : Shell::counter_map_ = new CounterMap();
3012 28618 : if (i::FLAG_dump_counters || i::FLAG_dump_counters_nvp || i::FLAG_gc_stats) {
3013 0 : create_params.counter_lookup_callback = LookupCounter;
3014 0 : create_params.create_histogram_callback = CreateHistogram;
3015 0 : create_params.add_histogram_sample_callback = AddHistogramSample;
3016 : }
3017 :
3018 : create_params.host_import_module_dynamically_callback_ =
3019 28618 : Shell::HostImportModuleDynamically;
3020 :
3021 28618 : if (i::trap_handler::UseTrapHandler()) {
3022 2359 : if (!v8::V8::RegisterDefaultSignalHandler()) {
3023 0 : fprintf(stderr, "Could not register signal handler");
3024 0 : exit(1);
3025 : }
3026 : }
3027 :
3028 28618 : Isolate* isolate = Isolate::New(create_params);
3029 28618 : D8Console console(isolate);
3030 : {
3031 : Isolate::Scope scope(isolate);
3032 28618 : Initialize(isolate);
3033 28618 : PerIsolateData data(isolate);
3034 28618 : debug::SetConsoleDelegate(isolate, &console);
3035 :
3036 28618 : if (options.trace_enabled) {
3037 : platform::tracing::TraceConfig* trace_config;
3038 0 : if (options.trace_config) {
3039 0 : int size = 0;
3040 0 : char* trace_config_json_str = ReadChars(options.trace_config, &size);
3041 : trace_config =
3042 0 : tracing::CreateTraceConfigFromJSON(isolate, trace_config_json_str);
3043 0 : delete[] trace_config_json_str;
3044 : } else {
3045 : trace_config =
3046 0 : platform::tracing::TraceConfig::CreateDefaultTraceConfig();
3047 : }
3048 0 : tracing_controller->StartTracing(trace_config);
3049 : }
3050 :
3051 28618 : if (options.stress_opt || options.stress_deopt) {
3052 : Testing::SetStressRunType(options.stress_opt
3053 : ? Testing::kStressTypeOpt
3054 8061 : : Testing::kStressTypeDeopt);
3055 8061 : options.stress_runs = Testing::GetStressRuns();
3056 56419 : for (int i = 0; i < options.stress_runs && result == 0; i++) {
3057 : printf("============ Stress %d/%d ============\n", i + 1,
3058 40297 : options.stress_runs);
3059 40297 : Testing::PrepareStressRun(i);
3060 40297 : bool last_run = i == options.stress_runs - 1;
3061 40297 : result = RunMain(isolate, argc, argv, last_run);
3062 : }
3063 : printf("======== Full Deoptimization =======\n");
3064 8061 : Testing::DeoptimizeAll(isolate);
3065 20557 : } else if (i::FLAG_stress_runs > 0) {
3066 30 : options.stress_runs = i::FLAG_stress_runs;
3067 190 : for (int i = 0; i < options.stress_runs && result == 0; i++) {
3068 : printf("============ Run %d/%d ============\n", i + 1,
3069 130 : options.stress_runs);
3070 130 : bool last_run = i == options.stress_runs - 1;
3071 130 : result = RunMain(isolate, argc, argv, last_run);
3072 : }
3073 : } else {
3074 : bool last_run = true;
3075 20527 : result = RunMain(isolate, argc, argv, last_run);
3076 : }
3077 :
3078 : // Run interactive shell if explicitly requested or if no script has been
3079 : // executed, but never on --test
3080 57236 : if (options.use_interactive_shell()) {
3081 0 : RunShell(isolate);
3082 : }
3083 :
3084 28624 : if (i::FLAG_trace_ignition_dispatches &&
3085 6 : i::FLAG_trace_ignition_dispatches_output_file != nullptr) {
3086 0 : WriteIgnitionDispatchCountersFile(isolate);
3087 : }
3088 :
3089 : // Shut down contexts and collect garbage.
3090 : evaluation_context_.Reset();
3091 : stringify_function_.Reset();
3092 28618 : CollectGarbage(isolate);
3093 : }
3094 28618 : OnExit(isolate);
3095 28618 : V8::Dispose();
3096 28618 : V8::ShutdownPlatform();
3097 28618 : delete g_platform;
3098 : if (i::FLAG_verify_predictable) {
3099 : delete tracing_controller;
3100 : }
3101 :
3102 28618 : return result;
3103 : }
3104 :
3105 : } // namespace v8
3106 :
3107 :
3108 : #ifndef GOOGLE3
3109 28618 : int main(int argc, char* argv[]) {
3110 28618 : return v8::Shell::Main(argc, argv);
3111 85854 : }
3112 : #endif
|