Reland ^2 "[async] Expose async hooks to d8"
This is a reland of8e0f67be3f
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:
parent
476a885343
commit
ade7f55b3a
2
BUILD.gn
2
BUILD.gn
@ -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
245
src/async-hooks-wrapper.cc
Normal 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
95
src/async-hooks-wrapper.h
Normal 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_
|
55
src/d8.cc
55
src/d8.cc
@ -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()) {
|
||||
|
11
src/d8.h
11
src/d8.h
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
68
test/mjsunit/async-hooks/api-methods.js
Normal file
68
test/mjsunit/async-hooks/api-methods.js
Normal 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");
|
||||
})();
|
74
test/mjsunit/async-hooks/async-await-tree.js
Normal file
74
test/mjsunit/async-hooks/async-await-tree.js
Normal 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]);
|
||||
});
|
||||
})();
|
48
test/mjsunit/async-hooks/chained-promises.js
Normal file
48
test/mjsunit/async-hooks/chained-promises.js
Normal 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");
|
||||
});
|
||||
})();
|
65
test/mjsunit/async-hooks/execution-order.js
Normal file
65
test/mjsunit/async-hooks/execution-order.js
Normal 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");
|
||||
});
|
||||
})();
|
70
test/mjsunit/async-hooks/promises-async-await.js
Normal file
70
test/mjsunit/async-hooks/promises-async-await.js
Normal 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());
|
||||
});
|
||||
|
||||
})();
|
61
test/mjsunit/es8/async-await-interleaved.js
Normal file
61
test/mjsunit/es8/async-await-interleaved.js
Normal 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();
|
||||
})();
|
Loading…
Reference in New Issue
Block a user