Upgrade V8 to version 4.9.385.28
https://chromium.googlesource.com/v8/v8/+/4.9.385.28
FPIIM-449
Change-Id: I4b2e74289d4bf3667f2f3dc8aa2e541f63e26eb4
diff --git a/src/d8.cc b/src/d8.cc
index 7157cb8..c58c172 100644
--- a/src/d8.cc
+++ b/src/d8.cc
@@ -19,6 +19,7 @@
#ifndef V8_SHARED
#include <algorithm>
+#include <vector>
#endif // !V8_SHARED
#ifdef V8_SHARED
@@ -43,9 +44,8 @@
#include "src/base/platform/platform.h"
#include "src/base/sys-info.h"
#include "src/basic-block-profiler.h"
-#include "src/d8-debug.h"
-#include "src/debug.h"
-#include "src/natives.h"
+#include "src/snapshot/natives.h"
+#include "src/utils.h"
#include "src/v8.h"
#endif // !V8_SHARED
@@ -62,14 +62,150 @@
#define DCHECK(condition) assert(condition)
#endif
+#ifndef CHECK
+#define CHECK(condition) assert(condition)
+#endif
+
namespace v8 {
+namespace {
-static Handle<Value> Throw(Isolate* isolate, const char* message) {
- return isolate->ThrowException(String::NewFromUtf8(isolate, message));
+const int MB = 1024 * 1024;
+#ifndef V8_SHARED
+const int kMaxWorkers = 50;
+#endif
+
+
+class ShellArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
+ public:
+ virtual void* Allocate(size_t length) {
+ void* data = AllocateUninitialized(length);
+ return data == NULL ? data : memset(data, 0, length);
+ }
+ virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
+ virtual void Free(void* data, size_t) { free(data); }
+};
+
+
+class MockArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
+ public:
+ void* Allocate(size_t length) override {
+ size_t actual_length = length > 10 * MB ? 1 : length;
+ void* data = AllocateUninitialized(actual_length);
+ return data == NULL ? data : memset(data, 0, actual_length);
+ }
+ void* AllocateUninitialized(size_t length) override {
+ return length > 10 * MB ? malloc(1) : malloc(length);
+ }
+ void Free(void* p, size_t) override { free(p); }
+};
+
+
+#ifndef V8_SHARED
+// Predictable v8::Platform implementation. All background and foreground
+// tasks are run immediately, delayed tasks are not executed at all.
+class PredictablePlatform : public Platform {
+ public:
+ PredictablePlatform() {}
+
+ void CallOnBackgroundThread(Task* task,
+ ExpectedRuntime expected_runtime) override {
+ task->Run();
+ delete task;
+ }
+
+ void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override {
+ task->Run();
+ delete task;
+ }
+
+ void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task,
+ double delay_in_seconds) override {
+ delete task;
+ }
+
+ void CallIdleOnForegroundThread(v8::Isolate* isolate,
+ IdleTask* task) override {
+ UNREACHABLE();
+ }
+
+ bool IdleTasksEnabled(v8::Isolate* isolate) override { return false; }
+
+ double MonotonicallyIncreasingTime() override {
+ return synthetic_time_in_sec_ += 0.00001;
+ }
+
+ uint64_t AddTraceEvent(char phase, const uint8_t* categoryEnabledFlag,
+ const char* name, uint64_t id, uint64_t bind_id,
+ int numArgs, const char** argNames,
+ const uint8_t* argTypes, const uint64_t* argValues,
+ unsigned int flags) override {
+ return 0;
+ }
+
+ void UpdateTraceEventDuration(const uint8_t* categoryEnabledFlag,
+ const char* name, uint64_t handle) override {}
+
+ const uint8_t* GetCategoryGroupEnabled(const char* name) override {
+ static uint8_t no = 0;
+ return &no;
+ }
+
+ const char* GetCategoryGroupName(
+ const uint8_t* categoryEnabledFlag) override {
+ static const char* dummy = "dummy";
+ return dummy;
+ }
+
+ private:
+ double synthetic_time_in_sec_ = 0.0;
+
+ DISALLOW_COPY_AND_ASSIGN(PredictablePlatform);
+};
+#endif // !V8_SHARED
+
+
+v8::Platform* g_platform = NULL;
+
+
+static Local<Value> Throw(Isolate* isolate, const char* message) {
+ return isolate->ThrowException(
+ String::NewFromUtf8(isolate, message, NewStringType::kNormal)
+ .ToLocalChecked());
}
+#ifndef V8_SHARED
+bool FindInObjectList(Local<Object> object, const Shell::ObjectList& list) {
+ for (int i = 0; i < list.length(); ++i) {
+ if (list[i]->StrictEquals(object)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+Worker* GetWorkerFromInternalField(Isolate* isolate, Local<Object> object) {
+ if (object->InternalFieldCount() != 1) {
+ Throw(isolate, "this is not a Worker");
+ return NULL;
+ }
+
+ Worker* worker =
+ static_cast<Worker*>(object->GetAlignedPointerFromInternalField(0));
+ if (worker == NULL) {
+ Throw(isolate, "Worker is defunct because main thread is terminating");
+ return NULL;
+ }
+
+ return worker;
+}
+#endif // !V8_SHARED
+
+
+} // namespace
+
class PerIsolateData {
public:
@@ -101,64 +237,36 @@
int realm_count_;
int realm_current_;
int realm_switch_;
- Persistent<Context>* realms_;
- Persistent<Value> realm_shared_;
+ Global<Context>* realms_;
+ Global<Value> realm_shared_;
int RealmIndexOrThrow(const v8::FunctionCallbackInfo<v8::Value>& args,
int arg_offset);
- int RealmFind(Handle<Context> context);
+ int RealmFind(Local<Context> context);
};
-LineEditor *LineEditor::current_ = NULL;
-
-
-LineEditor::LineEditor(Type type, const char* name)
- : type_(type), name_(name) {
- if (current_ == NULL || current_->type_ < type) current_ = this;
-}
-
-
-class DumbLineEditor: public LineEditor {
- public:
- explicit DumbLineEditor(Isolate* isolate)
- : LineEditor(LineEditor::DUMB, "dumb"), isolate_(isolate) { }
- virtual Handle<String> Prompt(const char* prompt);
- private:
- Isolate* isolate_;
-};
-
-
-Handle<String> DumbLineEditor::Prompt(const char* prompt) {
- printf("%s", prompt);
-#if defined(__native_client__)
- // Native Client libc is used to being embedded in Chrome and
- // has trouble recognizing when to flush.
- fflush(stdout);
-#endif
- return Shell::ReadFromStdin(isolate_);
-}
-
-
#ifndef V8_SHARED
CounterMap* Shell::counter_map_;
base::OS::MemoryMappedFile* Shell::counters_file_ = NULL;
CounterCollection Shell::local_counters_;
CounterCollection* Shell::counters_ = &local_counters_;
-base::Mutex Shell::context_mutex_;
+base::LazyMutex Shell::context_mutex_;
const base::TimeTicks Shell::kInitialTicks =
base::TimeTicks::HighResolutionNow();
-Persistent<Context> Shell::utility_context_;
+Global<Context> Shell::utility_context_;
+base::LazyMutex Shell::workers_mutex_;
+bool Shell::allow_new_workers_ = true;
+i::List<Worker*> Shell::workers_;
+i::List<SharedArrayBuffer::Contents> Shell::externalized_shared_contents_;
#endif // !V8_SHARED
-Persistent<Context> Shell::evaluation_context_;
+Global<Context> Shell::evaluation_context_;
+ArrayBuffer::Allocator* Shell::array_buffer_allocator;
ShellOptions Shell::options;
-const char* Shell::kPrompt = "d8> ";
-
+base::OnceType Shell::quit_once_ = V8_ONCE_INIT;
#ifndef V8_SHARED
-const int MB = 1024 * 1024;
-
bool CounterMap::Match(void* key1, void* key2) {
const char* name1 = reinterpret_cast<const char*>(key1);
const char* name2 = reinterpret_cast<const char*>(key2);
@@ -187,25 +295,30 @@
name_buffer = new uint16_t[name_length];
name_string->Write(name_buffer, 0, name_length);
}
- Isolate* temp_isolate = Isolate::New();
+ Isolate::CreateParams create_params;
+ create_params.array_buffer_allocator = Shell::array_buffer_allocator;
+ Isolate* temp_isolate = Isolate::New(create_params);
ScriptCompiler::CachedData* result = NULL;
{
Isolate::Scope isolate_scope(temp_isolate);
HandleScope handle_scope(temp_isolate);
Context::Scope context_scope(Context::New(temp_isolate));
- Local<String> source_copy = v8::String::NewFromTwoByte(
- temp_isolate, source_buffer, v8::String::kNormalString, source_length);
+ Local<String> source_copy =
+ v8::String::NewFromTwoByte(temp_isolate, source_buffer,
+ v8::NewStringType::kNormal,
+ source_length).ToLocalChecked();
Local<Value> name_copy;
if (name_buffer) {
- name_copy = v8::String::NewFromTwoByte(
- temp_isolate, name_buffer, v8::String::kNormalString, name_length);
+ name_copy = v8::String::NewFromTwoByte(temp_isolate, name_buffer,
+ v8::NewStringType::kNormal,
+ name_length).ToLocalChecked();
} else {
name_copy = v8::Undefined(temp_isolate);
}
ScriptCompiler::Source script_source(source_copy, ScriptOrigin(name_copy));
- ScriptCompiler::CompileUnbound(temp_isolate, &script_source,
- compile_options);
- if (script_source.GetCachedData()) {
+ if (!ScriptCompiler::CompileUnboundScript(temp_isolate, &script_source,
+ compile_options).IsEmpty() &&
+ script_source.GetCachedData()) {
int length = script_source.GetCachedData()->length;
uint8_t* cache = new uint8_t[length];
memcpy(cache, script_source.GetCachedData()->data, length);
@@ -221,14 +334,18 @@
// Compile a string within the current v8 context.
-Local<UnboundScript> Shell::CompileString(
+MaybeLocal<Script> Shell::CompileString(
Isolate* isolate, Local<String> source, Local<Value> name,
- ScriptCompiler::CompileOptions compile_options) {
+ ScriptCompiler::CompileOptions compile_options, SourceType source_type) {
+ Local<Context> context(isolate->GetCurrentContext());
ScriptOrigin origin(name);
if (compile_options == ScriptCompiler::kNoCompileOptions) {
ScriptCompiler::Source script_source(source, origin);
- return ScriptCompiler::CompileUnbound(isolate, &script_source,
- compile_options);
+ return source_type == SCRIPT
+ ? ScriptCompiler::Compile(context, &script_source,
+ compile_options)
+ : ScriptCompiler::CompileModule(context, &script_source,
+ compile_options);
}
ScriptCompiler::CachedData* data =
@@ -242,85 +359,85 @@
DCHECK(false); // A new compile option?
}
if (data == NULL) compile_options = ScriptCompiler::kNoCompileOptions;
- return ScriptCompiler::CompileUnbound(isolate, &cached_source,
- compile_options);
+ MaybeLocal<Script> result =
+ source_type == SCRIPT
+ ? ScriptCompiler::Compile(context, &cached_source, compile_options)
+ : ScriptCompiler::CompileModule(context, &cached_source,
+ compile_options);
+ CHECK(data == NULL || !data->rejected);
+ return result;
}
// Executes a string within the current v8 context.
-bool Shell::ExecuteString(Isolate* isolate,
- Handle<String> source,
- Handle<Value> name,
- bool print_result,
- bool report_exceptions) {
-#ifndef V8_SHARED
- bool FLAG_debugger = i::FLAG_debugger;
-#else
- bool FLAG_debugger = false;
-#endif // !V8_SHARED
+bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
+ Local<Value> name, bool print_result,
+ bool report_exceptions, SourceType source_type) {
HandleScope handle_scope(isolate);
- TryCatch try_catch;
- options.script_executed = true;
- if (FLAG_debugger) {
- // When debugging make exceptions appear to be uncaught.
- try_catch.SetVerbose(true);
- }
+ TryCatch try_catch(isolate);
- Handle<UnboundScript> script =
- Shell::CompileString(isolate, source, name, options.compile_options);
- if (script.IsEmpty()) {
- // Print errors that happened during compilation.
- if (report_exceptions && !FLAG_debugger)
- ReportException(isolate, &try_catch);
- return false;
- } else {
+ MaybeLocal<Value> maybe_result;
+ {
PerIsolateData* data = PerIsolateData::Get(isolate);
Local<Context> realm =
Local<Context>::New(isolate, data->realms_[data->realm_current_]);
- realm->Enter();
- Handle<Value> result = script->BindToCurrentContext()->Run();
- realm->Exit();
- data->realm_current_ = data->realm_switch_;
- if (result.IsEmpty()) {
- DCHECK(try_catch.HasCaught());
- // Print errors that happened during execution.
- if (report_exceptions && !FLAG_debugger)
- ReportException(isolate, &try_catch);
+ Context::Scope context_scope(realm);
+ Local<Script> script;
+ if (!Shell::CompileString(isolate, source, name, options.compile_options,
+ source_type).ToLocal(&script)) {
+ // Print errors that happened during compilation.
+ if (report_exceptions) ReportException(isolate, &try_catch);
return false;
- } else {
- DCHECK(!try_catch.HasCaught());
- if (print_result) {
-#if !defined(V8_SHARED)
- if (options.test_shell) {
-#endif
- if (!result->IsUndefined()) {
- // If all went well and the result wasn't undefined then print
- // the returned value.
- v8::String::Utf8Value str(result);
- fwrite(*str, sizeof(**str), str.length(), stdout);
- printf("\n");
- }
-#if !defined(V8_SHARED)
- } else {
- v8::TryCatch try_catch;
- v8::Local<v8::Context> context =
- v8::Local<v8::Context>::New(isolate, utility_context_);
- v8::Context::Scope context_scope(context);
- Handle<Object> global = context->Global();
- Handle<Value> fun =
- global->Get(String::NewFromUtf8(isolate, "Stringify"));
- Handle<Value> argv[1] = { result };
- Handle<Value> s = Handle<Function>::Cast(fun)->Call(global, 1, argv);
- if (try_catch.HasCaught()) return true;
- v8::String::Utf8Value str(s);
- fwrite(*str, sizeof(**str), str.length(), stdout);
- printf("\n");
- }
-#endif
- }
- return true;
}
+ maybe_result = script->Run(realm);
+ EmptyMessageQueues(isolate);
+ data->realm_current_ = data->realm_switch_;
}
+ Local<Value> result;
+ if (!maybe_result.ToLocal(&result)) {
+ DCHECK(try_catch.HasCaught());
+ // Print errors that happened during execution.
+ if (report_exceptions) ReportException(isolate, &try_catch);
+ return false;
+ }
+ DCHECK(!try_catch.HasCaught());
+ if (print_result) {
+#if !defined(V8_SHARED)
+ if (options.test_shell) {
+#endif
+ if (!result->IsUndefined()) {
+ // If all went well and the result wasn't undefined then print
+ // the returned value.
+ v8::String::Utf8Value str(result);
+ fwrite(*str, sizeof(**str), str.length(), stdout);
+ printf("\n");
+ }
+#if !defined(V8_SHARED)
+ } else {
+ v8::TryCatch try_catch(isolate);
+ v8::Local<v8::Context> context =
+ v8::Local<v8::Context>::New(isolate, utility_context_);
+ v8::Context::Scope context_scope(context);
+ Local<Object> global = context->Global();
+ Local<Value> fun =
+ global->Get(context, String::NewFromUtf8(isolate, "Stringify",
+ v8::NewStringType::kNormal)
+ .ToLocalChecked()).ToLocalChecked();
+ Local<Value> argv[1] = {result};
+ Local<Value> s;
+ if (!Local<Function>::Cast(fun)
+ ->Call(context, global, 1, argv)
+ .ToLocal(&s)) {
+ return true;
+ }
+ DCHECK(!try_catch.HasCaught());
+ v8::String::Utf8Value str(s);
+ fwrite(*str, sizeof(**str), str.length(), stdout);
+ printf("\n");
+ }
+#endif
+ }
+ return true;
}
@@ -328,7 +445,7 @@
data_->realm_count_ = 1;
data_->realm_current_ = 0;
data_->realm_switch_ = 0;
- data_->realms_ = new Persistent<Context>[1];
+ data_->realms_ = new Global<Context>[1];
data_->realms_[0].Reset(data_->isolate_,
data_->isolate_->GetEnteredContext());
}
@@ -344,7 +461,7 @@
}
-int PerIsolateData::RealmFind(Handle<Context> context) {
+int PerIsolateData::RealmFind(Local<Context> context) {
for (int i = 0; i < realm_count_; ++i) {
if (realms_[i] == context) return i;
}
@@ -359,10 +476,10 @@
Throw(args.GetIsolate(), "Invalid argument");
return -1;
}
- int index = args[arg_offset]->Int32Value();
- if (index < 0 ||
- index >= realm_count_ ||
- realms_[index].IsEmpty()) {
+ int index = args[arg_offset]
+ ->Int32Value(args.GetIsolate()->GetCurrentContext())
+ .FromMaybe(-1);
+ if (index < 0 || index >= realm_count_ || realms_[index].IsEmpty()) {
Throw(args.GetIsolate(), "Invalid realm index");
return -1;
}
@@ -372,13 +489,11 @@
#ifndef V8_SHARED
// performance.now() returns a time stamp as double, measured in milliseconds.
-// When FLAG_verify_predictable mode is enabled it returns current value
-// of Heap::allocations_count().
+// When FLAG_verify_predictable mode is enabled it returns result of
+// v8::Platform::MonotonicallyIncreasingTime().
void Shell::PerformanceNow(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (i::FLAG_verify_predictable) {
- Isolate* v8_isolate = args.GetIsolate();
- i::Heap* heap = reinterpret_cast<i::Isolate*>(v8_isolate)->heap();
- args.GetReturnValue().Set(heap->synthetic_time());
+ args.GetReturnValue().Set(g_platform->MonotonicallyIncreasingTime());
} else {
base::TimeDelta delta =
base::TimeTicks::HighResolutionNow() - kInitialTicks;
@@ -406,7 +521,10 @@
Throw(args.GetIsolate(), "Invalid argument");
return;
}
- int index = data->RealmFind(args[0]->ToObject(isolate)->CreationContext());
+ int index = data->RealmFind(args[0]
+ ->ToObject(isolate->GetCurrentContext())
+ .ToLocalChecked()
+ ->CreationContext());
if (index == -1) return;
args.GetReturnValue().Set(index);
}
@@ -426,17 +544,24 @@
// Realm.create() creates a new realm and returns its index.
void Shell::RealmCreate(const v8::FunctionCallbackInfo<v8::Value>& args) {
Isolate* isolate = args.GetIsolate();
+ TryCatch try_catch(isolate);
PerIsolateData* data = PerIsolateData::Get(isolate);
- Persistent<Context>* old_realms = data->realms_;
+ Global<Context>* old_realms = data->realms_;
int index = data->realm_count_;
- data->realms_ = new Persistent<Context>[++data->realm_count_];
+ data->realms_ = new Global<Context>[++data->realm_count_];
for (int i = 0; i < index; ++i) {
data->realms_[i].Reset(isolate, old_realms[i]);
+ old_realms[i].Reset();
}
delete[] old_realms;
- Handle<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
- data->realms_[index].Reset(
- isolate, Context::New(isolate, NULL, global_template));
+ Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
+ Local<Context> context = Context::New(isolate, NULL, global_template);
+ if (context.IsEmpty()) {
+ DCHECK(try_catch.HasCaught());
+ try_catch.ReThrow();
+ return;
+ }
+ data->realms_[index].Reset(isolate, context);
args.GetReturnValue().Set(index);
}
@@ -453,6 +578,8 @@
return;
}
data->realms_[index].Reset();
+ isolate->ContextDisposedNotification();
+ isolate->IdleNotificationDeadline(g_platform->MonotonicallyIncreasingTime());
}
@@ -476,13 +603,20 @@
Throw(args.GetIsolate(), "Invalid argument");
return;
}
- ScriptCompiler::Source script_source(args[1]->ToString(isolate));
- Handle<UnboundScript> script = ScriptCompiler::CompileUnbound(
- isolate, &script_source);
- if (script.IsEmpty()) return;
+ ScriptCompiler::Source script_source(
+ args[1]->ToString(isolate->GetCurrentContext()).ToLocalChecked());
+ Local<UnboundScript> script;
+ if (!ScriptCompiler::CompileUnboundScript(isolate, &script_source)
+ .ToLocal(&script)) {
+ return;
+ }
Local<Context> realm = Local<Context>::New(isolate, data->realms_[index]);
realm->Enter();
- Handle<Value> result = script->BindToCurrentContext()->Run();
+ Local<Value> result;
+ if (!script->BindToCurrentContext()->Run(realm).ToLocal(&result)) {
+ realm->Exit();
+ return;
+ }
realm->Exit();
args.GetReturnValue().Set(result);
}
@@ -521,9 +655,15 @@
}
// Explicitly catch potential exceptions in toString().
- v8::TryCatch try_catch;
- Handle<String> str_obj = args[i]->ToString(args.GetIsolate());
- if (try_catch.HasCaught()) {
+ v8::TryCatch try_catch(args.GetIsolate());
+ Local<Value> arg = args[i];
+ Local<String> str_obj;
+
+ if (arg->IsSymbol()) {
+ arg = Local<Symbol>::Cast(arg)->Name();
+ }
+ if (!arg->ToString(args.GetIsolate()->GetCurrentContext())
+ .ToLocal(&str_obj)) {
try_catch.ReThrow();
return;
}
@@ -544,7 +684,7 @@
Throw(args.GetIsolate(), "Error loading file");
return;
}
- Handle<String> source = ReadFile(args.GetIsolate(), *file);
+ Local<String> source = ReadFile(args.GetIsolate(), *file);
if (source.IsEmpty()) {
Throw(args.GetIsolate(), "Error loading file");
return;
@@ -553,10 +693,11 @@
}
-Handle<String> Shell::ReadFromStdin(Isolate* isolate) {
+Local<String> Shell::ReadFromStdin(Isolate* isolate) {
static const int kBufferSize = 256;
char buffer[kBufferSize];
- Handle<String> accumulator = String::NewFromUtf8(isolate, "");
+ Local<String> accumulator =
+ String::NewFromUtf8(isolate, "", NewStringType::kNormal).ToLocalChecked();
int length;
while (true) {
// Continue reading if the line ends with an escape '\\' or the line has
@@ -564,23 +705,26 @@
// If fgets gets an error, just give up.
char* input = NULL;
input = fgets(buffer, kBufferSize, stdin);
- if (input == NULL) return Handle<String>();
+ if (input == NULL) return Local<String>();
length = static_cast<int>(strlen(buffer));
if (length == 0) {
return accumulator;
} else if (buffer[length-1] != '\n') {
accumulator = String::Concat(
accumulator,
- String::NewFromUtf8(isolate, buffer, String::kNormalString, length));
+ String::NewFromUtf8(isolate, buffer, NewStringType::kNormal, length)
+ .ToLocalChecked());
} else if (length > 1 && buffer[length-2] == '\\') {
buffer[length-2] = '\n';
accumulator = String::Concat(
- accumulator, String::NewFromUtf8(isolate, buffer,
- String::kNormalString, length - 1));
+ accumulator,
+ String::NewFromUtf8(isolate, buffer, NewStringType::kNormal,
+ length - 1).ToLocalChecked());
} else {
return String::Concat(
- accumulator, String::NewFromUtf8(isolate, buffer,
- String::kNormalString, length - 1));
+ accumulator,
+ String::NewFromUtf8(isolate, buffer, NewStringType::kNormal,
+ length - 1).ToLocalChecked());
}
}
}
@@ -594,16 +738,16 @@
Throw(args.GetIsolate(), "Error loading file");
return;
}
- Handle<String> source = ReadFile(args.GetIsolate(), *file);
+ Local<String> source = ReadFile(args.GetIsolate(), *file);
if (source.IsEmpty()) {
Throw(args.GetIsolate(), "Error loading file");
return;
}
- if (!ExecuteString(args.GetIsolate(),
- source,
- String::NewFromUtf8(args.GetIsolate(), *file),
- false,
- true)) {
+ if (!ExecuteString(
+ args.GetIsolate(), source,
+ String::NewFromUtf8(args.GetIsolate(), *file,
+ NewStringType::kNormal).ToLocalChecked(),
+ false, true)) {
Throw(args.GetIsolate(), "Error executing file");
return;
}
@@ -611,23 +755,160 @@
}
+#ifndef V8_SHARED
+void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ Isolate* isolate = args.GetIsolate();
+ HandleScope handle_scope(isolate);
+ if (args.Length() < 1 || !args[0]->IsString()) {
+ Throw(args.GetIsolate(), "1st argument must be string");
+ return;
+ }
+
+ if (!args.IsConstructCall()) {
+ Throw(args.GetIsolate(), "Worker must be constructed with new");
+ return;
+ }
+
+ {
+ base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer());
+ if (workers_.length() >= kMaxWorkers) {
+ Throw(args.GetIsolate(), "Too many workers, I won't let you create more");
+ return;
+ }
+
+ // Initialize the internal field to NULL; if we return early without
+ // creating a new Worker (because the main thread is terminating) we can
+ // early-out from the instance calls.
+ args.Holder()->SetAlignedPointerInInternalField(0, NULL);
+
+ if (!allow_new_workers_) return;
+
+ Worker* worker = new Worker;
+ args.Holder()->SetAlignedPointerInInternalField(0, worker);
+ workers_.Add(worker);
+
+ String::Utf8Value script(args[0]);
+ if (!*script) {
+ Throw(args.GetIsolate(), "Can't get worker script");
+ return;
+ }
+ worker->StartExecuteInThread(*script);
+ }
+}
+
+
+void Shell::WorkerPostMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ Isolate* isolate = args.GetIsolate();
+ HandleScope handle_scope(isolate);
+ Local<Context> context = isolate->GetCurrentContext();
+
+ if (args.Length() < 1) {
+ Throw(isolate, "Invalid argument");
+ return;
+ }
+
+ Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
+ if (!worker) {
+ return;
+ }
+
+ Local<Value> message = args[0];
+ ObjectList to_transfer;
+ if (args.Length() >= 2) {
+ if (!args[1]->IsArray()) {
+ Throw(isolate, "Transfer list must be an Array");
+ return;
+ }
+
+ Local<Array> transfer = Local<Array>::Cast(args[1]);
+ uint32_t length = transfer->Length();
+ for (uint32_t i = 0; i < length; ++i) {
+ Local<Value> element;
+ if (transfer->Get(context, i).ToLocal(&element)) {
+ if (!element->IsArrayBuffer() && !element->IsSharedArrayBuffer()) {
+ Throw(isolate,
+ "Transfer array elements must be an ArrayBuffer or "
+ "SharedArrayBuffer.");
+ break;
+ }
+
+ to_transfer.Add(Local<Object>::Cast(element));
+ }
+ }
+ }
+
+ ObjectList seen_objects;
+ SerializationData* data = new SerializationData;
+ if (SerializeValue(isolate, message, to_transfer, &seen_objects, data)) {
+ worker->PostMessage(data);
+ } else {
+ delete data;
+ }
+}
+
+
+void Shell::WorkerGetMessage(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ Isolate* isolate = args.GetIsolate();
+ HandleScope handle_scope(isolate);
+ Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
+ if (!worker) {
+ return;
+ }
+
+ SerializationData* data = worker->GetMessage();
+ if (data) {
+ int offset = 0;
+ Local<Value> data_value;
+ if (Shell::DeserializeValue(isolate, *data, &offset).ToLocal(&data_value)) {
+ args.GetReturnValue().Set(data_value);
+ }
+ delete data;
+ }
+}
+
+
+void Shell::WorkerTerminate(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ Isolate* isolate = args.GetIsolate();
+ HandleScope handle_scope(isolate);
+ Worker* worker = GetWorkerFromInternalField(isolate, args.Holder());
+ if (!worker) {
+ return;
+ }
+
+ worker->Terminate();
+}
+#endif // !V8_SHARED
+
+
+void Shell::QuitOnce(v8::FunctionCallbackInfo<v8::Value>* args) {
+ int exit_code = (*args)[0]
+ ->Int32Value(args->GetIsolate()->GetCurrentContext())
+ .FromMaybe(0);
+#ifndef V8_SHARED
+ CleanupWorkers();
+#endif // !V8_SHARED
+ OnExit(args->GetIsolate());
+ Exit(exit_code);
+}
+
+
void Shell::Quit(const v8::FunctionCallbackInfo<v8::Value>& args) {
- int exit_code = args[0]->Int32Value();
- OnExit(args.GetIsolate());
- exit(exit_code);
+ base::CallOnce(&quit_once_, &QuitOnce,
+ const_cast<v8::FunctionCallbackInfo<v8::Value>*>(&args));
}
void Shell::Version(const v8::FunctionCallbackInfo<v8::Value>& args) {
args.GetReturnValue().Set(
- String::NewFromUtf8(args.GetIsolate(), V8::GetVersion()));
+ String::NewFromUtf8(args.GetIsolate(), V8::GetVersion(),
+ NewStringType::kNormal).ToLocalChecked());
}
void Shell::ReportException(Isolate* isolate, v8::TryCatch* try_catch) {
HandleScope handle_scope(isolate);
#ifndef V8_SHARED
- Handle<Context> utility_context;
+ Local<Context> utility_context;
bool enter_context = !isolate->InContext();
if (enter_context) {
utility_context = Local<Context>::New(isolate, utility_context_);
@@ -636,7 +917,7 @@
#endif // !V8_SHARED
v8::String::Utf8Value exception(try_catch->Exception());
const char* exception_string = ToCString(exception);
- Handle<Message> message = try_catch->Message();
+ Local<Message> message = try_catch->Message();
if (message.IsEmpty()) {
// V8 didn't provide any extra information about this error; just
// print the exception.
@@ -645,26 +926,32 @@
// Print (filename):(line number): (message).
v8::String::Utf8Value filename(message->GetScriptOrigin().ResourceName());
const char* filename_string = ToCString(filename);
- int linenum = message->GetLineNumber();
+ int linenum =
+ message->GetLineNumber(isolate->GetCurrentContext()).FromJust();
printf("%s:%i: %s\n", filename_string, linenum, exception_string);
// Print line of source code.
- v8::String::Utf8Value sourceline(message->GetSourceLine());
+ v8::String::Utf8Value sourceline(
+ message->GetSourceLine(isolate->GetCurrentContext()).ToLocalChecked());
const char* sourceline_string = ToCString(sourceline);
printf("%s\n", sourceline_string);
// Print wavy underline (GetUnderline is deprecated).
- int start = message->GetStartColumn();
+ int start =
+ message->GetStartColumn(isolate->GetCurrentContext()).FromJust();
for (int i = 0; i < start; i++) {
printf(" ");
}
- int end = message->GetEndColumn();
+ int end = message->GetEndColumn(isolate->GetCurrentContext()).FromJust();
for (int i = start; i < end; i++) {
printf("^");
}
printf("\n");
- v8::String::Utf8Value stack_trace(try_catch->StackTrace());
- if (stack_trace.length() > 0) {
- const char* stack_trace_string = ToCString(stack_trace);
- printf("%s\n", stack_trace_string);
+ Local<Value> stack_trace_string;
+ if (try_catch->StackTrace(isolate->GetCurrentContext())
+ .ToLocal(&stack_trace_string) &&
+ stack_trace_string->IsString()) {
+ v8::String::Utf8Value stack_trace(
+ Local<String>::Cast(stack_trace_string));
+ printf("%s\n", ToCString(stack_trace));
}
}
printf("\n");
@@ -675,57 +962,6 @@
#ifndef V8_SHARED
-Handle<Array> Shell::GetCompletions(Isolate* isolate,
- Handle<String> text,
- Handle<String> full) {
- EscapableHandleScope handle_scope(isolate);
- v8::Local<v8::Context> utility_context =
- v8::Local<v8::Context>::New(isolate, utility_context_);
- v8::Context::Scope context_scope(utility_context);
- Handle<Object> global = utility_context->Global();
- Local<Value> fun =
- global->Get(String::NewFromUtf8(isolate, "GetCompletions"));
- static const int kArgc = 3;
- v8::Local<v8::Context> evaluation_context =
- v8::Local<v8::Context>::New(isolate, evaluation_context_);
- Handle<Value> argv[kArgc] = { evaluation_context->Global(), text, full };
- Local<Value> val = Local<Function>::Cast(fun)->Call(global, kArgc, argv);
- return handle_scope.Escape(Local<Array>::Cast(val));
-}
-
-
-Local<Object> Shell::DebugMessageDetails(Isolate* isolate,
- Handle<String> message) {
- EscapableHandleScope handle_scope(isolate);
- v8::Local<v8::Context> context =
- v8::Local<v8::Context>::New(isolate, utility_context_);
- v8::Context::Scope context_scope(context);
- Handle<Object> global = context->Global();
- Handle<Value> fun =
- global->Get(String::NewFromUtf8(isolate, "DebugMessageDetails"));
- static const int kArgc = 1;
- Handle<Value> argv[kArgc] = { message };
- Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv);
- return handle_scope.Escape(Local<Object>(Handle<Object>::Cast(val)));
-}
-
-
-Local<Value> Shell::DebugCommandToJSONRequest(Isolate* isolate,
- Handle<String> command) {
- EscapableHandleScope handle_scope(isolate);
- v8::Local<v8::Context> context =
- v8::Local<v8::Context>::New(isolate, utility_context_);
- v8::Context::Scope context_scope(context);
- Handle<Object> global = context->Global();
- Handle<Value> fun =
- global->Get(String::NewFromUtf8(isolate, "DebugCommandToJSONRequest"));
- static const int kArgc = 1;
- Handle<Value> argv[kArgc] = { command };
- Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv);
- return handle_scope.Escape(Local<Value>(val));
-}
-
-
int32_t* Counter::Bind(const char* name, bool is_histogram) {
int i;
for (i = 0; i < kMaxNameSize - 1 && name[i]; i++)
@@ -824,10 +1060,25 @@
}
+class NoUseStrongForUtilityScriptScope {
+ public:
+ NoUseStrongForUtilityScriptScope() : flag_(i::FLAG_use_strong) {
+ i::FLAG_use_strong = false;
+ }
+ ~NoUseStrongForUtilityScriptScope() { i::FLAG_use_strong = flag_; }
+
+ private:
+ bool flag_;
+};
+
+
void Shell::InstallUtilityScript(Isolate* isolate) {
+ NoUseStrongForUtilityScriptScope no_use_strong;
HandleScope scope(isolate);
// If we use the utility context, we have to set the security tokens so that
// utility, evaluation and debug context can all access each other.
+ Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
+ utility_context_.Reset(isolate, Context::New(isolate, NULL, global_template));
v8::Local<v8::Context> utility_context =
v8::Local<v8::Context>::New(isolate, utility_context_);
v8::Local<v8::Context> evaluation_context =
@@ -836,33 +1087,23 @@
evaluation_context->SetSecurityToken(Undefined(isolate));
v8::Context::Scope context_scope(utility_context);
- if (i::FLAG_debugger) printf("JavaScript debugger enabled\n");
- // Install the debugger object in the utility scope
- i::Debug* debug = reinterpret_cast<i::Isolate*>(isolate)->debug();
- debug->Load();
- i::Handle<i::Context> debug_context = debug->debug_context();
- i::Handle<i::JSObject> js_debug
- = i::Handle<i::JSObject>(debug_context->global_object());
- utility_context->Global()->Set(String::NewFromUtf8(isolate, "$debug"),
- Utils::ToLocal(js_debug));
- debug_context->set_security_token(
- reinterpret_cast<i::Isolate*>(isolate)->heap()->undefined_value());
-
// Run the d8 shell utility script in the utility context
int source_index = i::NativesCollection<i::D8>::GetIndex("d8");
i::Vector<const char> shell_source =
i::NativesCollection<i::D8>::GetScriptSource(source_index);
i::Vector<const char> shell_source_name =
i::NativesCollection<i::D8>::GetScriptName(source_index);
- Handle<String> source =
- String::NewFromUtf8(isolate, shell_source.start(), String::kNormalString,
- shell_source.length());
- Handle<String> name =
+ Local<String> source =
+ String::NewFromUtf8(isolate, shell_source.start(), NewStringType::kNormal,
+ shell_source.length()).ToLocalChecked();
+ Local<String> name =
String::NewFromUtf8(isolate, shell_source_name.start(),
- String::kNormalString, shell_source_name.length());
+ NewStringType::kNormal,
+ shell_source_name.length()).ToLocalChecked();
ScriptOrigin origin(name);
- Handle<Script> script = Script::Compile(source, &origin);
- script->Run();
+ Local<Script> script =
+ Script::Compile(utility_context, source, &origin).ToLocalChecked();
+ script->Run(utility_context).ToLocalChecked();
// Mark the d8 shell script as native to avoid it showing up as normal source
// in the debugger.
i::Handle<i::Object> compiled_script = Utils::OpenHandle(*script);
@@ -871,64 +1112,137 @@
i::JSFunction::cast(*compiled_script)->shared()->script()))
: i::Handle<i::Script>(i::Script::cast(
i::SharedFunctionInfo::cast(*compiled_script)->script()));
- script_object->set_type(i::Smi::FromInt(i::Script::TYPE_NATIVE));
-
- // Start the in-process debugger if requested.
- if (i::FLAG_debugger) v8::Debug::SetDebugEventListener(HandleDebugEvent);
+ script_object->set_type(i::Script::TYPE_EXTENSION);
}
#endif // !V8_SHARED
-Handle<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
- Handle<ObjectTemplate> global_template = ObjectTemplate::New(isolate);
- global_template->Set(String::NewFromUtf8(isolate, "print"),
- FunctionTemplate::New(isolate, Print));
- global_template->Set(String::NewFromUtf8(isolate, "write"),
- FunctionTemplate::New(isolate, Write));
- global_template->Set(String::NewFromUtf8(isolate, "read"),
- FunctionTemplate::New(isolate, Read));
- global_template->Set(String::NewFromUtf8(isolate, "readbuffer"),
- FunctionTemplate::New(isolate, ReadBuffer));
- global_template->Set(String::NewFromUtf8(isolate, "readline"),
- FunctionTemplate::New(isolate, ReadLine));
- global_template->Set(String::NewFromUtf8(isolate, "load"),
- FunctionTemplate::New(isolate, Load));
- global_template->Set(String::NewFromUtf8(isolate, "quit"),
- FunctionTemplate::New(isolate, Quit));
- global_template->Set(String::NewFromUtf8(isolate, "version"),
- FunctionTemplate::New(isolate, Version));
+Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
+ Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate);
+ global_template->Set(
+ String::NewFromUtf8(isolate, "print", NewStringType::kNormal)
+ .ToLocalChecked(),
+ FunctionTemplate::New(isolate, Print));
+ global_template->Set(
+ String::NewFromUtf8(isolate, "write", NewStringType::kNormal)
+ .ToLocalChecked(),
+ FunctionTemplate::New(isolate, Write));
+ global_template->Set(
+ String::NewFromUtf8(isolate, "read", NewStringType::kNormal)
+ .ToLocalChecked(),
+ FunctionTemplate::New(isolate, Read));
+ global_template->Set(
+ String::NewFromUtf8(isolate, "readbuffer", NewStringType::kNormal)
+ .ToLocalChecked(),
+ FunctionTemplate::New(isolate, ReadBuffer));
+ global_template->Set(
+ String::NewFromUtf8(isolate, "readline", NewStringType::kNormal)
+ .ToLocalChecked(),
+ FunctionTemplate::New(isolate, ReadLine));
+ global_template->Set(
+ String::NewFromUtf8(isolate, "load", NewStringType::kNormal)
+ .ToLocalChecked(),
+ FunctionTemplate::New(isolate, Load));
+ // Some Emscripten-generated code tries to call 'quit', which in turn would
+ // call C's exit(). This would lead to memory leaks, because there is no way
+ // we can terminate cleanly then, so we need a way to hide 'quit'.
+ if (!options.omit_quit) {
+ global_template->Set(
+ String::NewFromUtf8(isolate, "quit", NewStringType::kNormal)
+ .ToLocalChecked(),
+ FunctionTemplate::New(isolate, Quit));
+ }
+ global_template->Set(
+ String::NewFromUtf8(isolate, "version", NewStringType::kNormal)
+ .ToLocalChecked(),
+ FunctionTemplate::New(isolate, Version));
// Bind the Realm object.
- Handle<ObjectTemplate> realm_template = ObjectTemplate::New(isolate);
- realm_template->Set(String::NewFromUtf8(isolate, "current"),
- FunctionTemplate::New(isolate, RealmCurrent));
- realm_template->Set(String::NewFromUtf8(isolate, "owner"),
- FunctionTemplate::New(isolate, RealmOwner));
- realm_template->Set(String::NewFromUtf8(isolate, "global"),
- FunctionTemplate::New(isolate, RealmGlobal));
- realm_template->Set(String::NewFromUtf8(isolate, "create"),
- FunctionTemplate::New(isolate, RealmCreate));
- realm_template->Set(String::NewFromUtf8(isolate, "dispose"),
- FunctionTemplate::New(isolate, RealmDispose));
- realm_template->Set(String::NewFromUtf8(isolate, "switch"),
- FunctionTemplate::New(isolate, RealmSwitch));
- realm_template->Set(String::NewFromUtf8(isolate, "eval"),
- FunctionTemplate::New(isolate, RealmEval));
- realm_template->SetAccessor(String::NewFromUtf8(isolate, "shared"),
- RealmSharedGet, RealmSharedSet);
- global_template->Set(String::NewFromUtf8(isolate, "Realm"), realm_template);
+ Local<ObjectTemplate> realm_template = ObjectTemplate::New(isolate);
+ realm_template->Set(
+ String::NewFromUtf8(isolate, "current", NewStringType::kNormal)
+ .ToLocalChecked(),
+ FunctionTemplate::New(isolate, RealmCurrent));
+ realm_template->Set(
+ String::NewFromUtf8(isolate, "owner", NewStringType::kNormal)
+ .ToLocalChecked(),
+ FunctionTemplate::New(isolate, RealmOwner));
+ realm_template->Set(
+ String::NewFromUtf8(isolate, "global", NewStringType::kNormal)
+ .ToLocalChecked(),
+ FunctionTemplate::New(isolate, RealmGlobal));
+ realm_template->Set(
+ String::NewFromUtf8(isolate, "create", NewStringType::kNormal)
+ .ToLocalChecked(),
+ FunctionTemplate::New(isolate, RealmCreate));
+ realm_template->Set(
+ String::NewFromUtf8(isolate, "dispose", NewStringType::kNormal)
+ .ToLocalChecked(),
+ FunctionTemplate::New(isolate, RealmDispose));
+ realm_template->Set(
+ String::NewFromUtf8(isolate, "switch", NewStringType::kNormal)
+ .ToLocalChecked(),
+ FunctionTemplate::New(isolate, RealmSwitch));
+ realm_template->Set(
+ String::NewFromUtf8(isolate, "eval", NewStringType::kNormal)
+ .ToLocalChecked(),
+ FunctionTemplate::New(isolate, RealmEval));
+ realm_template->SetAccessor(
+ String::NewFromUtf8(isolate, "shared", NewStringType::kNormal)
+ .ToLocalChecked(),
+ RealmSharedGet, RealmSharedSet);
+ global_template->Set(
+ String::NewFromUtf8(isolate, "Realm", NewStringType::kNormal)
+ .ToLocalChecked(),
+ realm_template);
#ifndef V8_SHARED
- Handle<ObjectTemplate> performance_template = ObjectTemplate::New(isolate);
- performance_template->Set(String::NewFromUtf8(isolate, "now"),
- FunctionTemplate::New(isolate, PerformanceNow));
- global_template->Set(String::NewFromUtf8(isolate, "performance"),
- performance_template);
+ Local<ObjectTemplate> performance_template = ObjectTemplate::New(isolate);
+ performance_template->Set(
+ String::NewFromUtf8(isolate, "now", NewStringType::kNormal)
+ .ToLocalChecked(),
+ FunctionTemplate::New(isolate, PerformanceNow));
+ global_template->Set(
+ String::NewFromUtf8(isolate, "performance", NewStringType::kNormal)
+ .ToLocalChecked(),
+ performance_template);
+
+ Local<FunctionTemplate> worker_fun_template =
+ FunctionTemplate::New(isolate, WorkerNew);
+ Local<Signature> worker_signature =
+ Signature::New(isolate, worker_fun_template);
+ worker_fun_template->SetClassName(
+ String::NewFromUtf8(isolate, "Worker", NewStringType::kNormal)
+ .ToLocalChecked());
+ worker_fun_template->ReadOnlyPrototype();
+ worker_fun_template->PrototypeTemplate()->Set(
+ String::NewFromUtf8(isolate, "terminate", NewStringType::kNormal)
+ .ToLocalChecked(),
+ FunctionTemplate::New(isolate, WorkerTerminate, Local<Value>(),
+ worker_signature));
+ worker_fun_template->PrototypeTemplate()->Set(
+ String::NewFromUtf8(isolate, "postMessage", NewStringType::kNormal)
+ .ToLocalChecked(),
+ FunctionTemplate::New(isolate, WorkerPostMessage, Local<Value>(),
+ worker_signature));
+ worker_fun_template->PrototypeTemplate()->Set(
+ String::NewFromUtf8(isolate, "getMessage", NewStringType::kNormal)
+ .ToLocalChecked(),
+ FunctionTemplate::New(isolate, WorkerGetMessage, Local<Value>(),
+ worker_signature));
+ worker_fun_template->InstanceTemplate()->SetInternalFieldCount(1);
+ global_template->Set(
+ String::NewFromUtf8(isolate, "Worker", NewStringType::kNormal)
+ .ToLocalChecked(),
+ worker_fun_template);
#endif // !V8_SHARED
- Handle<ObjectTemplate> os_templ = ObjectTemplate::New(isolate);
+ Local<ObjectTemplate> os_templ = ObjectTemplate::New(isolate);
AddOSMethods(isolate, os_templ);
- global_template->Set(String::NewFromUtf8(isolate, "os"), os_templ);
+ global_template->Set(
+ String::NewFromUtf8(isolate, "os", NewStringType::kNormal)
+ .ToLocalChecked(),
+ os_templ);
return global_template;
}
@@ -936,26 +1250,9 @@
void Shell::Initialize(Isolate* isolate) {
#ifndef V8_SHARED
- Shell::counter_map_ = new CounterMap();
// Set up counters
if (i::StrLength(i::FLAG_map_counters) != 0)
MapCounters(isolate, i::FLAG_map_counters);
- if (i::FLAG_dump_counters || i::FLAG_track_gc_object_stats) {
- isolate->SetCounterFunction(LookupCounter);
- isolate->SetCreateHistogramFunction(CreateHistogram);
- isolate->SetAddHistogramSampleFunction(AddHistogramSample);
- }
-#endif // !V8_SHARED
-}
-
-
-void Shell::InitializeDebugger(Isolate* isolate) {
- if (options.test_shell) return;
-#ifndef V8_SHARED
- HandleScope scope(isolate);
- Handle<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
- utility_context_.Reset(isolate,
- Context::New(isolate, NULL, global_template));
#endif // !V8_SHARED
}
@@ -963,10 +1260,10 @@
Local<Context> Shell::CreateEvaluationContext(Isolate* isolate) {
#ifndef V8_SHARED
// This needs to be a critical section since this is not thread-safe
- base::LockGuard<base::Mutex> lock_guard(&context_mutex_);
+ base::LockGuard<base::Mutex> lock_guard(context_mutex_.Pointer());
#endif // !V8_SHARED
// Initialize the global objects
- Handle<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
+ Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
EscapableHandleScope handle_scope(isolate);
Local<Context> context = Context::New(isolate, NULL, global_template);
DCHECK(!context.IsEmpty());
@@ -984,8 +1281,12 @@
}
i::Handle<i::JSArray> arguments_jsarray =
factory->NewJSArrayWithElements(arguments_array);
- context->Global()->Set(String::NewFromUtf8(isolate, "arguments"),
- Utils::ToLocal(arguments_jsarray));
+ context->Global()
+ ->Set(context,
+ String::NewFromUtf8(isolate, "arguments", NewStringType::kNormal)
+ .ToLocalChecked(),
+ Utils::ToLocal(arguments_jsarray))
+ .FromJust();
#endif // !V8_SHARED
return handle_scope.Escape(context);
}
@@ -1014,8 +1315,6 @@
void Shell::OnExit(v8::Isolate* isolate) {
- LineEditor* line_editor = LineEditor::Get();
- if (line_editor) line_editor->Close();
#ifndef V8_SHARED
reinterpret_cast<i::Isolate*>(isolate)->DumpAndResetCompilationStats();
if (i::FLAG_dump_counters) {
@@ -1083,30 +1382,35 @@
if (file == NULL) return NULL;
fseek(file, 0, SEEK_END);
- int size = ftell(file);
+ size_t size = ftell(file);
rewind(file);
char* chars = new char[size + 1];
chars[size] = '\0';
- for (int i = 0; i < size;) {
- int read = static_cast<int>(fread(&chars[i], 1, size - i, file));
- i += read;
+ for (size_t i = 0; i < size;) {
+ i += fread(&chars[i], 1, size - i, file);
+ if (ferror(file)) {
+ fclose(file);
+ delete[] chars;
+ return nullptr;
+ }
}
fclose(file);
- *size_out = size;
+ *size_out = static_cast<int>(size);
return chars;
}
struct DataAndPersistent {
uint8_t* data;
- Persistent<ArrayBuffer> handle;
+ int byte_length;
+ Global<ArrayBuffer> handle;
};
static void ReadBufferWeakCallback(
- const v8::WeakCallbackData<ArrayBuffer, DataAndPersistent>& data) {
- size_t byte_length = data.GetValue()->ByteLength();
+ const v8::WeakCallbackInfo<DataAndPersistent>& data) {
+ int byte_length = data.GetParameter()->byte_length;
data.GetIsolate()->AdjustAmountOfExternalAllocatedMemory(
-static_cast<intptr_t>(byte_length));
@@ -1134,10 +1438,11 @@
Throw(args.GetIsolate(), "Error reading file");
return;
}
- Handle<v8::ArrayBuffer> buffer =
- ArrayBuffer::New(isolate, data->data, length);
+ data->byte_length = length;
+ Local<v8::ArrayBuffer> buffer = ArrayBuffer::New(isolate, data->data, length);
data->handle.Reset(isolate, buffer);
- data->handle.SetWeak(data, ReadBufferWeakCallback);
+ data->handle.SetWeak(data, ReadBufferWeakCallback,
+ v8::WeakCallbackType::kParameter);
data->handle.MarkIndependent();
isolate->AdjustAmountOfExternalAllocatedMemory(length);
@@ -1146,12 +1451,13 @@
// Reads a file into a v8 string.
-Handle<String> Shell::ReadFile(Isolate* isolate, const char* name) {
+Local<String> Shell::ReadFile(Isolate* isolate, const char* name) {
int size = 0;
char* chars = ReadChars(isolate, name, &size);
- if (chars == NULL) return Handle<String>();
- Handle<String> result =
- String::NewFromUtf8(isolate, chars, String::kNormalString, size);
+ if (chars == NULL) return Local<String>();
+ Local<String> result =
+ String::NewFromUtf8(isolate, chars, NewStringType::kNormal, size)
+ .ToLocalChecked();
delete[] chars;
return result;
}
@@ -1163,13 +1469,19 @@
v8::Local<v8::Context>::New(isolate, evaluation_context_);
v8::Context::Scope context_scope(context);
PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
- Handle<String> name = String::NewFromUtf8(isolate, "(d8)");
- LineEditor* console = LineEditor::Get();
- printf("V8 version %s [console: %s]\n", V8::GetVersion(), console->name());
- console->Open(isolate);
+ Local<String> name =
+ String::NewFromUtf8(isolate, "(d8)", NewStringType::kNormal)
+ .ToLocalChecked();
+ printf("V8 version %s\n", V8::GetVersion());
while (true) {
HandleScope inner_scope(isolate);
- Handle<String> input = console->Prompt(Shell::kPrompt);
+ printf("d8> ");
+#if defined(__native_client__)
+ // Native Client libc is used to being embedded in Chrome and
+ // has trouble recognizing when to flush.
+ fflush(stdout);
+#endif
+ Local<String> input = Shell::ReadFromStdin(isolate);
if (input.IsEmpty()) break;
ExecuteString(isolate, input, name, true, true);
}
@@ -1189,31 +1501,47 @@
bool exception_was_thrown = false;
for (int i = begin_offset_; i < end_offset_; ++i) {
const char* arg = argv_[i];
+ Shell::SourceType source_type = Shell::SCRIPT;
if (strcmp(arg, "-e") == 0 && i + 1 < end_offset_) {
// Execute argument given to -e option directly.
HandleScope handle_scope(isolate);
- Handle<String> file_name = String::NewFromUtf8(isolate, "unnamed");
- Handle<String> source = String::NewFromUtf8(isolate, argv_[i + 1]);
+ Local<String> file_name =
+ String::NewFromUtf8(isolate, "unnamed", NewStringType::kNormal)
+ .ToLocalChecked();
+ Local<String> source =
+ String::NewFromUtf8(isolate, argv_[i + 1], NewStringType::kNormal)
+ .ToLocalChecked();
+ Shell::options.script_executed = true;
if (!Shell::ExecuteString(isolate, source, file_name, false, true)) {
exception_was_thrown = true;
break;
}
++i;
+ continue;
+ } else if (strcmp(arg, "--module") == 0 && i + 1 < end_offset_) {
+ // Treat the next file as a module.
+ source_type = Shell::MODULE;
+ arg = argv_[++i];
} else if (arg[0] == '-') {
// Ignore other options. They have been parsed already.
- } else {
- // Use all other arguments as names of files to load and run.
- HandleScope handle_scope(isolate);
- Handle<String> file_name = String::NewFromUtf8(isolate, arg);
- Handle<String> source = ReadFile(isolate, arg);
- if (source.IsEmpty()) {
- printf("Error reading '%s'\n", arg);
- Shell::Exit(1);
- }
- if (!Shell::ExecuteString(isolate, source, file_name, false, true)) {
- exception_was_thrown = true;
- break;
- }
+ continue;
+ }
+
+ // Use all other arguments as names of files to load and run.
+ HandleScope handle_scope(isolate);
+ Local<String> file_name =
+ String::NewFromUtf8(isolate, arg, NewStringType::kNormal)
+ .ToLocalChecked();
+ Local<String> source = ReadFile(isolate, arg);
+ if (source.IsEmpty()) {
+ printf("Error reading '%s'\n", arg);
+ Shell::Exit(1);
+ }
+ Shell::options.script_executed = true;
+ if (!Shell::ExecuteString(isolate, source, file_name, false, true,
+ source_type)) {
+ exception_was_thrown = true;
+ break;
}
}
if (exception_was_thrown != Shell::options.expected_to_throw) {
@@ -1222,12 +1550,13 @@
}
-Handle<String> SourceGroup::ReadFile(Isolate* isolate, const char* name) {
+Local<String> SourceGroup::ReadFile(Isolate* isolate, const char* name) {
int size;
char* chars = ReadChars(isolate, name, &size);
- if (chars == NULL) return Handle<String>();
- Handle<String> result =
- String::NewFromUtf8(isolate, chars, String::kNormalString, size);
+ if (chars == NULL) return Local<String>();
+ Local<String> result =
+ String::NewFromUtf8(isolate, chars, NewStringType::kNormal, size)
+ .ToLocalChecked();
delete[] chars;
return result;
}
@@ -1244,8 +1573,10 @@
void SourceGroup::ExecuteInThread() {
- Isolate* isolate = Isolate::New();
- do {
+ Isolate::CreateParams create_params;
+ create_params.array_buffer_allocator = Shell::array_buffer_allocator;
+ Isolate* isolate = Isolate::New(create_params);
+ for (int i = 0; i < Shell::options.stress_runs; ++i) {
next_semaphore_.Wait();
{
Isolate::Scope iscope(isolate);
@@ -1259,20 +1590,10 @@
Execute(isolate);
}
}
- if (Shell::options.send_idle_notification) {
- const int kLongIdlePauseInMs = 1000;
- isolate->ContextDisposedNotification();
- isolate->IdleNotification(kLongIdlePauseInMs);
- }
- if (Shell::options.invoke_weak_callbacks) {
- // By sending a low memory notifications, we will try hard to collect
- // all garbage and will therefore also invoke all weak callbacks of
- // actually unreachable persistent handles.
- isolate->LowMemoryNotification();
- }
+ Shell::CollectGarbage(isolate);
}
done_semaphore_.Signal();
- } while (!Shell::options.last_run);
+ }
isolate->Dispose();
}
@@ -1289,10 +1610,280 @@
void SourceGroup::WaitForThread() {
if (thread_ == NULL) return;
- if (Shell::options.last_run) {
- thread_->Join();
+ done_semaphore_.Wait();
+}
+
+
+void SourceGroup::JoinThread() {
+ if (thread_ == NULL) return;
+ thread_->Join();
+}
+
+
+SerializationData::~SerializationData() {
+ // Any ArrayBuffer::Contents are owned by this SerializationData object if
+ // ownership hasn't been transferred out via ReadArrayBufferContents.
+ // SharedArrayBuffer::Contents may be used by multiple threads, so must be
+ // cleaned up by the main thread in Shell::CleanupWorkers().
+ for (int i = 0; i < array_buffer_contents_.length(); ++i) {
+ ArrayBuffer::Contents& contents = array_buffer_contents_[i];
+ if (contents.Data()) {
+ Shell::array_buffer_allocator->Free(contents.Data(),
+ contents.ByteLength());
+ }
+ }
+}
+
+
+void SerializationData::WriteTag(SerializationTag tag) { data_.Add(tag); }
+
+
+void SerializationData::WriteMemory(const void* p, int length) {
+ if (length > 0) {
+ i::Vector<uint8_t> block = data_.AddBlock(0, length);
+ memcpy(&block[0], p, length);
+ }
+}
+
+
+void SerializationData::WriteArrayBufferContents(
+ const ArrayBuffer::Contents& contents) {
+ array_buffer_contents_.Add(contents);
+ WriteTag(kSerializationTagTransferredArrayBuffer);
+ int index = array_buffer_contents_.length() - 1;
+ Write(index);
+}
+
+
+void SerializationData::WriteSharedArrayBufferContents(
+ const SharedArrayBuffer::Contents& contents) {
+ shared_array_buffer_contents_.Add(contents);
+ WriteTag(kSerializationTagTransferredSharedArrayBuffer);
+ int index = shared_array_buffer_contents_.length() - 1;
+ Write(index);
+}
+
+
+SerializationTag SerializationData::ReadTag(int* offset) const {
+ return static_cast<SerializationTag>(Read<uint8_t>(offset));
+}
+
+
+void SerializationData::ReadMemory(void* p, int length, int* offset) const {
+ if (length > 0) {
+ memcpy(p, &data_[*offset], length);
+ (*offset) += length;
+ }
+}
+
+
+void SerializationData::ReadArrayBufferContents(ArrayBuffer::Contents* contents,
+ int* offset) const {
+ int index = Read<int>(offset);
+ DCHECK(index < array_buffer_contents_.length());
+ *contents = array_buffer_contents_[index];
+ // Ownership of this ArrayBuffer::Contents is passed to the caller. Neuter
+ // our copy so it won't be double-free'd when this SerializationData is
+ // destroyed.
+ array_buffer_contents_[index] = ArrayBuffer::Contents();
+}
+
+
+void SerializationData::ReadSharedArrayBufferContents(
+ SharedArrayBuffer::Contents* contents, int* offset) const {
+ int index = Read<int>(offset);
+ DCHECK(index < shared_array_buffer_contents_.length());
+ *contents = shared_array_buffer_contents_[index];
+}
+
+
+void SerializationDataQueue::Enqueue(SerializationData* data) {
+ base::LockGuard<base::Mutex> lock_guard(&mutex_);
+ data_.Add(data);
+}
+
+
+bool SerializationDataQueue::Dequeue(SerializationData** data) {
+ base::LockGuard<base::Mutex> lock_guard(&mutex_);
+ *data = NULL;
+ if (data_.is_empty()) return false;
+ *data = data_.Remove(0);
+ return true;
+}
+
+
+bool SerializationDataQueue::IsEmpty() {
+ base::LockGuard<base::Mutex> lock_guard(&mutex_);
+ return data_.is_empty();
+}
+
+
+void SerializationDataQueue::Clear() {
+ base::LockGuard<base::Mutex> lock_guard(&mutex_);
+ for (int i = 0; i < data_.length(); ++i) {
+ delete data_[i];
+ }
+ data_.Clear();
+}
+
+
+Worker::Worker()
+ : in_semaphore_(0),
+ out_semaphore_(0),
+ thread_(NULL),
+ script_(NULL),
+ running_(false) {}
+
+
+Worker::~Worker() {
+ delete thread_;
+ thread_ = NULL;
+ delete[] script_;
+ script_ = NULL;
+ in_queue_.Clear();
+ out_queue_.Clear();
+}
+
+
+void Worker::StartExecuteInThread(const char* script) {
+ running_ = true;
+ script_ = i::StrDup(script);
+ thread_ = new WorkerThread(this);
+ thread_->Start();
+}
+
+
+void Worker::PostMessage(SerializationData* data) {
+ in_queue_.Enqueue(data);
+ in_semaphore_.Signal();
+}
+
+
+SerializationData* Worker::GetMessage() {
+ SerializationData* data = NULL;
+ while (!out_queue_.Dequeue(&data)) {
+ // If the worker is no longer running, and there are no messages in the
+ // queue, don't expect any more messages from it.
+ if (!base::NoBarrier_Load(&running_)) break;
+ out_semaphore_.Wait();
+ }
+ return data;
+}
+
+
+void Worker::Terminate() {
+ base::NoBarrier_Store(&running_, false);
+ // Post NULL to wake the Worker thread message loop, and tell it to stop
+ // running.
+ PostMessage(NULL);
+}
+
+
+void Worker::WaitForThread() {
+ Terminate();
+ thread_->Join();
+}
+
+
+void Worker::ExecuteInThread() {
+ Isolate::CreateParams create_params;
+ create_params.array_buffer_allocator = Shell::array_buffer_allocator;
+ Isolate* isolate = Isolate::New(create_params);
+ {
+ Isolate::Scope iscope(isolate);
+ {
+ HandleScope scope(isolate);
+ PerIsolateData data(isolate);
+ Local<Context> context = Shell::CreateEvaluationContext(isolate);
+ {
+ Context::Scope cscope(context);
+ PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate));
+
+ Local<Object> global = context->Global();
+ Local<Value> this_value = External::New(isolate, this);
+ Local<FunctionTemplate> postmessage_fun_template =
+ FunctionTemplate::New(isolate, PostMessageOut, this_value);
+
+ Local<Function> postmessage_fun;
+ if (postmessage_fun_template->GetFunction(context)
+ .ToLocal(&postmessage_fun)) {
+ global->Set(context, String::NewFromUtf8(isolate, "postMessage",
+ NewStringType::kNormal)
+ .ToLocalChecked(),
+ postmessage_fun).FromJust();
+ }
+
+ // First run the script
+ Local<String> file_name =
+ String::NewFromUtf8(isolate, "unnamed", NewStringType::kNormal)
+ .ToLocalChecked();
+ Local<String> source =
+ String::NewFromUtf8(isolate, script_, NewStringType::kNormal)
+ .ToLocalChecked();
+ if (Shell::ExecuteString(isolate, source, file_name, false, true)) {
+ // Get the message handler
+ Local<Value> onmessage =
+ global->Get(context, String::NewFromUtf8(isolate, "onmessage",
+ NewStringType::kNormal)
+ .ToLocalChecked()).ToLocalChecked();
+ if (onmessage->IsFunction()) {
+ Local<Function> onmessage_fun = Local<Function>::Cast(onmessage);
+ // Now wait for messages
+ while (true) {
+ in_semaphore_.Wait();
+ SerializationData* data;
+ if (!in_queue_.Dequeue(&data)) continue;
+ if (data == NULL) {
+ break;
+ }
+ int offset = 0;
+ Local<Value> data_value;
+ if (Shell::DeserializeValue(isolate, *data, &offset)
+ .ToLocal(&data_value)) {
+ Local<Value> argv[] = {data_value};
+ (void)onmessage_fun->Call(context, global, 1, argv);
+ }
+ delete data;
+ }
+ }
+ }
+ }
+ }
+ Shell::CollectGarbage(isolate);
+ }
+ isolate->Dispose();
+
+ // Post NULL to wake the thread waiting on GetMessage() if there is one.
+ out_queue_.Enqueue(NULL);
+ out_semaphore_.Signal();
+}
+
+
+void Worker::PostMessageOut(const v8::FunctionCallbackInfo<v8::Value>& args) {
+ Isolate* isolate = args.GetIsolate();
+ HandleScope handle_scope(isolate);
+
+ if (args.Length() < 1) {
+ Throw(isolate, "Invalid argument");
+ return;
+ }
+
+ Local<Value> message = args[0];
+
+ // TODO(binji): Allow transferring from worker to main thread?
+ Shell::ObjectList to_transfer;
+
+ Shell::ObjectList seen_objects;
+ SerializationData* data = new SerializationData;
+ if (Shell::SerializeValue(isolate, message, to_transfer, &seen_objects,
+ data)) {
+ DCHECK(args.Data()->IsExternal());
+ Local<External> this_value = Local<External>::Cast(args.Data());
+ Worker* worker = static_cast<Worker*>(this_value->Value());
+ worker->out_queue_.Enqueue(data);
+ worker->out_semaphore_.Signal();
} else {
- done_semaphore_.Wait();
+ delete data;
}
}
#endif // !V8_SHARED
@@ -1331,6 +1922,10 @@
} else if (strcmp(argv[i], "--test") == 0) {
options.test_shell = true;
argv[i] = NULL;
+ } else if (strcmp(argv[i], "--notest") == 0 ||
+ strcmp(argv[i], "--no-test") == 0) {
+ options.test_shell = false;
+ argv[i] = NULL;
} else if (strcmp(argv[i], "--send-idle-notification") == 0) {
options.send_idle_notification = true;
argv[i] = NULL;
@@ -1339,6 +1934,9 @@
// TODO(jochen) See issue 3351
options.send_idle_notification = true;
argv[i] = NULL;
+ } else if (strcmp(argv[i], "--omit-quit") == 0) {
+ options.omit_quit = true;
+ argv[i] = NULL;
} else if (strcmp(argv[i], "-f") == 0) {
// Ignore any -f flags for compatibility with other stand-alone
// JavaScript engines.
@@ -1367,9 +1965,6 @@
} else if (strcmp(argv[i], "--dump-counters") == 0) {
printf("D8 with shared library does not include counters\n");
return false;
- } else if (strcmp(argv[i], "--debugger") == 0) {
- printf("Javascript debugger not included\n");
- return false;
#endif // V8_SHARED
#ifdef V8_USE_EXTERNAL_STARTUP_DATA
} else if (strncmp(argv[i], "--natives_blob=", 15) == 0) {
@@ -1398,6 +1993,8 @@
v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
+ bool enable_harmony_modules = false;
+
// Set up isolated source groups.
options.isolate_sources = new SourceGroup[options.num_isolates];
SourceGroup* current = options.isolate_sources;
@@ -1408,8 +2005,16 @@
current->End(i);
current++;
current->Begin(argv, i + 1);
+ } else if (strcmp(str, "--module") == 0) {
+ // Pass on to SourceGroup, which understands this option.
+ enable_harmony_modules = true;
} else if (strncmp(argv[i], "--", 2) == 0) {
printf("Warning: unknown flag %s.\nTry --help for options\n", argv[i]);
+ } else if (strcmp(str, "-e") == 0 && i + 1 < argc) {
+ options.script_executed = true;
+ } else if (strncmp(str, "-", 1) != 0) {
+ // Not a flag, so it must be a script to execute.
+ options.script_executed = true;
}
}
current->End(argc);
@@ -1418,11 +2023,15 @@
SetFlagsFromString("--nologfile_per_isolate");
}
+ if (enable_harmony_modules) {
+ SetFlagsFromString("--harmony-modules");
+ }
+
return true;
}
-int Shell::RunMain(Isolate* isolate, int argc, char* argv[]) {
+int Shell::RunMain(Isolate* isolate, int argc, char* argv[], bool last_run) {
#ifndef V8_SHARED
for (int i = 1; i < options.num_isolates; ++i) {
options.isolate_sources[i].StartExecuteInThread();
@@ -1431,16 +2040,9 @@
{
HandleScope scope(isolate);
Local<Context> context = CreateEvaluationContext(isolate);
- if (options.last_run && options.use_interactive_shell()) {
+ if (last_run && options.use_interactive_shell()) {
// Keep using the same context in the interactive shell.
evaluation_context_.Reset(isolate, context);
-#ifndef V8_SHARED
- // If the interactive debugger is enabled make sure to activate
- // it before running the files passed on the command line.
- if (i::FLAG_debugger) {
- InstallUtilityScript(isolate);
- }
-#endif // !V8_SHARED
}
{
Context::Scope cscope(context);
@@ -1448,10 +2050,27 @@
options.isolate_sources[0].Execute(isolate);
}
}
+ CollectGarbage(isolate);
+#ifndef V8_SHARED
+ for (int i = 1; i < options.num_isolates; ++i) {
+ if (last_run) {
+ options.isolate_sources[i].JoinThread();
+ } else {
+ options.isolate_sources[i].WaitForThread();
+ }
+ }
+ CleanupWorkers();
+#endif // !V8_SHARED
+ return 0;
+}
+
+
+void Shell::CollectGarbage(Isolate* isolate) {
if (options.send_idle_notification) {
- const int kLongIdlePauseInMs = 1000;
+ const double kLongIdlePauseInSeconds = 1.0;
isolate->ContextDisposedNotification();
- isolate->IdleNotification(kLongIdlePauseInMs);
+ isolate->IdleNotificationDeadline(
+ g_platform->MonotonicallyIncreasingTime() + kLongIdlePauseInSeconds);
}
if (options.invoke_weak_callbacks) {
// By sending a low memory notifications, we will try hard to collect all
@@ -1459,17 +2078,284 @@
// unreachable persistent handles.
isolate->LowMemoryNotification();
}
+}
+
+void Shell::EmptyMessageQueues(Isolate* isolate) {
#ifndef V8_SHARED
- for (int i = 1; i < options.num_isolates; ++i) {
- options.isolate_sources[i].WaitForThread();
+ if (!i::FLAG_verify_predictable) {
+#endif
+ while (v8::platform::PumpMessageLoop(g_platform, isolate)) continue;
+#ifndef V8_SHARED
}
-#endif // !V8_SHARED
- return 0;
+#endif
}
#ifndef V8_SHARED
+bool Shell::SerializeValue(Isolate* isolate, Local<Value> value,
+ const ObjectList& to_transfer,
+ ObjectList* seen_objects,
+ SerializationData* out_data) {
+ DCHECK(out_data);
+ Local<Context> context = isolate->GetCurrentContext();
+
+ if (value->IsUndefined()) {
+ out_data->WriteTag(kSerializationTagUndefined);
+ } else if (value->IsNull()) {
+ out_data->WriteTag(kSerializationTagNull);
+ } else if (value->IsTrue()) {
+ out_data->WriteTag(kSerializationTagTrue);
+ } else if (value->IsFalse()) {
+ out_data->WriteTag(kSerializationTagFalse);
+ } else if (value->IsNumber()) {
+ Local<Number> num = Local<Number>::Cast(value);
+ double value = num->Value();
+ out_data->WriteTag(kSerializationTagNumber);
+ out_data->Write(value);
+ } else if (value->IsString()) {
+ v8::String::Utf8Value str(value);
+ out_data->WriteTag(kSerializationTagString);
+ out_data->Write(str.length());
+ out_data->WriteMemory(*str, str.length());
+ } else if (value->IsArray()) {
+ Local<Array> array = Local<Array>::Cast(value);
+ if (FindInObjectList(array, *seen_objects)) {
+ Throw(isolate, "Duplicated arrays not supported");
+ return false;
+ }
+ seen_objects->Add(array);
+ out_data->WriteTag(kSerializationTagArray);
+ uint32_t length = array->Length();
+ out_data->Write(length);
+ for (uint32_t i = 0; i < length; ++i) {
+ Local<Value> element_value;
+ if (array->Get(context, i).ToLocal(&element_value)) {
+ if (!SerializeValue(isolate, element_value, to_transfer, seen_objects,
+ out_data))
+ return false;
+ } else {
+ Throw(isolate, "Failed to serialize array element.");
+ return false;
+ }
+ }
+ } else if (value->IsArrayBuffer()) {
+ Local<ArrayBuffer> array_buffer = Local<ArrayBuffer>::Cast(value);
+ if (FindInObjectList(array_buffer, *seen_objects)) {
+ Throw(isolate, "Duplicated array buffers not supported");
+ return false;
+ }
+ seen_objects->Add(array_buffer);
+ if (FindInObjectList(array_buffer, to_transfer)) {
+ // Transfer ArrayBuffer
+ if (!array_buffer->IsNeuterable()) {
+ Throw(isolate, "Attempting to transfer an un-neuterable ArrayBuffer");
+ return false;
+ }
+
+ ArrayBuffer::Contents contents = array_buffer->IsExternal()
+ ? array_buffer->GetContents()
+ : array_buffer->Externalize();
+ array_buffer->Neuter();
+ out_data->WriteArrayBufferContents(contents);
+ } else {
+ ArrayBuffer::Contents contents = array_buffer->GetContents();
+ // Clone ArrayBuffer
+ if (contents.ByteLength() > i::kMaxInt) {
+ Throw(isolate, "ArrayBuffer is too big to clone");
+ return false;
+ }
+
+ int32_t byte_length = static_cast<int32_t>(contents.ByteLength());
+ out_data->WriteTag(kSerializationTagArrayBuffer);
+ out_data->Write(byte_length);
+ out_data->WriteMemory(contents.Data(), byte_length);
+ }
+ } else if (value->IsSharedArrayBuffer()) {
+ Local<SharedArrayBuffer> sab = Local<SharedArrayBuffer>::Cast(value);
+ if (FindInObjectList(sab, *seen_objects)) {
+ Throw(isolate, "Duplicated shared array buffers not supported");
+ return false;
+ }
+ seen_objects->Add(sab);
+ if (!FindInObjectList(sab, to_transfer)) {
+ Throw(isolate, "SharedArrayBuffer must be transferred");
+ return false;
+ }
+
+ SharedArrayBuffer::Contents contents;
+ if (sab->IsExternal()) {
+ contents = sab->GetContents();
+ } else {
+ contents = sab->Externalize();
+ base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer());
+ externalized_shared_contents_.Add(contents);
+ }
+ out_data->WriteSharedArrayBufferContents(contents);
+ } else if (value->IsObject()) {
+ Local<Object> object = Local<Object>::Cast(value);
+ if (FindInObjectList(object, *seen_objects)) {
+ Throw(isolate, "Duplicated objects not supported");
+ return false;
+ }
+ seen_objects->Add(object);
+ Local<Array> property_names;
+ if (!object->GetOwnPropertyNames(context).ToLocal(&property_names)) {
+ Throw(isolate, "Unable to get property names");
+ return false;
+ }
+
+ uint32_t length = property_names->Length();
+ out_data->WriteTag(kSerializationTagObject);
+ out_data->Write(length);
+ for (uint32_t i = 0; i < length; ++i) {
+ Local<Value> name;
+ Local<Value> property_value;
+ if (property_names->Get(context, i).ToLocal(&name) &&
+ object->Get(context, name).ToLocal(&property_value)) {
+ if (!SerializeValue(isolate, name, to_transfer, seen_objects, out_data))
+ return false;
+ if (!SerializeValue(isolate, property_value, to_transfer, seen_objects,
+ out_data))
+ return false;
+ } else {
+ Throw(isolate, "Failed to serialize property.");
+ return false;
+ }
+ }
+ } else {
+ Throw(isolate, "Don't know how to serialize object");
+ return false;
+ }
+
+ return true;
+}
+
+
+MaybeLocal<Value> Shell::DeserializeValue(Isolate* isolate,
+ const SerializationData& data,
+ int* offset) {
+ DCHECK(offset);
+ EscapableHandleScope scope(isolate);
+ // This function should not use utility_context_ because it is running on a
+ // different thread.
+ Local<Value> result;
+ SerializationTag tag = data.ReadTag(offset);
+
+ switch (tag) {
+ case kSerializationTagUndefined:
+ result = Undefined(isolate);
+ break;
+ case kSerializationTagNull:
+ result = Null(isolate);
+ break;
+ case kSerializationTagTrue:
+ result = True(isolate);
+ break;
+ case kSerializationTagFalse:
+ result = False(isolate);
+ break;
+ case kSerializationTagNumber:
+ result = Number::New(isolate, data.Read<double>(offset));
+ break;
+ case kSerializationTagString: {
+ int length = data.Read<int>(offset);
+ CHECK(length >= 0);
+ std::vector<char> buffer(length + 1); // + 1 so it is never empty.
+ data.ReadMemory(&buffer[0], length, offset);
+ MaybeLocal<String> str =
+ String::NewFromUtf8(isolate, &buffer[0], NewStringType::kNormal,
+ length).ToLocalChecked();
+ if (!str.IsEmpty()) result = str.ToLocalChecked();
+ break;
+ }
+ case kSerializationTagArray: {
+ uint32_t length = data.Read<uint32_t>(offset);
+ Local<Array> array = Array::New(isolate, length);
+ for (uint32_t i = 0; i < length; ++i) {
+ Local<Value> element_value;
+ CHECK(DeserializeValue(isolate, data, offset).ToLocal(&element_value));
+ array->Set(isolate->GetCurrentContext(), i, element_value).FromJust();
+ }
+ result = array;
+ break;
+ }
+ case kSerializationTagObject: {
+ int length = data.Read<int>(offset);
+ Local<Object> object = Object::New(isolate);
+ for (int i = 0; i < length; ++i) {
+ Local<Value> property_name;
+ CHECK(DeserializeValue(isolate, data, offset).ToLocal(&property_name));
+ Local<Value> property_value;
+ CHECK(DeserializeValue(isolate, data, offset).ToLocal(&property_value));
+ object->Set(isolate->GetCurrentContext(), property_name, property_value)
+ .FromJust();
+ }
+ result = object;
+ break;
+ }
+ case kSerializationTagArrayBuffer: {
+ int32_t byte_length = data.Read<int32_t>(offset);
+ Local<ArrayBuffer> array_buffer = ArrayBuffer::New(isolate, byte_length);
+ ArrayBuffer::Contents contents = array_buffer->GetContents();
+ DCHECK(static_cast<size_t>(byte_length) == contents.ByteLength());
+ data.ReadMemory(contents.Data(), byte_length, offset);
+ result = array_buffer;
+ break;
+ }
+ case kSerializationTagTransferredArrayBuffer: {
+ ArrayBuffer::Contents contents;
+ data.ReadArrayBufferContents(&contents, offset);
+ result = ArrayBuffer::New(isolate, contents.Data(), contents.ByteLength(),
+ ArrayBufferCreationMode::kInternalized);
+ break;
+ }
+ case kSerializationTagTransferredSharedArrayBuffer: {
+ SharedArrayBuffer::Contents contents;
+ data.ReadSharedArrayBufferContents(&contents, offset);
+ result = SharedArrayBuffer::New(isolate, contents.Data(),
+ contents.ByteLength());
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+
+ return scope.Escape(result);
+}
+
+
+void Shell::CleanupWorkers() {
+ // Make a copy of workers_, because we don't want to call Worker::Terminate
+ // while holding the workers_mutex_ lock. Otherwise, if a worker is about to
+ // create a new Worker, it would deadlock.
+ i::List<Worker*> workers_copy;
+ {
+ base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer());
+ allow_new_workers_ = false;
+ workers_copy.AddAll(workers_);
+ workers_.Clear();
+ }
+
+ for (int i = 0; i < workers_copy.length(); ++i) {
+ Worker* worker = workers_copy[i];
+ worker->WaitForThread();
+ delete worker;
+ }
+
+ // Now that all workers are terminated, we can re-enable Worker creation.
+ base::LockGuard<base::Mutex> lock_guard(workers_mutex_.Pointer());
+ allow_new_workers_ = true;
+
+ for (int i = 0; i < externalized_shared_contents_.length(); ++i) {
+ const SharedArrayBuffer::Contents& contents =
+ externalized_shared_contents_[i];
+ Shell::array_buffer_allocator->Free(contents.Data(), contents.ByteLength());
+ }
+ externalized_shared_contents_.Clear();
+}
+
+
static void DumpHeapConstants(i::Isolate* isolate) {
i::Heap* heap = isolate->heap();
@@ -1526,109 +2412,6 @@
#endif // !V8_SHARED
-class ShellArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
- public:
- virtual void* Allocate(size_t length) {
- void* data = AllocateUninitialized(length);
- return data == NULL ? data : memset(data, 0, length);
- }
- virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
- virtual void Free(void* data, size_t) { free(data); }
-};
-
-
-class MockArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
- public:
- void* Allocate(size_t) OVERRIDE { return malloc(0); }
- void* AllocateUninitialized(size_t length) OVERRIDE { return malloc(0); }
- void Free(void* p, size_t) OVERRIDE { free(p); }
-};
-
-
-#ifdef V8_USE_EXTERNAL_STARTUP_DATA
-class StartupDataHandler {
- public:
- StartupDataHandler(const char* exec_path, const char* natives_blob,
- const char* snapshot_blob) {
- // If we have (at least one) explicitly given blob, use those.
- // If not, use the default blob locations next to the d8 binary.
- if (natives_blob || snapshot_blob) {
- LoadFromFiles(natives_blob, snapshot_blob);
- } else {
- char* natives;
- char* snapshot;
- LoadFromFiles(RelativePath(&natives, exec_path, "natives_blob.bin"),
- RelativePath(&snapshot, exec_path, "snapshot_blob.bin"));
-
- free(natives);
- free(snapshot);
- }
- }
-
- ~StartupDataHandler() {
- delete[] natives_.data;
- delete[] snapshot_.data;
- }
-
- private:
- static char* RelativePath(char** buffer, const char* exec_path,
- const char* name) {
- DCHECK(exec_path);
- const char* last_slash = strrchr(exec_path, '/');
- if (last_slash) {
- int after_slash = last_slash - exec_path + 1;
- int name_length = static_cast<int>(strlen(name));
- *buffer =
- reinterpret_cast<char*>(calloc(after_slash + name_length + 1, 1));
- strncpy(*buffer, exec_path, after_slash);
- strncat(*buffer, name, name_length);
- } else {
- *buffer = strdup(name);
- }
- return *buffer;
- }
-
- void LoadFromFiles(const char* natives_blob, const char* snapshot_blob) {
- Load(natives_blob, &natives_, v8::V8::SetNativesDataBlob);
- Load(snapshot_blob, &snapshot_, v8::V8::SetSnapshotDataBlob);
- }
-
- void Load(const char* blob_file,
- v8::StartupData* startup_data,
- void (*setter_fn)(v8::StartupData*)) {
- startup_data->data = NULL;
- startup_data->raw_size = 0;
-
- if (!blob_file)
- return;
-
- FILE* file = fopen(blob_file, "rb");
- if (!file)
- return;
-
- fseek(file, 0, SEEK_END);
- startup_data->raw_size = ftell(file);
- rewind(file);
-
- startup_data->data = new char[startup_data->raw_size];
- int read_size =
- static_cast<int>(fread(const_cast<char*>(startup_data->data), 1,
- startup_data->raw_size, file));
- fclose(file);
-
- if (startup_data->raw_size == read_size) (*setter_fn)(startup_data);
- }
-
- v8::StartupData natives_;
- v8::StartupData snapshot_;
-
- // Disallow copy & assign.
- StartupDataHandler(const StartupDataHandler& other);
- void operator=(const StartupDataHandler& other);
-};
-#endif // V8_USE_EXTERNAL_STARTUP_DATA
-
-
int Shell::Main(int argc, char* argv[]) {
#if (defined(_WIN32) || defined(_WIN64))
UINT new_flags =
@@ -1647,25 +2430,35 @@
#endif // defined(_WIN32) || defined(_WIN64)
if (!SetOptions(argc, argv)) return 1;
v8::V8::InitializeICU(options.icu_data_file);
- v8::Platform* platform = v8::platform::CreateDefaultPlatform();
- v8::V8::InitializePlatform(platform);
+#ifndef V8_SHARED
+ g_platform = i::FLAG_verify_predictable
+ ? new PredictablePlatform()
+ : v8::platform::CreateDefaultPlatform();
+#else
+ g_platform = v8::platform::CreateDefaultPlatform();
+#endif // !V8_SHARED
+
+ v8::V8::InitializePlatform(g_platform);
v8::V8::Initialize();
-#ifdef V8_USE_EXTERNAL_STARTUP_DATA
- StartupDataHandler startup_data(argv[0], options.natives_blob,
- options.snapshot_blob);
-#endif
+ if (options.natives_blob || options.snapshot_blob) {
+ v8::V8::InitializeExternalStartupData(options.natives_blob,
+ options.snapshot_blob);
+ } else {
+ v8::V8::InitializeExternalStartupData(argv[0]);
+ }
SetFlagsFromString("--trace-hydrogen-file=hydrogen.cfg");
SetFlagsFromString("--trace-turbo-cfg-file=turbo.cfg");
SetFlagsFromString("--redirect-code-traces-to=code.asm");
- ShellArrayBufferAllocator array_buffer_allocator;
- MockArrayBufferAllocator mock_arraybuffer_allocator;
- if (options.mock_arraybuffer_allocator) {
- v8::V8::SetArrayBufferAllocator(&mock_arraybuffer_allocator);
- } else {
- v8::V8::SetArrayBufferAllocator(&array_buffer_allocator);
- }
int result = 0;
Isolate::CreateParams create_params;
+ ShellArrayBufferAllocator shell_array_buffer_allocator;
+ MockArrayBufferAllocator mock_arraybuffer_allocator;
+ if (options.mock_arraybuffer_allocator) {
+ Shell::array_buffer_allocator = &mock_arraybuffer_allocator;
+ } else {
+ Shell::array_buffer_allocator = &shell_array_buffer_allocator;
+ }
+ create_params.array_buffer_allocator = Shell::array_buffer_allocator;
#if !defined(V8_SHARED) && defined(ENABLE_GDB_JIT_INTERFACE)
if (i::FLAG_gdbjit) {
create_params.code_event_handler = i::GDBJITInterface::EventHandler;
@@ -1677,16 +2470,20 @@
#ifndef V8_SHARED
create_params.constraints.ConfigureDefaults(
base::SysInfo::AmountOfPhysicalMemory(),
- base::SysInfo::AmountOfVirtualMemory(),
- base::SysInfo::NumberOfProcessors());
+ base::SysInfo::AmountOfVirtualMemory());
+
+ Shell::counter_map_ = new CounterMap();
+ if (i::FLAG_dump_counters || i::FLAG_track_gc_object_stats) {
+ create_params.counter_lookup_callback = LookupCounter;
+ create_params.create_histogram_callback = CreateHistogram;
+ create_params.add_histogram_sample_callback = AddHistogramSample;
+ }
#endif
Isolate* isolate = Isolate::New(create_params);
- DumbLineEditor dumb_line_editor(isolate);
{
Isolate::Scope scope(isolate);
Initialize(isolate);
PerIsolateData data(isolate);
- InitializeDebugger(isolate);
#ifndef V8_SHARED
if (options.dump_heap_constants) {
@@ -1699,38 +2496,46 @@
Testing::SetStressRunType(options.stress_opt
? Testing::kStressTypeOpt
: Testing::kStressTypeDeopt);
- int stress_runs = Testing::GetStressRuns();
- for (int i = 0; i < stress_runs && result == 0; i++) {
- printf("============ Stress %d/%d ============\n", i + 1, stress_runs);
+ options.stress_runs = Testing::GetStressRuns();
+ for (int i = 0; i < options.stress_runs && result == 0; i++) {
+ printf("============ Stress %d/%d ============\n", i + 1,
+ options.stress_runs);
Testing::PrepareStressRun(i);
- options.last_run = (i == stress_runs - 1);
- result = RunMain(isolate, argc, argv);
+ bool last_run = i == options.stress_runs - 1;
+ result = RunMain(isolate, argc, argv, last_run);
}
printf("======== Full Deoptimization =======\n");
- Testing::DeoptimizeAll();
+ Testing::DeoptimizeAll(isolate);
#if !defined(V8_SHARED)
} else if (i::FLAG_stress_runs > 0) {
- int stress_runs = i::FLAG_stress_runs;
- for (int i = 0; i < stress_runs && result == 0; i++) {
- printf("============ Run %d/%d ============\n", i + 1, stress_runs);
- options.last_run = (i == stress_runs - 1);
- result = RunMain(isolate, argc, argv);
+ options.stress_runs = i::FLAG_stress_runs;
+ for (int i = 0; i < options.stress_runs && result == 0; i++) {
+ printf("============ Run %d/%d ============\n", i + 1,
+ options.stress_runs);
+ bool last_run = i == options.stress_runs - 1;
+ result = RunMain(isolate, argc, argv, last_run);
}
#endif
} else {
- result = RunMain(isolate, argc, argv);
+ bool last_run = true;
+ result = RunMain(isolate, argc, argv, last_run);
}
// Run interactive shell if explicitly requested or if no script has been
// executed, but never on --test
if (options.use_interactive_shell()) {
#ifndef V8_SHARED
- if (!i::FLAG_debugger) {
- InstallUtilityScript(isolate);
- }
+ InstallUtilityScript(isolate);
#endif // !V8_SHARED
RunShell(isolate);
}
+
+ // Shut down contexts and collect garbage.
+ evaluation_context_.Reset();
+#ifndef V8_SHARED
+ utility_context_.Reset();
+#endif // !V8_SHARED
+ CollectGarbage(isolate);
}
OnExit(isolate);
#ifndef V8_SHARED
@@ -1744,7 +2549,7 @@
isolate->Dispose();
V8::Dispose();
V8::ShutdownPlatform();
- delete platform;
+ delete g_platform;
return result;
}