Reland ^2 "[async] Expose async hooks to d8"

This is a reland of 8e0f67be3f

Previously landed as: 3c4d0316e4 / 1065818
Previously landed as: 8e0f67be3f / 1088890

Original change's description:
> [async] Expose async hooks to d8
>
> This implementation follows the Node.js API as a guideline.

Bug: chromium:850530
Change-Id: I8ba22b11c80328108b197d687826ce0198420c9c
Reviewed-on: https://chromium-review.googlesource.com/1125679
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Maya Lekova <mslekova@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54191}
This commit is contained in:
Maya Lekova 2018-07-04 09:40:36 +02:00 committed by Commit Bot
parent 476a885343
commit ade7f55b3a
13 changed files with 801 additions and 0 deletions

View File

@ -3330,6 +3330,8 @@ if (is_component_build) {
v8_executable("d8") {
sources = [
"$target_gen_dir/d8-js.cc",
"src/async-hooks-wrapper.cc",
"src/async-hooks-wrapper.h",
"src/d8-console.cc",
"src/d8-console.h",
"src/d8.cc",

245
src/async-hooks-wrapper.cc Normal file
View File

@ -0,0 +1,245 @@
// Copyright 2018 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.
#include "src/async-hooks-wrapper.h"
#include "src/d8.h"
namespace v8 {
void AsyncHooksWrap::Enable() { enabled_ = true; }
void AsyncHooksWrap::Disable() { enabled_ = false; }
v8::Local<v8::Function> AsyncHooksWrap::init_function() const {
return init_function_.Get(isolate_);
}
void AsyncHooksWrap::set_init_function(v8::Local<v8::Function> value) {
init_function_.Reset(isolate_, value);
}
v8::Local<v8::Function> AsyncHooksWrap::before_function() const {
return before_function_.Get(isolate_);
}
void AsyncHooksWrap::set_before_function(v8::Local<v8::Function> value) {
before_function_.Reset(isolate_, value);
}
v8::Local<v8::Function> AsyncHooksWrap::after_function() const {
return after_function_.Get(isolate_);
}
void AsyncHooksWrap::set_after_function(v8::Local<v8::Function> value) {
after_function_.Reset(isolate_, value);
}
v8::Local<v8::Function> AsyncHooksWrap::promiseResolve_function() const {
return promiseResolve_function_.Get(isolate_);
}
void AsyncHooksWrap::set_promiseResolve_function(
v8::Local<v8::Function> value) {
promiseResolve_function_.Reset(isolate_, value);
}
static AsyncHooksWrap* UnwrapHook(
const v8::FunctionCallbackInfo<v8::Value>& args) {
Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate);
Local<Object> hook = args.This();
Local<External> wrap = Local<External>::Cast(hook->GetInternalField(0));
void* ptr = wrap->Value();
return static_cast<AsyncHooksWrap*>(ptr);
}
static void EnableHook(const v8::FunctionCallbackInfo<v8::Value>& args) {
AsyncHooksWrap* wrap = UnwrapHook(args);
wrap->Enable();
}
static void DisableHook(const v8::FunctionCallbackInfo<v8::Value>& args) {
AsyncHooksWrap* wrap = UnwrapHook(args);
wrap->Disable();
}
async_id_t AsyncHooks::GetExecutionAsyncId() const {
return asyncContexts.top().execution_async_id;
}
async_id_t AsyncHooks::GetTriggerAsyncId() const {
return asyncContexts.top().trigger_async_id;
}
Local<Object> AsyncHooks::CreateHook(
const v8::FunctionCallbackInfo<v8::Value>& args) {
Isolate* isolate = args.GetIsolate();
EscapableHandleScope handle_scope(isolate);
Local<Context> currentContext = isolate->GetCurrentContext();
AsyncHooksWrap* wrap = new AsyncHooksWrap(isolate);
CHECK(args[0]->IsObject());
Local<Object> fn_obj = args[0].As<Object>();
#define SET_HOOK_FN(name) \
Local<Value> name##_v = \
fn_obj \
->Get(currentContext, \
String::NewFromUtf8(isolate, #name, NewStringType::kNormal) \
.ToLocalChecked()) \
.ToLocalChecked(); \
if (name##_v->IsFunction()) { \
wrap->set_##name##_function(name##_v.As<Function>()); \
}
SET_HOOK_FN(init);
SET_HOOK_FN(before);
SET_HOOK_FN(after);
SET_HOOK_FN(promiseResolve);
#undef SET_HOOK_FN
async_wraps_.push_back(wrap);
Local<Object> obj = async_hooks_templ.Get(isolate)
->NewInstance(currentContext)
.ToLocalChecked();
obj->SetInternalField(0, External::New(isolate, wrap));
return handle_scope.Escape(obj);
}
void AsyncHooks::ShellPromiseHook(PromiseHookType type, Local<Promise> promise,
Local<Value> parent) {
AsyncHooks* hooks = Shell::GetAsyncHooks();
HandleScope handle_scope(hooks->isolate_);
Local<Context> currentContext = hooks->isolate_->GetCurrentContext();
if (type == PromiseHookType::kInit) {
++hooks->current_async_id;
Local<Integer> async_id =
Integer::New(hooks->isolate_, hooks->current_async_id);
promise->SetPrivate(currentContext,
hooks->async_id_smb.Get(hooks->isolate_), async_id);
if (parent->IsPromise()) {
Local<Promise> parent_promise = parent.As<Promise>();
Local<Value> parent_async_id =
parent_promise
->GetPrivate(hooks->isolate_->GetCurrentContext(),
hooks->async_id_smb.Get(hooks->isolate_))
.ToLocalChecked();
promise->SetPrivate(currentContext,
hooks->trigger_id_smb.Get(hooks->isolate_),
parent_async_id);
} else {
CHECK(parent->IsUndefined());
Local<Integer> trigger_id = Integer::New(hooks->isolate_, 0);
promise->SetPrivate(currentContext,
hooks->trigger_id_smb.Get(hooks->isolate_),
trigger_id);
}
} else if (type == PromiseHookType::kBefore) {
AsyncContext ctx;
ctx.execution_async_id =
promise
->GetPrivate(hooks->isolate_->GetCurrentContext(),
hooks->async_id_smb.Get(hooks->isolate_))
.ToLocalChecked()
.As<Integer>()
->Value();
ctx.trigger_async_id =
promise
->GetPrivate(hooks->isolate_->GetCurrentContext(),
hooks->trigger_id_smb.Get(hooks->isolate_))
.ToLocalChecked()
.As<Integer>()
->Value();
hooks->asyncContexts.push(ctx);
} else if (type == PromiseHookType::kAfter) {
hooks->asyncContexts.pop();
}
for (AsyncHooksWrap* wrap : hooks->async_wraps_) {
PromiseHookDispatch(type, promise, parent, wrap, hooks);
}
}
void AsyncHooks::Initialize() {
HandleScope handle_scope(isolate_);
async_hook_ctor.Reset(isolate_, FunctionTemplate::New(isolate_));
async_hook_ctor.Get(isolate_)->SetClassName(
String::NewFromUtf8(isolate_, "AsyncHook", NewStringType::kNormal)
.ToLocalChecked());
async_hooks_templ.Reset(isolate_,
async_hook_ctor.Get(isolate_)->InstanceTemplate());
async_hooks_templ.Get(isolate_)->SetInternalFieldCount(1);
async_hooks_templ.Get(isolate_)->Set(
String::NewFromUtf8(isolate_, "enable"),
FunctionTemplate::New(isolate_, EnableHook));
async_hooks_templ.Get(isolate_)->Set(
String::NewFromUtf8(isolate_, "disable"),
FunctionTemplate::New(isolate_, DisableHook));
async_id_smb.Reset(isolate_, Private::New(isolate_));
trigger_id_smb.Reset(isolate_, Private::New(isolate_));
isolate_->SetPromiseHook(ShellPromiseHook);
}
void AsyncHooks::Deinitialize() {
isolate_->SetPromiseHook(nullptr);
for (AsyncHooksWrap* wrap : async_wraps_) {
delete wrap;
}
}
void AsyncHooks::PromiseHookDispatch(PromiseHookType type,
Local<Promise> promise,
Local<Value> parent, AsyncHooksWrap* wrap,
AsyncHooks* hooks) {
if (!wrap->IsEnabled()) {
return;
}
HandleScope handle_scope(hooks->isolate_);
Local<Value> rcv = Undefined(hooks->isolate_);
Local<Value> async_id =
promise
->GetPrivate(hooks->isolate_->GetCurrentContext(),
hooks->async_id_smb.Get(hooks->isolate_))
.ToLocalChecked();
Local<Value> args[1] = {async_id};
// Sacrifice the brevity for readability and debugfulness
if (type == PromiseHookType::kInit) {
if (!wrap->init_function().IsEmpty()) {
Local<Value> initArgs[4] = {
async_id,
String::NewFromUtf8(hooks->isolate_, "PROMISE",
NewStringType::kNormal)
.ToLocalChecked(),
promise
->GetPrivate(hooks->isolate_->GetCurrentContext(),
hooks->trigger_id_smb.Get(hooks->isolate_))
.ToLocalChecked(),
promise};
wrap->init_function()->Call(rcv, 4, initArgs);
}
} else if (type == PromiseHookType::kBefore) {
if (!wrap->before_function().IsEmpty()) {
wrap->before_function()->Call(rcv, 1, args);
}
} else if (type == PromiseHookType::kAfter) {
if (!wrap->after_function().IsEmpty()) {
wrap->after_function()->Call(rcv, 1, args);
}
} else if (type == PromiseHookType::kResolve) {
if (!wrap->promiseResolve_function().IsEmpty()) {
wrap->promiseResolve_function()->Call(rcv, 1, args);
}
}
}
} // namespace v8

95
src/async-hooks-wrapper.h Normal file
View File

@ -0,0 +1,95 @@
// Copyright 2018 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.
#ifndef V8_ASYNC_HOOKS_WRAPPER_H_
#define V8_ASYNC_HOOKS_WRAPPER_H_
#include <stack>
#include "include/v8.h"
#include "src/objects.h"
namespace v8 {
typedef double async_id_t;
struct AsyncContext {
async_id_t execution_async_id;
async_id_t trigger_async_id;
};
class AsyncHooksWrap {
public:
explicit AsyncHooksWrap(Isolate* isolate) {
enabled_ = false;
isolate_ = isolate;
}
void Enable();
void Disable();
bool IsEnabled() const { return enabled_; }
inline v8::Local<v8::Function> init_function() const;
inline void set_init_function(v8::Local<v8::Function> value);
inline v8::Local<v8::Function> before_function() const;
inline void set_before_function(v8::Local<v8::Function> value);
inline v8::Local<v8::Function> after_function() const;
inline void set_after_function(v8::Local<v8::Function> value);
inline v8::Local<v8::Function> promiseResolve_function() const;
inline void set_promiseResolve_function(v8::Local<v8::Function> value);
private:
Isolate* isolate_;
Persistent<v8::Function> init_function_;
Persistent<v8::Function> before_function_;
Persistent<v8::Function> after_function_;
Persistent<v8::Function> promiseResolve_function_;
bool enabled_;
};
class AsyncHooks {
public:
explicit AsyncHooks(Isolate* isolate) {
isolate_ = isolate;
AsyncContext ctx;
ctx.execution_async_id = 1;
ctx.trigger_async_id = 0;
asyncContexts.push(ctx);
current_async_id = 1;
Initialize();
}
~AsyncHooks() { Deinitialize(); }
async_id_t GetExecutionAsyncId() const;
async_id_t GetTriggerAsyncId() const;
Local<Object> CreateHook(const v8::FunctionCallbackInfo<v8::Value>& args);
private:
std::vector<AsyncHooksWrap*> async_wraps_;
Isolate* isolate_;
Persistent<FunctionTemplate> async_hook_ctor;
Persistent<ObjectTemplate> async_hooks_templ;
Persistent<Private> async_id_smb;
Persistent<Private> trigger_id_smb;
void Initialize();
void Deinitialize();
static void ShellPromiseHook(PromiseHookType type, Local<Promise> promise,
Local<Value> parent);
static void PromiseHookDispatch(PromiseHookType type, Local<Promise> promise,
Local<Value> parent, AsyncHooksWrap* wrap,
AsyncHooks* hooks);
std::stack<AsyncContext> asyncContexts;
async_id_t current_async_id;
};
} // namespace v8
#endif // V8_ASYNC_HOOKS_WRAPPER_H_

View File

@ -469,6 +469,7 @@ base::LazyMutex Shell::workers_mutex_;
bool Shell::allow_new_workers_ = true;
std::vector<Worker*> Shell::workers_;
std::vector<ExternalizedContents> Shell::externalized_contents_;
AsyncHooks* Shell::async_hooks_wrapper_;
base::LazyMutex Shell::isolate_status_lock_;
std::map<v8::Isolate*, bool> Shell::isolate_status_;
base::LazyMutex Shell::cached_code_mutex_;
@ -1244,6 +1245,32 @@ void Shell::RealmSharedSet(Local<String> property,
data->realm_shared_.Reset(isolate, value);
}
// async_hooks.createHook() registers functions to be called for different
// lifetime events of each async operation.
void Shell::AsyncHooksCreateHook(
const v8::FunctionCallbackInfo<v8::Value>& args) {
Local<Object> wrap = async_hooks_wrapper_->CreateHook(args);
args.GetReturnValue().Set(wrap);
}
// async_hooks.executionAsyncId() returns the asyncId of the current execution
// context.
void Shell::AsyncHooksExecutionAsyncId(
const v8::FunctionCallbackInfo<v8::Value>& args) {
Isolate* isolate = args.GetIsolate();
HandleScope handle_scope(isolate);
args.GetReturnValue().Set(
v8::Number::New(isolate, async_hooks_wrapper_->GetExecutionAsyncId()));
}
void Shell::AsyncHooksTriggerAsyncId(
const v8::FunctionCallbackInfo<v8::Value>& args) {
Isolate* isolate = args.GetIsolate();
HandleScope handle_scope(isolate);
args.GetReturnValue().Set(
v8::Number::New(isolate, async_hooks_wrapper_->GetTriggerAsyncId()));
}
void WriteToFile(FILE* file, const v8::FunctionCallbackInfo<v8::Value>& args) {
for (int i = 0; i < args.Length(); i++) {
HandleScope handle_scope(args.GetIsolate());
@ -1857,6 +1884,26 @@ Local<ObjectTemplate> Shell::CreateGlobalTemplate(Isolate* isolate) {
.ToLocalChecked(),
os_templ);
if (i::FLAG_expose_async_hooks) {
Local<ObjectTemplate> async_hooks_templ = ObjectTemplate::New(isolate);
async_hooks_templ->Set(
String::NewFromUtf8(isolate, "createHook", NewStringType::kNormal)
.ToLocalChecked(),
FunctionTemplate::New(isolate, AsyncHooksCreateHook));
async_hooks_templ->Set(
String::NewFromUtf8(isolate, "executionAsyncId", NewStringType::kNormal)
.ToLocalChecked(),
FunctionTemplate::New(isolate, AsyncHooksExecutionAsyncId));
async_hooks_templ->Set(
String::NewFromUtf8(isolate, "triggerAsyncId", NewStringType::kNormal)
.ToLocalChecked(),
FunctionTemplate::New(isolate, AsyncHooksTriggerAsyncId));
global_template->Set(
String::NewFromUtf8(isolate, "async_hooks", NewStringType::kNormal)
.ToLocalChecked(),
async_hooks_templ);
}
return global_template;
}
@ -1910,6 +1957,10 @@ void Shell::Initialize(Isolate* isolate) {
v8::Isolate::kMessageError | v8::Isolate::kMessageWarning |
v8::Isolate::kMessageInfo | v8::Isolate::kMessageDebug |
v8::Isolate::kMessageLog);
if (i::FLAG_expose_async_hooks) {
async_hooks_wrapper_ = new AsyncHooks(isolate);
}
}
@ -2058,6 +2109,10 @@ void Shell::WriteLcovData(v8::Isolate* isolate, const char* file) {
}
void Shell::OnExit(v8::Isolate* isolate) {
if (i::FLAG_expose_async_hooks) {
delete async_hooks_wrapper_; // This uses the isolate
}
// Dump basic block profiling data.
if (i::BasicBlockProfiler* profiler =
reinterpret_cast<i::Isolate*>(isolate)->basic_block_profiler()) {

View File

@ -13,6 +13,7 @@
#include <vector>
#include "src/allocation.h"
#include "src/async-hooks-wrapper.h"
#include "src/base/platform/time.h"
#include "src/string-hasher.h"
#include "src/utils.h"
@ -400,6 +401,14 @@ class Shell : public i::AllStatic {
Local<Value> value,
const PropertyCallbackInfo<void>& info);
static void AsyncHooksCreateHook(
const v8::FunctionCallbackInfo<v8::Value>& args);
static void AsyncHooksExecutionAsyncId(
const v8::FunctionCallbackInfo<v8::Value>& args);
static void AsyncHooksTriggerAsyncId(
const v8::FunctionCallbackInfo<v8::Value>& args);
static AsyncHooks* GetAsyncHooks() { return async_hooks_wrapper_; }
static void Print(const v8::FunctionCallbackInfo<v8::Value>& args);
static void PrintErr(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Write(const v8::FunctionCallbackInfo<v8::Value>& args);
@ -494,6 +503,8 @@ class Shell : public i::AllStatic {
static std::vector<Worker*> workers_;
static std::vector<ExternalizedContents> externalized_contents_;
static AsyncHooks* async_hooks_wrapper_;
static void WriteIgnitionDispatchCountersFile(v8::Isolate* isolate);
// Append LCOV coverage data to file.
static void WriteLcovData(v8::Isolate* isolate, const char* file);

View File

@ -863,6 +863,7 @@ DEFINE_BOOL(enable_experimental_builtins, false,
"enable new csa-based experimental builtins")
DEFINE_BOOL(disallow_code_generation_from_strings, false,
"disallow eval and friends")
DEFINE_BOOL(expose_async_hooks, false, "expose async_hooks object")
// builtins.cc
DEFINE_BOOL(allow_unsafe_function_constructor, false,

View File

@ -258,6 +258,12 @@ assertKind(elements_kind.fast, obj);
obj = newarraycase_list_smiobj(2);
assertKind(elements_kind.fast, obj);
// Perform a gc because without it the test below can experience an
// allocation failure at an inconvenient point. Allocation mementos get
// cleared on gc, and they can't deliver elements kind feedback when that
// happens.
gc();
// Case: array constructor calls with out of date feedback.
// The boilerplate should incorporate all feedback, but the input array
// should be minimally transitioned based on immediate need.

View File

@ -0,0 +1,68 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --expose-async-hooks
// Check for correct API methods
(function() {
assertTrue(async_hooks.hasOwnProperty('createHook'),
'Async hooks missing createHook method');
assertTrue(async_hooks.hasOwnProperty('executionAsyncId'),
'Async hooks missing executionAsyncId method');
assertTrue(async_hooks.hasOwnProperty('triggerAsyncId'),
'Async hooks missing triggerAsyncId method');
let ah = async_hooks.createHook({});
assertTrue(ah.hasOwnProperty('enable'), 'Async hooks missing enable method');
assertTrue(ah.hasOwnProperty('disable'),
'Async hooks missing disable method');
})();
// Check for correct enabling/disabling of async hooks
(function() {
let storedPromise;
let ah = async_hooks.createHook({
init(asyncId, type, triggerAsyncId, resource) {
storedPromise = resource.promise || resource;
}
});
ah.enable();
let createdPromise = new Promise(function(resolve) {
resolve(42);
});
assertSame(storedPromise, createdPromise,
"Async hooks weren't enabled correctly");
ah.disable();
createdPromise = Promise.resolve(52);
assertNotSame(storedPromise, createdPromise,
"Async hooks weren't disabled correctly");
ah.enable();
createdPromise = Promise.resolve(62);
assertSame(storedPromise, createdPromise,
"Async hooks weren't enabled correctly");
})();

View File

@ -0,0 +1,74 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --expose-async-hooks
// Check for async/await asyncIds relation
(function() {
let asyncIds = [], triggerIds = [];
let ah = async_hooks.createHook({
init(asyncId, type, triggerAsyncId, resource) {
if (type !== 'PROMISE') {
return;
}
asyncIds.push(asyncId);
triggerIds.push(triggerAsyncId);
},
});
ah.enable();
// Simplified version of Node.js util.promisify(setTimeout)
function sleep(callback, timeout) {
const promise = new Promise(function(resolve, reject) {
try {
setTimeout((err, ...values) => {
if (err) {
reject(err);
} else {
resolve(values[0]);
}
}, timeout);
} catch (err) {
reject(err);
}
});
return promise;
}
async function foo() {
await sleep(10);
}
foo().then(function() {
assertEquals(asyncIds.length, 6);
assertEquals(triggerIds.length, 6);
assertEquals(triggerIds[2], asyncIds[0]);
assertEquals(triggerIds[3], asyncIds[2]);
assertEquals(triggerIds[4], asyncIds[0]);
assertEquals(triggerIds[5], asyncIds[1]);
});
})();

View File

@ -0,0 +1,48 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --expose-async-hooks
// Check for chained promises asyncIds relation
(function() {
let asyncIds = [], triggerIds = [];
let ah = async_hooks.createHook({
init(asyncId, type, triggerAsyncId, resource) {
asyncIds.push(asyncId);
triggerIds.push(triggerAsyncId);
},
});
ah.enable();
let createdPromise = new Promise(function(resolve) {
resolve(42);
}).then(function() {
assertEquals(asyncIds.length, 2, 'Exactly 2 promises should be inited');
assertEquals(triggerIds.length, 2, 'Exactly 2 promises should be inited');
assertEquals(triggerIds[1], asyncIds[0],
"Parent promise asyncId doesn't correspond to child triggerAsyncId");
});
})();

View File

@ -0,0 +1,65 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --expose-async-hooks
// Check for correct execution of available hooks and asyncIds
(function() {
let inited = false, resolved = false, before = false, after = false;
let storedAsyncId;
let ah = async_hooks.createHook({
init(asyncId, type, triggerAsyncId, resource) {
if (type !== 'PROMISE') {
return;
}
inited = true;
storedAsyncId = asyncId;
},
promiseResolve(asyncId) {
assertEquals(asyncId, storedAsyncId, 'AsyncId mismatch in resolve hook');
resolved = true;
},
before(asyncId) {
assertEquals(asyncId, storedAsyncId, 'AsyncId mismatch in before hook');
before = true;
},
after(asyncId) {
assertEquals(asyncId, storedAsyncId, 'AsyncId mismatch in after hook');
after = true;
},
});
ah.enable();
new Promise(function(resolve) {
resolve(42);
}).then(function() {
assertTrue(inited, "Didn't call init hook");
assertTrue(resolved, "Didn't call resolve hook");
assertTrue(before, "Didn't call before hook before the callback");
assertFalse(after, "Called after hook before the callback");
});
})();

View File

@ -0,0 +1,70 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --expose-async-hooks
// Check for executionAsyncId/triggerAsyncId when chained promises and
// async/await are combined
(function() {
let p;
let outerExecutionAsyncId = -1, outerTriggerAsyncId = -1;
function inIrrelevantContext(resolve) {
resolve(42);
}
function inContext1(foo) {
foo();
}
function inContext2(foo) {
foo();
}
outerExecutionAsyncId = async_hooks.executionAsyncId();
outerTriggerAsyncId = async_hooks.triggerAsyncId();
inContext1(() => {
p = new Promise(resolve => {
assertEquals(outerExecutionAsyncId, async_hooks.executionAsyncId());
assertEquals(outerTriggerAsyncId, async_hooks.triggerAsyncId());
inIrrelevantContext(resolve);
}).then(() => {
assertNotEquals(outerExecutionAsyncId, async_hooks.executionAsyncId());
assertNotEquals(outerTriggerAsyncId, async_hooks.triggerAsyncId());
});
});
inContext2(async () => {
assertEquals(outerExecutionAsyncId, async_hooks.executionAsyncId());
assertEquals(outerTriggerAsyncId, async_hooks.triggerAsyncId());
await p;
assertNotEquals(outerExecutionAsyncId, async_hooks.executionAsyncId());
assertNotEquals(outerTriggerAsyncId, async_hooks.triggerAsyncId());
});
})();

View File

@ -0,0 +1,61 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Check for correct interleaving of Promises and async/await
(function () {
const iterations = 10;
let promiseCounter = iterations;
let awaitCounter = 0;
async function check(v) {
awaitCounter = v;
// The following checks ensure that "await" takes 3 ticks on the
// microtask queue. Note: this will change in the future
if (awaitCounter === 0) {
assertEquals(iterations, promiseCounter);
} else if (awaitCounter <= Math.floor(iterations / 3)) {
assertEquals(iterations - awaitCounter * 3, promiseCounter);
} else {
assertEquals(0, promiseCounter);
}
}
async function f() {
for (let i = 0; i < iterations; i++) {
await check(i);
}
return 0;
}
function countdown(v) {
promiseCounter = v;
if (v > 0) Promise.resolve(v - 1).then(countdown);
}
countdown(iterations);
f();
})();