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