Commit c0e4e79b authored by Yang Guo's avatar Yang Guo Committed by Commit Bot

[d8] implement setTimeout.

R=ahaas@chromium.org, jarin@chromium.org

Bug: v8:6770
Change-Id: Iebf4dc9f2dd75079c5362e02d859c48e2113cf20
Reviewed-on: https://chromium-review.googlesource.com/643067Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Commit-Queue: Yang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47780}
parent 3612350c
...@@ -395,7 +395,6 @@ static platform::tracing::TraceConfig* CreateTraceConfigFromJSON( ...@@ -395,7 +395,6 @@ static platform::tracing::TraceConfig* CreateTraceConfigFromJSON(
class PerIsolateData { class PerIsolateData {
public: public:
explicit PerIsolateData(Isolate* isolate) : isolate_(isolate), realms_(NULL) { explicit PerIsolateData(Isolate* isolate) : isolate_(isolate), realms_(NULL) {
HandleScope scope(isolate);
isolate->SetData(0, this); isolate->SetData(0, this);
} }
...@@ -415,6 +414,25 @@ class PerIsolateData { ...@@ -415,6 +414,25 @@ class PerIsolateData {
PerIsolateData* data_; PerIsolateData* data_;
}; };
inline void SetTimeout(Local<Function> callback, Local<Context> context) {
set_timeout_callbacks_.emplace(isolate_, callback);
set_timeout_contexts_.emplace(isolate_, context);
}
inline MaybeLocal<Function> GetTimeoutCallback() {
if (set_timeout_callbacks_.empty()) return MaybeLocal<Function>();
Local<Function> result = set_timeout_callbacks_.front().Get(isolate_);
set_timeout_callbacks_.pop();
return result;
}
inline MaybeLocal<Context> GetTimeoutContext() {
if (set_timeout_contexts_.empty()) return MaybeLocal<Context>();
Local<Context> result = set_timeout_contexts_.front().Get(isolate_);
set_timeout_contexts_.pop();
return result;
}
private: private:
friend class Shell; friend class Shell;
friend class RealmScope; friend class RealmScope;
...@@ -424,6 +442,8 @@ class PerIsolateData { ...@@ -424,6 +442,8 @@ class PerIsolateData {
int realm_switch_; int realm_switch_;
Global<Context>* realms_; Global<Context>* realms_;
Global<Value> realm_shared_; Global<Value> realm_shared_;
std::queue<Global<Function>> set_timeout_callbacks_;
std::queue<Global<Context>> set_timeout_contexts_;
int RealmIndexOrThrow(const v8::FunctionCallbackInfo<v8::Value>& args, int RealmIndexOrThrow(const v8::FunctionCallbackInfo<v8::Value>& args,
int arg_offset); int arg_offset);
...@@ -1293,6 +1313,14 @@ void Shell::Load(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -1293,6 +1313,14 @@ void Shell::Load(const v8::FunctionCallbackInfo<v8::Value>& args) {
} }
} }
void Shell::SetTimeout(const v8::FunctionCallbackInfo<v8::Value>& args) {
Isolate* isolate = args.GetIsolate();
args.GetReturnValue().Set(v8::Number::New(isolate, 0));
if (args.Length() == 0 || !args[0]->IsFunction()) return;
Local<Function> callback = Local<Function>::Cast(args[0]);
Local<Context> context = isolate->GetCurrentContext();
PerIsolateData::Get(isolate)->SetTimeout(callback, context);
}
void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) { void Shell::WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args) {
Isolate* isolate = args.GetIsolate(); Isolate* isolate = args.GetIsolate();
...@@ -1644,6 +1672,10 @@ Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) { ...@@ -1644,6 +1672,10 @@ Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
String::NewFromUtf8(isolate, "load", NewStringType::kNormal) String::NewFromUtf8(isolate, "load", NewStringType::kNormal)
.ToLocalChecked(), .ToLocalChecked(),
FunctionTemplate::New(isolate, Load)); FunctionTemplate::New(isolate, Load));
global_template->Set(
String::NewFromUtf8(isolate, "setTimeout", NewStringType::kNormal)
.ToLocalChecked(),
FunctionTemplate::New(isolate, SetTimeout));
// Some Emscripten-generated code tries to call 'quit', which in turn would // 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 // 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'. // we can terminate cleanly then, so we need a way to hide 'quit'.
...@@ -2847,39 +2879,48 @@ void Shell::SetWaitUntilDone(Isolate* isolate, bool value) { ...@@ -2847,39 +2879,48 @@ void Shell::SetWaitUntilDone(Isolate* isolate, bool value) {
} }
} }
bool Shell::IsWaitUntilDone(Isolate* isolate) { namespace {
base::LockGuard<base::Mutex> guard(isolate_status_lock_.Pointer()); void ProcessMessages(Isolate* isolate,
DCHECK_GT(isolate_status_.count(isolate), 0); std::function<platform::MessageLoopBehavior()> behavior) {
return isolate_status_[isolate]; Platform* platform = GetDefaultPlatform();
while (true) {
while (v8::platform::PumpMessageLoop(platform, isolate, behavior())) {
isolate->RunMicrotasks();
}
if (platform->IdleTasksEnabled(isolate)) {
v8::platform::RunIdleTasks(platform, isolate,
50.0 / base::Time::kMillisecondsPerSecond);
}
HandleScope handle_scope(isolate);
PerIsolateData* data = PerIsolateData::Get(isolate);
Local<Function> callback;
if (!data->GetTimeoutCallback().ToLocal(&callback)) break;
Local<Context> context;
if (!data->GetTimeoutContext().ToLocal(&context)) break;
TryCatch try_catch(isolate);
try_catch.SetVerbose(true);
Context::Scope context_scope(context);
if (callback->Call(context, Undefined(isolate), 0, nullptr).IsEmpty()) {
Shell::ReportException(isolate, &try_catch);
return;
}
}
} }
} // anonymous namespace
void Shell::CompleteMessageLoop(Isolate* isolate) { void Shell::CompleteMessageLoop(Isolate* isolate) {
Platform* platform = GetDefaultPlatform(); ProcessMessages(isolate, [isolate]() {
while (v8::platform::PumpMessageLoop( base::LockGuard<base::Mutex> guard(isolate_status_lock_.Pointer());
platform, isolate, DCHECK_GT(isolate_status_.count(isolate), 0);
Shell::IsWaitUntilDone(isolate) return isolate_status_[isolate]
? platform::MessageLoopBehavior::kWaitForWork ? platform::MessageLoopBehavior::kWaitForWork
: platform::MessageLoopBehavior::kDoNotWait)) { : platform::MessageLoopBehavior::kDoNotWait;
isolate->RunMicrotasks(); });
}
if (platform->IdleTasksEnabled(isolate)) {
v8::platform::RunIdleTasks(platform, isolate,
50.0 / base::Time::kMillisecondsPerSecond);
}
} }
void Shell::EmptyMessageQueues(Isolate* isolate) { void Shell::EmptyMessageQueues(Isolate* isolate) {
Platform* platform = GetDefaultPlatform(); ProcessMessages(isolate,
// Pump the message loop until it is empty. []() { return platform::MessageLoopBehavior::kDoNotWait; });
while (v8::platform::PumpMessageLoop(
platform, isolate, platform::MessageLoopBehavior::kDoNotWait)) {
isolate->RunMicrotasks();
}
// Run the idle tasks.
if (platform->IdleTasksEnabled(isolate)) {
v8::platform::RunIdleTasks(platform, isolate,
50.0 / base::Time::kMillisecondsPerSecond);
}
} }
class Serializer : public ValueSerializer::Delegate { class Serializer : public ValueSerializer::Delegate {
......
...@@ -408,6 +408,7 @@ class Shell : public i::AllStatic { ...@@ -408,6 +408,7 @@ class Shell : public i::AllStatic {
args.GetReturnValue().Set(ReadFromStdin(args.GetIsolate())); args.GetReturnValue().Set(ReadFromStdin(args.GetIsolate()));
} }
static void Load(const v8::FunctionCallbackInfo<v8::Value>& args); static void Load(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetTimeout(const v8::FunctionCallbackInfo<v8::Value>& args);
static void WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args); static void WorkerNew(const v8::FunctionCallbackInfo<v8::Value>& args);
static void WorkerPostMessage( static void WorkerPostMessage(
const v8::FunctionCallbackInfo<v8::Value>& args); const v8::FunctionCallbackInfo<v8::Value>& args);
...@@ -462,7 +463,6 @@ class Shell : public i::AllStatic { ...@@ -462,7 +463,6 @@ class Shell : public i::AllStatic {
static ArrayBuffer::Allocator* array_buffer_allocator; static ArrayBuffer::Allocator* array_buffer_allocator;
static void SetWaitUntilDone(Isolate* isolate, bool value); static void SetWaitUntilDone(Isolate* isolate, bool value);
static bool IsWaitUntilDone(Isolate* isolate);
static char* ReadCharsFromTcpPort(const char* name, int* size_out); static char* ReadCharsFromTcpPort(const char* name, int* size_out);
......
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --no-stress-opt
var state = 0;
function inc() {
console.log("increment state");
state++;
}
function repeat() {
console.log("current state: " + state);
if (state < 3) {
setTimeout(inc, 0);
setTimeout(repeat, 0);
} else {
setTimeout(function() { throw new Error(); });
}
}
setTimeout(inc, 0);
console.log("state: " + state);
setTimeout(repeat, 0);
console.log("state: " + state);
state: 0
state: 0
increment state
current state: 1
increment state
current state: 2
increment state
current state: 3
*%(basename)s:19: Error
setTimeout(function() { throw new Error(); });
^
Error
at *%(basename)s:19:35
...@@ -297,6 +297,10 @@ ...@@ -297,6 +297,10 @@
# This section is for tests that fail in both V8 and JSC. Thus they # This section is for tests that fail in both V8 and JSC. Thus they
# have been determined to be incompatible between Mozilla and V8/JSC. # have been determined to be incompatible between Mozilla and V8/JSC.
# d8 does not implement a window object.
'js1_5/Regress/regress-317476': [FAIL],
'js1_5/Regress/regress-314401': [FAIL],
# Any local 'arguments' variable should not be allowed to shadow the value # Any local 'arguments' variable should not be allowed to shadow the value
# returned via the indirect 'arguments' property accessor. # returned via the indirect 'arguments' property accessor.
'js1_4/Functions/function-001': [FAIL_OK], 'js1_4/Functions/function-001': [FAIL_OK],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment