Skip to content

Commit

Permalink
src: move CallbackScope to separate cc/h
Browse files Browse the repository at this point in the history
PR-URL: #20789
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
  • Loading branch information
jasnell authored and MylesBorins committed May 22, 2018
1 parent 2e99576 commit 36d4a42
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 147 deletions.
2 changes: 2 additions & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@

'sources': [
'src/async_wrap.cc',
'src/callback_scope.cc',
'src/cares_wrap.cc',
'src/connection_wrap.cc',
'src/connect_wrap.cc',
Expand Down Expand Up @@ -363,6 +364,7 @@
'src/async_wrap-inl.h',
'src/base_object.h',
'src/base_object-inl.h',
'src/callback_scope.h',
'src/connection_wrap.h',
'src/connect_wrap.h',
'src/env.h',
Expand Down
126 changes: 126 additions & 0 deletions src/callback_scope.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#include "node.h"
#include "callback_scope.h"
#include "async_wrap.h"
#include "async_wrap-inl.h"
#include "env.h"
#include "env-inl.h"
#include "v8.h"

namespace node {

using v8::HandleScope;
using v8::Isolate;
using v8::Local;
using v8::Object;

using AsyncHooks = Environment::AsyncHooks;

CallbackScope::CallbackScope(Isolate* isolate,
Local<Object> object,
async_context asyncContext)
: private_(new InternalCallbackScope(Environment::GetCurrent(isolate),
object,
asyncContext)),
try_catch_(isolate) {
try_catch_.SetVerbose(true);
}

CallbackScope::~CallbackScope() {
if (try_catch_.HasCaught())
private_->MarkAsFailed();
delete private_;
}

InternalCallbackScope::InternalCallbackScope(AsyncWrap* async_wrap)
: InternalCallbackScope(async_wrap->env(),
async_wrap->object(),
{ async_wrap->get_async_id(),
async_wrap->get_trigger_async_id() }) {}

InternalCallbackScope::InternalCallbackScope(Environment* env,
Local<Object> object,
const async_context& asyncContext,
ResourceExpectation expect)
: env_(env),
async_context_(asyncContext),
object_(object),
callback_scope_(env) {
if (expect == kRequireResource) {
CHECK(!object.IsEmpty());
}

if (!env->can_call_into_js()) {
failed_ = true;
return;
}

HandleScope handle_scope(env->isolate());
// If you hit this assertion, you forgot to enter the v8::Context first.
CHECK_EQ(Environment::GetCurrent(env->isolate()), env);

if (asyncContext.async_id != 0) {
// No need to check a return value because the application will exit if
// an exception occurs.
AsyncWrap::EmitBefore(env, asyncContext.async_id);
}

if (!IsInnerMakeCallback()) {
env->tick_info()->set_has_thrown(false);
}

env->async_hooks()->push_async_ids(async_context_.async_id,
async_context_.trigger_async_id);
pushed_ids_ = true;
}

InternalCallbackScope::~InternalCallbackScope() {
Close();
}

void InternalCallbackScope::Close() {
if (closed_) return;
closed_ = true;
HandleScope handle_scope(env_->isolate());

if (pushed_ids_)
env_->async_hooks()->pop_async_id(async_context_.async_id);

if (failed_) return;

if (async_context_.async_id != 0) {
AsyncWrap::EmitAfter(env_, async_context_.async_id);
}

if (IsInnerMakeCallback()) {
return;
}

Environment::TickInfo* tick_info = env_->tick_info();

if (!env_->can_call_into_js()) return;
if (!tick_info->has_scheduled()) {
env_->isolate()->RunMicrotasks();
}

// Make sure the stack unwound properly. If there are nested MakeCallback's
// then it should return early and not reach this code.
if (env_->async_hooks()->fields()[AsyncHooks::kTotals]) {
CHECK_EQ(env_->execution_async_id(), 0);
CHECK_EQ(env_->trigger_async_id(), 0);
}

if (!tick_info->has_scheduled() && !tick_info->has_promise_rejections()) {
return;
}

Local<Object> process = env_->process_object();

if (!env_->can_call_into_js()) return;

if (env_->tick_callback_function()->Call(process, 0, nullptr).IsEmpty()) {
env_->tick_info()->set_has_thrown(true);
failed_ = true;
}
}

} // namespace node
56 changes: 56 additions & 0 deletions src/callback_scope.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#ifndef SRC_CALLBACK_SCOPE_H_
#define SRC_CALLBACK_SCOPE_H_

#ifdef _WIN32
# ifndef BUILDING_NODE_EXTENSION
# define NODE_EXTERN __declspec(dllexport)
# else
# define NODE_EXTERN __declspec(dllimport)
# endif
#else
# define NODE_EXTERN /* nothing */
#endif

#include "v8.h"

namespace node {

typedef double async_id;
struct async_context {
::node::async_id async_id;
::node::async_id trigger_async_id;
};

class InternalCallbackScope;

/* This class works like `MakeCallback()` in that it sets up a specific
* asyncContext as the current one and informs the async_hooks and domains
* modules that this context is currently active.
*
* `MakeCallback()` is a wrapper around this class as well as
* `Function::Call()`. Either one of these mechanisms needs to be used for
* top-level calls into JavaScript (i.e. without any existing JS stack).
*
* This object should be stack-allocated to ensure that it is contained in a
* valid HandleScope.
*/
class NODE_EXTERN CallbackScope {
public:
CallbackScope(v8::Isolate* isolate,
v8::Local<v8::Object> resource,
async_context asyncContext);
~CallbackScope();

private:
InternalCallbackScope* private_;
v8::TryCatch try_catch_;

void operator=(const CallbackScope&) = delete;
void operator=(CallbackScope&&) = delete;
CallbackScope(const CallbackScope&) = delete;
CallbackScope(CallbackScope&&) = delete;
};

} // namespace node

#endif // SRC_CALLBACK_SCOPE_H_
111 changes: 0 additions & 111 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,6 @@ using v8::Undefined;
using v8::V8;
using v8::Value;

using AsyncHooks = Environment::AsyncHooks;

static Mutex process_mutex;
static Mutex environ_mutex;

Expand Down Expand Up @@ -923,115 +921,6 @@ void RemoveEnvironmentCleanupHook(v8::Isolate* isolate,
env->RemoveCleanupHook(fun, arg);
}


CallbackScope::CallbackScope(Isolate* isolate,
Local<Object> object,
async_context asyncContext)
: private_(new InternalCallbackScope(Environment::GetCurrent(isolate),
object,
asyncContext)),
try_catch_(isolate) {
try_catch_.SetVerbose(true);
}

CallbackScope::~CallbackScope() {
if (try_catch_.HasCaught())
private_->MarkAsFailed();
delete private_;
}

InternalCallbackScope::InternalCallbackScope(AsyncWrap* async_wrap)
: InternalCallbackScope(async_wrap->env(),
async_wrap->object(),
{ async_wrap->get_async_id(),
async_wrap->get_trigger_async_id() }) {}

InternalCallbackScope::InternalCallbackScope(Environment* env,
Local<Object> object,
const async_context& asyncContext,
ResourceExpectation expect)
: env_(env),
async_context_(asyncContext),
object_(object),
callback_scope_(env) {
if (expect == kRequireResource) {
CHECK(!object.IsEmpty());
}

if (!env->can_call_into_js()) {
failed_ = true;
return;
}

HandleScope handle_scope(env->isolate());
// If you hit this assertion, you forgot to enter the v8::Context first.
CHECK_EQ(Environment::GetCurrent(env->isolate()), env);

if (asyncContext.async_id != 0) {
// No need to check a return value because the application will exit if
// an exception occurs.
AsyncWrap::EmitBefore(env, asyncContext.async_id);
}

if (!IsInnerMakeCallback()) {
env->tick_info()->set_has_thrown(false);
}

env->async_hooks()->push_async_ids(async_context_.async_id,
async_context_.trigger_async_id);
pushed_ids_ = true;
}

InternalCallbackScope::~InternalCallbackScope() {
Close();
}

void InternalCallbackScope::Close() {
if (closed_) return;
closed_ = true;
HandleScope handle_scope(env_->isolate());

if (pushed_ids_)
env_->async_hooks()->pop_async_id(async_context_.async_id);

if (failed_) return;

if (async_context_.async_id != 0) {
AsyncWrap::EmitAfter(env_, async_context_.async_id);
}

if (IsInnerMakeCallback()) {
return;
}

Environment::TickInfo* tick_info = env_->tick_info();

if (!env_->can_call_into_js()) return;
if (!tick_info->has_scheduled()) {
env_->isolate()->RunMicrotasks();
}

// Make sure the stack unwound properly. If there are nested MakeCallback's
// then it should return early and not reach this code.
if (env_->async_hooks()->fields()[AsyncHooks::kTotals]) {
CHECK_EQ(env_->execution_async_id(), 0);
CHECK_EQ(env_->trigger_async_id(), 0);
}

if (!tick_info->has_scheduled() && !tick_info->has_promise_rejections()) {
return;
}

Local<Object> process = env_->process_object();

if (!env_->can_call_into_js()) return;

if (env_->tick_callback_function()->Call(process, 0, nullptr).IsEmpty()) {
env_->tick_info()->set_has_thrown(true);
failed_ = true;
}
}

MaybeLocal<Value> InternalMakeCallback(Environment* env,
Local<Object> recv,
const Local<Function> callback,
Expand Down
37 changes: 1 addition & 36 deletions src/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
#include "v8.h" // NOLINT(build/include_order)
#include "v8-platform.h" // NOLINT(build/include_order)
#include "node_version.h" // NODE_MODULE_VERSION
#include "callback_scope.h"

#define NODE_MAKE_VERSION(major, minor, patch) \
((major) * 0x1000 + (minor) * 0x100 + (patch))
Expand Down Expand Up @@ -593,12 +594,6 @@ typedef void (*promise_hook_func) (v8::PromiseHookType type,
v8::Local<v8::Value> parent,
void* arg);

typedef double async_id;
struct async_context {
::node::async_id async_id;
::node::async_id trigger_async_id;
};

/* Registers an additional v8::PromiseHook wrapper. This API exists because V8
* itself supports only a single PromiseHook. */
NODE_EXTERN void AddPromiseHook(v8::Isolate* isolate,
Expand Down Expand Up @@ -647,36 +642,6 @@ NODE_EXTERN async_context EmitAsyncInit(v8::Isolate* isolate,
NODE_EXTERN void EmitAsyncDestroy(v8::Isolate* isolate,
async_context asyncContext);

class InternalCallbackScope;

/* This class works like `MakeCallback()` in that it sets up a specific
* asyncContext as the current one and informs the async_hooks and domains
* modules that this context is currently active.
*
* `MakeCallback()` is a wrapper around this class as well as
* `Function::Call()`. Either one of these mechanisms needs to be used for
* top-level calls into JavaScript (i.e. without any existing JS stack).
*
* This object should be stack-allocated to ensure that it is contained in a
* valid HandleScope.
*/
class NODE_EXTERN CallbackScope {
public:
CallbackScope(v8::Isolate* isolate,
v8::Local<v8::Object> resource,
async_context asyncContext);
~CallbackScope();

private:
InternalCallbackScope* private_;
v8::TryCatch try_catch_;

void operator=(const CallbackScope&) = delete;
void operator=(CallbackScope&&) = delete;
CallbackScope(const CallbackScope&) = delete;
CallbackScope(CallbackScope&&) = delete;
};

/* An API specific to emit before/after callbacks is unnecessary because
* MakeCallback will automatically call them for you.
*
Expand Down

0 comments on commit 36d4a42

Please sign in to comment.