Browse Source

deps: re-implement debugger-agent

Reviewed-By: Trevor Norris <trevnorris@gmail.com>
PR-URL: https://github.com/joyent/node/pull/8476
archived-io.js-v0.12
Fedor Indutny 10 years ago
parent
commit
6a610a0f67
  1. 2
      Makefile
  2. 24
      deps/debugger-agent/debugger-agent.gyp
  3. 109
      deps/debugger-agent/include/debugger-agent.h
  4. 191
      deps/debugger-agent/lib/_debugger_agent.js
  5. 347
      deps/debugger-agent/src/agent.cc
  6. 64
      deps/debugger-agent/src/agent.h
  7. 4
      lib/_debugger.js
  8. 2
      node.gyp
  9. 18
      src/cares_wrap.cc
  10. 50
      src/env-inl.h
  11. 52
      src/env.h
  12. 5
      src/handle_wrap.cc
  13. 202
      src/node.cc
  14. 17
      src/node.h
  15. 10
      src/node.js
  16. 5
      src/req_wrap.h
  17. 0
      test/disabled/test-debug-brk-no-arg.js

2
Makefile

@ -406,7 +406,7 @@ CPPLINT_EXCLUDE += src/queue.h
CPPLINT_EXCLUDE += src/tree.h CPPLINT_EXCLUDE += src/tree.h
CPPLINT_EXCLUDE += src/v8abbr.h CPPLINT_EXCLUDE += src/v8abbr.h
CPPLINT_FILES = $(filter-out $(CPPLINT_EXCLUDE), $(wildcard src/*.cc src/*.h src/*.c tools/icu/*.h tools/icu/*.cc)) CPPLINT_FILES = $(filter-out $(CPPLINT_EXCLUDE), $(wildcard src/*.cc src/*.h src/*.c tools/icu/*.h tools/icu/*.cc deps/debugger-agent/include/* deps/debugger-agent/src/*))
cpplint: cpplint:
@$(PYTHON) tools/cpplint.py $(CPPLINT_FILES) @$(PYTHON) tools/cpplint.py $(CPPLINT_FILES)

24
deps/debugger-agent/debugger-agent.gyp

@ -0,0 +1,24 @@
{
"targets": [{
"target_name": "debugger-agent",
"type": "<(library)",
"include_dirs": [
"src",
"include",
"../v8/include",
"../uv/include",
# Private node.js folder and stuff needed to include from it
"../../src",
"../cares/include",
],
"direct_dependent_settings": {
"include_dirs": [
"include",
],
},
"sources": [
"src/agent.cc",
],
}],
}

109
deps/debugger-agent/include/debugger-agent.h

@ -0,0 +1,109 @@
// Copyright Fedor Indutny and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifndef DEPS_DEBUGGER_AGENT_INCLUDE_DEBUGGER_AGENT_H_
#define DEPS_DEBUGGER_AGENT_INCLUDE_DEBUGGER_AGENT_H_
#include "uv.h"
#include "v8.h"
#include "v8-debug.h"
namespace node {
// Forward declaration
class Environment;
namespace debugger {
// Forward declaration
class AgentMessage;
class Agent {
public:
explicit Agent(node::Environment* env);
~Agent();
typedef void (*DispatchHandler)(node::Environment* env);
// Start the debugger agent thread
bool Start(int port, bool wait);
// Listen for debug events
void Enable();
// Stop the debugger agent
void Stop();
inline void set_dispatch_handler(DispatchHandler handler) {
dispatch_handler_ = handler;
}
inline node::Environment* parent_env() const { return parent_env_; }
inline node::Environment* child_env() const { return child_env_; }
protected:
void InitAdaptor(Environment* env);
// Worker body
void WorkerRun();
static void ThreadCb(Agent* agent);
static void ParentSignalCb(uv_async_t* signal);
static void ChildSignalCb(uv_async_t* signal);
static void MessageHandler(const v8::Debug::Message& message);
// V8 API
static Agent* Unwrap(const v8::FunctionCallbackInfo<v8::Value>& args);
static void NotifyListen(const v8::FunctionCallbackInfo<v8::Value>& args);
static void NotifyWait(const v8::FunctionCallbackInfo<v8::Value>& args);
static void SendCommand(const v8::FunctionCallbackInfo<v8::Value>& args);
void EnqueueMessage(AgentMessage* message);
enum State {
kNone,
kRunning
};
// TODO(indutny): Verify that there are no races
State state_;
int port_;
bool wait_;
uv_sem_t start_sem_;
uv_mutex_t message_mutex_;
uv_async_t child_signal_;
uv_thread_t thread_;
node::Environment* parent_env_;
node::Environment* child_env_;
uv_loop_t child_loop_;
v8::Persistent<v8::Object> api_;
// QUEUE
void* messages_[2];
DispatchHandler dispatch_handler_;
};
} // namespace debugger
} // namespace node
#endif // DEPS_DEBUGGER_AGENT_INCLUDE_DEBUGGER_AGENT_H_

191
deps/debugger-agent/lib/_debugger_agent.js

@ -0,0 +1,191 @@
var assert = require('assert');
var net = require('net');
var util = require('util');
var Buffer = require('buffer').Buffer;
var Transform = require('stream').Transform;
exports.start = function start() {
var agent = new Agent();
// Do not let `agent.listen()` request listening from cluster master
var cluster = require('cluster');
cluster.isWorker = false;
cluster.isMaster = true;
agent.on('error', function(err) {
process._rawDebug(err.stack || err);
});
agent.listen(process._debugAPI.port, function() {
var addr = this.address();
process._rawDebug('Debugger listening on port %d', addr.port);
process._debugAPI.notifyListen();
});
// Just to spin-off events
// TODO(indutny): Figure out why node.cc isn't doing this
setImmediate(function() {
});
process._debugAPI.onclose = function() {
// We don't care about it, but it prevents loop from cleaning up gently
// NOTE: removeAllListeners won't work, as it doesn't call `removeListener`
process.listeners('SIGWINCH').forEach(function(fn) {
process.removeListener('SIGWINCH', fn);
});
agent.close();
};
// Not used now, but anyway
return agent;
};
function Agent() {
net.Server.call(this, this.onConnection);
this.first = true;
this.binding = process._debugAPI;
var self = this;
this.binding.onmessage = function(msg) {
self.clients.forEach(function(client) {
client.send({}, msg);
});
};
this.clients = [];
assert(this.binding, 'Debugger agent running without bindings!');
}
util.inherits(Agent, net.Server);
Agent.prototype.onConnection = function onConnection(socket) {
var c = new Client(this, socket);
c.start();
this.clients.push(c);
var self = this;
c.once('close', function() {
var index = self.clients.indexOf(c);
assert(index !== -1);
self.clients.splice(index, 1);
});
};
Agent.prototype.notifyWait = function notifyWait() {
if (this.first)
this.binding.notifyWait();
this.first = false;
};
function Client(agent, socket) {
Transform.call(this);
this._readableState.objectMode = true;
this.agent = agent;
this.binding = this.agent.binding;
this.socket = socket;
// Parse incoming data
this.state = 'headers';
this.headers = {};
this.buffer = '';
socket.pipe(this);
this.on('data', this.onCommand);
var self = this;
this.socket.on('close', function() {
self.destroy();
});
}
util.inherits(Client, Transform);
Client.prototype.destroy = function destroy(msg) {
this.socket.destroy();
this.emit('close');
};
Client.prototype._transform = function _transform(data, enc, cb) {
cb();
this.buffer += data;
while (true) {
if (this.state === 'headers') {
// Not enough data
if (!/\r\n/.test(this.buffer))
break;
if (/^\r\n/.test(this.buffer)) {
this.buffer = this.buffer.slice(2);
this.state = 'body';
continue;
}
// Match:
// Header-name: header-value\r\n
var match = this.buffer.match(/^([^:\s\r\n]+)\s*:\s*([^\s\r\n]+)\r\n/);
if (!match)
return this.destroy('Expected header, but failed to parse it');
this.headers[match[1].toLowerCase()] = match[2];
this.buffer = this.buffer.slice(match[0].length);
} else {
var len = this.headers['content-length'];
if (len === undefined)
return this.destroy('Expected content-length');
len = len | 0;
if (Buffer.byteLength(this.buffer) < len)
break;
this.push(new Command(this.headers, this.buffer.slice(0, len)));
this.state = 'headers';
this.buffer = this.buffer.slice(len);
this.headers = {};
}
}
};
Client.prototype.send = function send(headers, data) {
if (!data)
data = '';
var out = [];
Object.keys(headers).forEach(function(key) {
out.push(key + ': ' + headers[key]);
});
out.push('Content-Length: ' + Buffer.byteLength(data), '');
this.socket.cork();
this.socket.write(out.join('\r\n') + '\r\n');
if (data.length > 0)
this.socket.write(data);
this.socket.uncork();
};
Client.prototype.start = function start() {
this.send({
Type: 'connect',
'V8-Version': process.versions.v8,
'Protocol-Version': 1,
'Embedding-Host': 'node ' + process.version
});
};
Client.prototype.onCommand = function onCommand(cmd) {
this.binding.sendCommand(cmd.body);
this.agent.notifyWait();
};
function Command(headers, body) {
this.headers = headers;
this.body = body;
}

347
deps/debugger-agent/src/agent.cc

@ -0,0 +1,347 @@
// Copyright Fedor Indutny and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "agent.h"
#include "debugger-agent.h"
#include "node.h"
#include "node_internals.h" // ARRAY_SIZE
#include "env.h"
#include "env-inl.h"
#include "v8.h"
#include "v8-debug.h"
#include "util.h"
#include "util-inl.h"
#include "queue.h"
#include <string.h>
namespace node {
namespace debugger {
using v8::Context;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::Handle;
using v8::HandleScope;
using v8::Integer;
using v8::Isolate;
using v8::Local;
using v8::Locker;
using v8::Object;
using v8::String;
using v8::Value;
Agent::Agent(Environment* env) : state_(kNone),
port_(5858),
wait_(false),
parent_env_(env),
child_env_(NULL),
dispatch_handler_(NULL) {
int err;
err = uv_sem_init(&start_sem_, 0);
CHECK_EQ(err, 0);
err = uv_mutex_init(&message_mutex_);
CHECK_EQ(err, 0);
QUEUE_INIT(&messages_);
}
Agent::~Agent() {
Stop();
uv_sem_destroy(&start_sem_);
uv_mutex_destroy(&message_mutex_);
// Clean-up messages
while (!QUEUE_EMPTY(&messages_)) {
QUEUE* q = QUEUE_HEAD(&messages_);
QUEUE_REMOVE(q);
AgentMessage* msg = ContainerOf(&AgentMessage::member, q);
delete msg;
}
}
bool Agent::Start(int port, bool wait) {
int err;
if (state_ == kRunning)
return false;
err = uv_loop_init(&child_loop_);
if (err != 0)
goto loop_init_failed;
// Interruption signal handler
err = uv_async_init(&child_loop_, &child_signal_, ChildSignalCb);
if (err != 0)
goto async_init_failed;
uv_unref(reinterpret_cast<uv_handle_t*>(&child_signal_));
port_ = port;
wait_ = wait;
err = uv_thread_create(&thread_,
reinterpret_cast<uv_thread_cb>(ThreadCb),
this);
if (err != 0)
goto thread_create_failed;
uv_sem_wait(&start_sem_);
state_ = kRunning;
return true;
thread_create_failed:
uv_close(reinterpret_cast<uv_handle_t*>(&child_signal_), NULL);
async_init_failed:
err = uv_loop_close(&child_loop_);
CHECK_EQ(err, 0);
loop_init_failed:
return false;
}
void Agent::Enable() {
v8::Debug::SetMessageHandler(MessageHandler);
// Assign environment to the debugger's context
// NOTE: The debugger context is created after `SetMessageHandler()` call
parent_env()->AssignToContext(v8::Debug::GetDebugContext());
}
void Agent::Stop() {
int err;
if (state_ != kRunning) {
return;
}
v8::Debug::SetMessageHandler(NULL);
// Send empty message to terminate things
EnqueueMessage(new AgentMessage(NULL, 0));
// Signal worker thread to make it stop
err = uv_async_send(&child_signal_);
CHECK_EQ(err, 0);
err = uv_thread_join(&thread_);
CHECK_EQ(err, 0);
uv_close(reinterpret_cast<uv_handle_t*>(&child_signal_), NULL);
uv_run(&child_loop_, UV_RUN_NOWAIT);
err = uv_loop_close(&child_loop_);
CHECK_EQ(err, 0);
state_ = kNone;
}
void Agent::WorkerRun() {
static const char* argv[] = { "node", "--debug-agent" };
Isolate* isolate = Isolate::New();
{
Locker locker(isolate);
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);
Local<Context> context = Context::New(isolate);
Context::Scope context_scope(context);
Environment* env = CreateEnvironment(
isolate,
&child_loop_,
context,
ARRAY_SIZE(argv),
argv,
ARRAY_SIZE(argv),
argv);
child_env_ = env;
// Expose API
InitAdaptor(env);
LoadEnvironment(env);
CHECK_EQ(&child_loop_, env->event_loop());
uv_run(&child_loop_, UV_RUN_DEFAULT);
// Clean-up peristent
api_.Reset();
// Clean-up all running handles
env->CleanupHandles();
env->Dispose();
env = NULL;
}
isolate->Dispose();
}
void Agent::InitAdaptor(Environment* env) {
Isolate* isolate = env->isolate();
HandleScope scope(isolate);
// Create API adaptor
Local<FunctionTemplate> t = FunctionTemplate::New(isolate);
t->InstanceTemplate()->SetInternalFieldCount(1);
t->SetClassName(String::NewFromUtf8(isolate, "DebugAPI"));
NODE_SET_PROTOTYPE_METHOD(t, "notifyListen", NotifyListen);
NODE_SET_PROTOTYPE_METHOD(t, "notifyWait", NotifyWait);
NODE_SET_PROTOTYPE_METHOD(t, "sendCommand", SendCommand);
Local<Object> api = t->GetFunction()->NewInstance();
api->SetAlignedPointerInInternalField(0, this);
api->Set(String::NewFromUtf8(isolate, "port"), Integer::New(isolate, port_));
env->process_object()->Set(String::NewFromUtf8(isolate, "_debugAPI"), api);
api_.Reset(env->isolate(), api);
}
Agent* Agent::Unwrap(const v8::FunctionCallbackInfo<v8::Value>& args) {
void* ptr = args.Holder()->GetAlignedPointerFromInternalField(0);
return reinterpret_cast<Agent*>(ptr);
}
void Agent::NotifyListen(const FunctionCallbackInfo<Value>& args) {
Agent* a = Unwrap(args);
// Notify other thread that we are ready to process events
uv_sem_post(&a->start_sem_);
}
void Agent::NotifyWait(const FunctionCallbackInfo<Value>& args) {
Agent* a = Unwrap(args);
a->wait_ = false;
int err = uv_async_send(&a->child_signal_);
CHECK_EQ(err, 0);
}
void Agent::SendCommand(const FunctionCallbackInfo<Value>& args) {
Agent* a = Unwrap(args);
Environment* env = a->child_env();
HandleScope scope(env->isolate());
String::Value v(args[0]);
v8::Debug::SendCommand(a->parent_env()->isolate(), *v, v.length());
if (a->dispatch_handler_ != NULL)
a->dispatch_handler_(a->parent_env());
}
void Agent::ThreadCb(Agent* agent) {
agent->WorkerRun();
}
void Agent::ChildSignalCb(uv_async_t* signal) {
Agent* a = ContainerOf(&Agent::child_signal_, signal);
Isolate* isolate = a->child_env()->isolate();
HandleScope scope(isolate);
Local<Object> api = PersistentToLocal(isolate, a->api_);
uv_mutex_lock(&a->message_mutex_);
while (!QUEUE_EMPTY(&a->messages_)) {
QUEUE* q = QUEUE_HEAD(&a->messages_);
AgentMessage* msg = ContainerOf(&AgentMessage::member, q);
// Time to close everything
if (msg->data() == NULL) {
QUEUE_REMOVE(q);
delete msg;
MakeCallback(isolate, api, "onclose", 0, NULL);
break;
}
// Waiting for client, do not send anything just yet
// TODO(indutny): move this to js-land
if (a->wait_)
break;
QUEUE_REMOVE(q);
Local<Value> argv[] = {
String::NewFromTwoByte(isolate,
msg->data(),
String::kNormalString,
msg->length())
};
// Emit message
MakeCallback(isolate,
api,
"onmessage",
ARRAY_SIZE(argv),
argv);
delete msg;
}
uv_mutex_unlock(&a->message_mutex_);
}
void Agent::EnqueueMessage(AgentMessage* message) {
uv_mutex_lock(&message_mutex_);
QUEUE_INSERT_TAIL(&messages_, &message->member);
uv_mutex_unlock(&message_mutex_);
uv_async_send(&child_signal_);
}
void Agent::MessageHandler(const v8::Debug::Message& message) {
Isolate* isolate = message.GetIsolate();
Environment* env = Environment::GetCurrent(isolate);
Agent* a = env->debugger_agent();
CHECK_NE(a, NULL);
CHECK_EQ(isolate, a->parent_env()->isolate());
HandleScope scope(isolate);
Local<String> json = message.GetJSON();
String::Value v(json);
AgentMessage* msg = new AgentMessage(*v, v.length());
a->EnqueueMessage(msg);
}
} // namespace debugger
} // namespace node

64
deps/debugger-agent/src/agent.h

@ -0,0 +1,64 @@
// Copyright Fedor Indutny and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifndef DEPS_DEBUGGER_AGENT_SRC_AGENT_H_
#define DEPS_DEBUGGER_AGENT_SRC_AGENT_H_
#include "v8.h"
#include "v8-debug.h"
#include "queue.h"
#include <assert.h>
#include <string.h>
namespace node {
namespace debugger {
class AgentMessage {
public:
AgentMessage(uint16_t* val, int length) : length_(length) {
if (val == NULL) {
data_ = val;
} else {
data_ = new uint16_t[length];
memcpy(data_, val, length * sizeof(*data_));
}
}
~AgentMessage() {
delete[] data_;
data_ = NULL;
}
inline const uint16_t* data() const { return data_; }
inline int length() const { return length_; }
QUEUE member;
private:
uint16_t* data_;
int length_;
};
} // namespace debugger
} // namespace node
#endif // DEPS_DEBUGGER_AGENT_SRC_AGENT_H_

4
lib/_debugger.js

@ -249,6 +249,10 @@ Client.prototype._onResponse = function(res) {
this._removeScript(res.body.body.script); this._removeScript(res.body.body.script);
handled = true; handled = true;
} else if (res.body && res.body.event === 'compileError') {
// This event is not used anywhere right now, perhaps somewhere in the
// future?
handled = true;
} }
if (cb) { if (cb) {

2
node.gyp

@ -67,6 +67,7 @@
'lib/util.js', 'lib/util.js',
'lib/vm.js', 'lib/vm.js',
'lib/zlib.js', 'lib/zlib.js',
'deps/debugger-agent/lib/_debugger_agent.js',
], ],
}, },
@ -77,6 +78,7 @@
'dependencies': [ 'dependencies': [
'node_js2c#host', 'node_js2c#host',
'deps/debugger-agent/debugger-agent.gyp:debugger-agent',
], ],
'include_dirs': [ 'include_dirs': [

18
src/cares_wrap.cc

@ -1200,6 +1200,20 @@ static void StrError(const FunctionCallbackInfo<Value>& args) {
} }
static void CaresTimerCloseCb(uv_handle_t* handle) {
Environment* env = Environment::from_cares_timer_handle(
reinterpret_cast<uv_timer_t*>(handle));
env->FinishHandleCleanup(handle);
}
static void CaresTimerClose(Environment* env,
uv_handle_t* handle,
void* arg) {
uv_close(handle, CaresTimerCloseCb);
}
static void Initialize(Handle<Object> target, static void Initialize(Handle<Object> target,
Handle<Value> unused, Handle<Value> unused,
Handle<Context> context) { Handle<Context> context) {
@ -1223,6 +1237,10 @@ static void Initialize(Handle<Object> target,
/* Initialize the timeout timer. The timer won't be started until the */ /* Initialize the timeout timer. The timer won't be started until the */
/* first socket is opened. */ /* first socket is opened. */
uv_timer_init(env->event_loop(), env->cares_timer_handle()); uv_timer_init(env->event_loop(), env->cares_timer_handle());
env->RegisterHandleCleanup(
reinterpret_cast<uv_handle_t*>(env->cares_timer_handle()),
CaresTimerClose,
NULL);
NODE_SET_METHOD(target, "queryA", Query<QueryAWrap>); NODE_SET_METHOD(target, "queryA", Query<QueryAWrap>);
NODE_SET_METHOD(target, "queryAaaa", Query<QueryAaaaWrap>); NODE_SET_METHOD(target, "queryAaaa", Query<QueryAaaaWrap>);

50
src/env-inl.h

@ -74,10 +74,10 @@ inline Environment::IsolateData* Environment::IsolateData::Get(
} }
inline Environment::IsolateData* Environment::IsolateData::GetOrCreate( inline Environment::IsolateData* Environment::IsolateData::GetOrCreate(
v8::Isolate* isolate) { v8::Isolate* isolate, uv_loop_t* loop) {
IsolateData* isolate_data = Get(isolate); IsolateData* isolate_data = Get(isolate);
if (isolate_data == NULL) { if (isolate_data == NULL) {
isolate_data = new IsolateData(isolate); isolate_data = new IsolateData(isolate, loop);
isolate->SetData(kIsolateSlot, isolate_data); isolate->SetData(kIsolateSlot, isolate_data);
} }
isolate_data->ref_count_ += 1; isolate_data->ref_count_ += 1;
@ -91,8 +91,9 @@ inline void Environment::IsolateData::Put() {
} }
} }
inline Environment::IsolateData::IsolateData(v8::Isolate* isolate) inline Environment::IsolateData::IsolateData(v8::Isolate* isolate,
: event_loop_(uv_default_loop()), uv_loop_t* loop)
: event_loop_(loop),
isolate_(isolate), isolate_(isolate),
#define V(PropertyName, StringValue) \ #define V(PropertyName, StringValue) \
PropertyName ## _(isolate, FIXED_ONE_BYTE_STRING(isolate, StringValue)), PropertyName ## _(isolate, FIXED_ONE_BYTE_STRING(isolate, StringValue)),
@ -188,8 +189,9 @@ inline void Environment::TickInfo::set_last_threw(bool value) {
last_threw_ = value; last_threw_ = value;
} }
inline Environment* Environment::New(v8::Local<v8::Context> context) { inline Environment* Environment::New(v8::Local<v8::Context> context,
Environment* env = new Environment(context); uv_loop_t* loop) {
Environment* env = new Environment(context, loop);
env->AssignToContext(context); env->AssignToContext(context);
return env; return env;
} }
@ -207,12 +209,14 @@ inline Environment* Environment::GetCurrent(v8::Local<v8::Context> context) {
context->GetAlignedPointerFromEmbedderData(kContextEmbedderDataIndex)); context->GetAlignedPointerFromEmbedderData(kContextEmbedderDataIndex));
} }
inline Environment::Environment(v8::Local<v8::Context> context) inline Environment::Environment(v8::Local<v8::Context> context,
uv_loop_t* loop)
: isolate_(context->GetIsolate()), : isolate_(context->GetIsolate()),
isolate_data_(IsolateData::GetOrCreate(context->GetIsolate())), isolate_data_(IsolateData::GetOrCreate(context->GetIsolate(), loop)),
using_smalloc_alloc_cb_(false), using_smalloc_alloc_cb_(false),
using_domains_(false), using_domains_(false),
printed_error_(false), printed_error_(false),
debugger_agent_(this),
context_(context->GetIsolate(), context) { context_(context->GetIsolate(), context) {
// We'll be creating new objects so make sure we've entered the context. // We'll be creating new objects so make sure we've entered the context.
v8::HandleScope handle_scope(isolate()); v8::HandleScope handle_scope(isolate());
@ -221,6 +225,10 @@ inline Environment::Environment(v8::Local<v8::Context> context)
set_module_load_list_array(v8::Array::New(isolate())); set_module_load_list_array(v8::Array::New(isolate()));
RB_INIT(&cares_task_list_); RB_INIT(&cares_task_list_);
QUEUE_INIT(&gc_tracker_queue_); QUEUE_INIT(&gc_tracker_queue_);
QUEUE_INIT(&req_wrap_queue_);
QUEUE_INIT(&handle_wrap_queue_);
QUEUE_INIT(&handle_cleanup_queue_);
handle_cleanup_waiting_ = 0;
} }
inline Environment::~Environment() { inline Environment::~Environment() {
@ -233,6 +241,21 @@ inline Environment::~Environment() {
isolate_data()->Put(); isolate_data()->Put();
} }
inline void Environment::CleanupHandles() {
while (!QUEUE_EMPTY(&handle_cleanup_queue_)) {
QUEUE* q = QUEUE_HEAD(&handle_cleanup_queue_);
QUEUE_REMOVE(q);
HandleCleanup* hc = ContainerOf(&HandleCleanup::handle_cleanup_queue_, q);
handle_cleanup_waiting_++;
hc->cb_(this, hc->handle_, hc->arg_);
delete hc;
}
while (handle_cleanup_waiting_ != 0)
uv_run(event_loop(), UV_RUN_ONCE);
}
inline void Environment::Dispose() { inline void Environment::Dispose() {
delete this; delete this;
} }
@ -287,6 +310,17 @@ inline uv_check_t* Environment::idle_check_handle() {
return &idle_check_handle_; return &idle_check_handle_;
} }
inline void Environment::RegisterHandleCleanup(uv_handle_t* handle,
HandleCleanupCb cb,
void *arg) {
HandleCleanup* hc = new HandleCleanup(handle, cb, arg);
QUEUE_INSERT_TAIL(&handle_cleanup_queue_, &hc->handle_cleanup_queue_);
}
inline void Environment::FinishHandleCleanup(uv_handle_t* handle) {
handle_cleanup_waiting_--;
}
inline uv_loop_t* Environment::event_loop() const { inline uv_loop_t* Environment::event_loop() const {
return isolate_data()->event_loop(); return isolate_data()->event_loop();
} }

52
src/env.h

@ -28,6 +28,7 @@
#include "uv.h" #include "uv.h"
#include "v8.h" #include "v8.h"
#include "queue.h" #include "queue.h"
#include "debugger-agent.h"
#include <stdint.h> #include <stdint.h>
@ -356,11 +357,34 @@ class Environment {
DISALLOW_COPY_AND_ASSIGN(TickInfo); DISALLOW_COPY_AND_ASSIGN(TickInfo);
}; };
typedef void (*HandleCleanupCb)(Environment* env,
uv_handle_t* handle,
void* arg);
class HandleCleanup {
private:
friend class Environment;
HandleCleanup(uv_handle_t* handle, HandleCleanupCb cb, void* arg)
: handle_(handle),
cb_(cb),
arg_(arg) {
QUEUE_INIT(&handle_cleanup_queue_);
}
uv_handle_t* handle_;
HandleCleanupCb cb_;
void* arg_;
QUEUE handle_cleanup_queue_;
};
static inline Environment* GetCurrent(v8::Isolate* isolate); static inline Environment* GetCurrent(v8::Isolate* isolate);
static inline Environment* GetCurrent(v8::Local<v8::Context> context); static inline Environment* GetCurrent(v8::Local<v8::Context> context);
// See CreateEnvironment() in src/node.cc. // See CreateEnvironment() in src/node.cc.
static inline Environment* New(v8::Local<v8::Context> context); static inline Environment* New(v8::Local<v8::Context> context,
uv_loop_t* loop);
inline void CleanupHandles();
inline void Dispose(); inline void Dispose();
// Defined in src/node_profiler.cc. // Defined in src/node_profiler.cc.
@ -385,6 +409,12 @@ class Environment {
static inline Environment* from_idle_check_handle(uv_check_t* handle); static inline Environment* from_idle_check_handle(uv_check_t* handle);
inline uv_check_t* idle_check_handle(); inline uv_check_t* idle_check_handle();
// Register clean-up cb to be called on env->Dispose()
inline void RegisterHandleCleanup(uv_handle_t* handle,
HandleCleanupCb cb,
void *arg);
inline void FinishHandleCleanup(uv_handle_t* handle);
inline AsyncListener* async_listener(); inline AsyncListener* async_listener();
inline DomainFlag* domain_flag(); inline DomainFlag* domain_flag();
inline TickInfo* tick_info(); inline TickInfo* tick_info();
@ -434,12 +464,19 @@ class Environment {
ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V)
#undef V #undef V
inline debugger::Agent* debugger_agent() {
return &debugger_agent_;
}
inline QUEUE* handle_wrap_queue() { return &handle_wrap_queue_; }
inline QUEUE* req_wrap_queue() { return &req_wrap_queue_; }
private: private:
static const int kIsolateSlot = NODE_ISOLATE_SLOT; static const int kIsolateSlot = NODE_ISOLATE_SLOT;
class GCInfo; class GCInfo;
class IsolateData; class IsolateData;
inline explicit Environment(v8::Local<v8::Context> context); inline Environment(v8::Local<v8::Context> context, uv_loop_t* loop);
inline ~Environment(); inline ~Environment();
inline IsolateData* isolate_data() const; inline IsolateData* isolate_data() const;
void AfterGarbageCollectionCallback(const GCInfo* before, void AfterGarbageCollectionCallback(const GCInfo* before,
@ -465,6 +502,12 @@ class Environment {
bool using_domains_; bool using_domains_;
QUEUE gc_tracker_queue_; QUEUE gc_tracker_queue_;
bool printed_error_; bool printed_error_;
debugger::Agent debugger_agent_;
QUEUE handle_wrap_queue_;
QUEUE req_wrap_queue_;
QUEUE handle_cleanup_queue_;
int handle_cleanup_waiting_;
#define V(PropertyName, TypeName) \ #define V(PropertyName, TypeName) \
v8::Persistent<TypeName> PropertyName ## _; v8::Persistent<TypeName> PropertyName ## _;
@ -494,7 +537,8 @@ class Environment {
// Per-thread, reference-counted singleton. // Per-thread, reference-counted singleton.
class IsolateData { class IsolateData {
public: public:
static inline IsolateData* GetOrCreate(v8::Isolate* isolate); static inline IsolateData* GetOrCreate(v8::Isolate* isolate,
uv_loop_t* loop);
inline void Put(); inline void Put();
inline uv_loop_t* event_loop() const; inline uv_loop_t* event_loop() const;
@ -509,7 +553,7 @@ class Environment {
private: private:
inline static IsolateData* Get(v8::Isolate* isolate); inline static IsolateData* Get(v8::Isolate* isolate);
inline explicit IsolateData(v8::Isolate* isolate); inline explicit IsolateData(v8::Isolate* isolate, uv_loop_t* loop);
inline v8::Isolate* isolate() const; inline v8::Isolate* isolate() const;
// Defined in src/node_profiler.cc. // Defined in src/node_profiler.cc.

5
src/handle_wrap.cc

@ -39,9 +39,6 @@ using v8::Local;
using v8::Object; using v8::Object;
using v8::Value; using v8::Value;
// defined in node.cc
extern QUEUE handle_wrap_queue;
void HandleWrap::Ref(const FunctionCallbackInfo<Value>& args) { void HandleWrap::Ref(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args.GetIsolate()); Environment* env = Environment::GetCurrent(args.GetIsolate());
@ -100,7 +97,7 @@ HandleWrap::HandleWrap(Environment* env,
handle__->data = this; handle__->data = this;
HandleScope scope(env->isolate()); HandleScope scope(env->isolate());
Wrap<HandleWrap>(object, this); Wrap<HandleWrap>(object, this);
QUEUE_INSERT_TAIL(&handle_wrap_queue, &handle_wrap_queue_); QUEUE_INSERT_TAIL(env->handle_wrap_queue(), &handle_wrap_queue_);
} }

202
src/node.cc

@ -122,10 +122,6 @@ using v8::V8;
using v8::Value; using v8::Value;
using v8::kExternalUint32Array; using v8::kExternalUint32Array;
// FIXME(bnoordhuis) Make these per-context?
QUEUE handle_wrap_queue = { &handle_wrap_queue, &handle_wrap_queue };
QUEUE req_wrap_queue = { &req_wrap_queue, &req_wrap_queue };
static bool print_eval = false; static bool print_eval = false;
static bool force_repl = false; static bool force_repl = false;
static bool trace_deprecation = false; static bool trace_deprecation = false;
@ -1554,13 +1550,14 @@ static Local<Value> ExecuteString(Environment* env,
static void GetActiveRequests(const FunctionCallbackInfo<Value>& args) { static void GetActiveRequests(const FunctionCallbackInfo<Value>& args) {
HandleScope scope(args.GetIsolate()); Environment* env = Environment::GetCurrent(args.GetIsolate());
HandleScope scope(env->isolate());
Local<Array> ary = Array::New(args.GetIsolate()); Local<Array> ary = Array::New(args.GetIsolate());
QUEUE* q = NULL; QUEUE* q = NULL;
int i = 0; int i = 0;
QUEUE_FOREACH(q, &req_wrap_queue) { QUEUE_FOREACH(q, env->req_wrap_queue()) {
ReqWrap<uv_req_t>* w = ContainerOf(&ReqWrap<uv_req_t>::req_wrap_queue_, q); ReqWrap<uv_req_t>* w = ContainerOf(&ReqWrap<uv_req_t>::req_wrap_queue_, q);
if (w->persistent().IsEmpty()) if (w->persistent().IsEmpty())
continue; continue;
@ -1583,7 +1580,7 @@ void GetActiveHandles(const FunctionCallbackInfo<Value>& args) {
Local<String> owner_sym = env->owner_string(); Local<String> owner_sym = env->owner_string();
QUEUE_FOREACH(q, &handle_wrap_queue) { QUEUE_FOREACH(q, env->handle_wrap_queue()) {
HandleWrap* w = ContainerOf(&HandleWrap::handle_wrap_queue_, q); HandleWrap* w = ContainerOf(&HandleWrap::handle_wrap_queue_, q);
if (w->persistent().IsEmpty() || (w->flags_ & HandleWrap::kUnref)) if (w->persistent().IsEmpty() || (w->flags_ & HandleWrap::kUnref))
continue; continue;
@ -1967,8 +1964,8 @@ static void Uptime(const FunctionCallbackInfo<Value>& args) {
HandleScope scope(env->isolate()); HandleScope scope(env->isolate());
double uptime; double uptime;
uv_update_time(uv_default_loop()); uv_update_time(env->event_loop());
uptime = uv_now(uv_default_loop()) - prog_start_time; uptime = uv_now(env->event_loop()) - prog_start_time;
args.GetReturnValue().Set(Number::New(env->isolate(), uptime / 1000)); args.GetReturnValue().Set(Number::New(env->isolate(), uptime / 1000));
} }
@ -2860,9 +2857,12 @@ static void RawDebug(const FunctionCallbackInfo<Value>& args) {
} }
void Load(Environment* env) { void LoadEnvironment(Environment* env) {
HandleScope handle_scope(env->isolate()); HandleScope handle_scope(env->isolate());
V8::SetFatalErrorHandler(node::OnFatalError);
V8::AddMessageListener(OnMessage);
// Compile, execute the src/node.js file. (Which was included as static C // Compile, execute the src/node.js file. (Which was included as static C
// string in node_natives.h. 'natve_node' is the string containing that // string in node_natives.h. 'natve_node' is the string containing that
// source code.) // source code.)
@ -3121,40 +3121,33 @@ static void ParseArgs(int* argc,
// Called from V8 Debug Agent TCP thread. // Called from V8 Debug Agent TCP thread.
static void DispatchMessagesDebugAgentCallback() { static void DispatchMessagesDebugAgentCallback(Environment* env) {
// TODO(indutny): move async handle to environment
uv_async_send(&dispatch_debug_messages_async); uv_async_send(&dispatch_debug_messages_async);
} }
// Called from the main thread. static void StartDebug(Environment* env, bool wait) {
static void EnableDebug(Isolate* isolate, bool wait_connect) { CHECK(!debugger_running);
assert(debugger_running == false);
Isolate::Scope isolate_scope(isolate); env->debugger_agent()->set_dispatch_handler(
HandleScope handle_scope(isolate); DispatchMessagesDebugAgentCallback);
v8::Debug::SetDebugMessageDispatchHandler(DispatchMessagesDebugAgentCallback, debugger_running = env->debugger_agent()->Start(debug_port, wait);
false);
debugger_running = v8::Debug::EnableAgent("node " NODE_VERSION,
debug_port,
wait_connect);
if (debugger_running == false) { if (debugger_running == false) {
fprintf(stderr, "Starting debugger on port %d failed\n", debug_port); fprintf(stderr, "Starting debugger on port %d failed\n", debug_port);
fflush(stderr); fflush(stderr);
return; return;
} }
fprintf(stderr, "Debugger listening on port %d\n", debug_port); }
fflush(stderr);
if (isolate == NULL)
return; // Still starting up.
Local<Context> context = isolate->GetCurrentContext();
if (context.IsEmpty())
return; // Still starting up.
Environment* env = Environment::GetCurrent(context);
// Assign environment to the debugger's context // Called from the main thread.
env->AssignToContext(v8::Debug::GetDebugContext()); static void EnableDebug(Environment* env) {
CHECK(debugger_running);
// Send message to enable debug in workers
HandleScope handle_scope(env->isolate());
Context::Scope context_scope(env->context());
Local<Object> message = Object::New(env->isolate()); Local<Object> message = Object::New(env->isolate());
message->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "cmd"), message->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "cmd"),
FIXED_ONE_BYTE_STRING(env->isolate(), "NODE_DEBUG_ENABLED")); FIXED_ONE_BYTE_STRING(env->isolate(), "NODE_DEBUG_ENABLED"));
@ -3163,6 +3156,9 @@ static void EnableDebug(Isolate* isolate, bool wait_connect) {
message message
}; };
MakeCallback(env, env->process_object(), "emit", ARRAY_SIZE(argv), argv); MakeCallback(env, env->process_object(), "emit", ARRAY_SIZE(argv), argv);
// Enabled debugger, possibly making it wait on a semaphore
env->debugger_agent()->Enable();
} }
@ -3170,7 +3166,12 @@ static void EnableDebug(Isolate* isolate, bool wait_connect) {
static void DispatchDebugMessagesAsyncCallback(uv_async_t* handle) { static void DispatchDebugMessagesAsyncCallback(uv_async_t* handle) {
if (debugger_running == false) { if (debugger_running == false) {
fprintf(stderr, "Starting debugger agent.\n"); fprintf(stderr, "Starting debugger agent.\n");
EnableDebug(node_isolate, false);
Environment* env = Environment::GetCurrent(node_isolate);
Context::Scope context_scope(env->context());
StartDebug(env, false);
EnableDebug(env);
} }
Isolate::Scope isolate_scope(node_isolate); Isolate::Scope isolate_scope(node_isolate);
v8::Debug::ProcessDebugMessages(); v8::Debug::ProcessDebugMessages();
@ -3399,7 +3400,8 @@ static void DebugPause(const FunctionCallbackInfo<Value>& args) {
static void DebugEnd(const FunctionCallbackInfo<Value>& args) { static void DebugEnd(const FunctionCallbackInfo<Value>& args) {
if (debugger_running) { if (debugger_running) {
v8::Debug::DisableAgent(); Environment* env = Environment::GetCurrent(args.GetIsolate());
env->debugger_agent()->Stop();
debugger_running = false; debugger_running = false;
} }
} }
@ -3512,13 +3514,7 @@ void Init(int* argc,
RegisterSignalHandler(SIGTERM, SignalExit, true); RegisterSignalHandler(SIGTERM, SignalExit, true);
#endif // __POSIX__ #endif // __POSIX__
V8::SetFatalErrorHandler(node::OnFatalError); if (!use_debug_agent) {
V8::AddMessageListener(OnMessage);
// If the --debug flag was specified then initialize the debug thread.
if (use_debug_agent) {
EnableDebug(node_isolate, debug_wait_connect);
} else {
RegisterDebugSignalHandler(); RegisterDebugSignalHandler();
} }
} }
@ -3591,22 +3587,62 @@ int EmitExit(Environment* env) {
} }
// Just a convenience method
Environment* CreateEnvironment(Isolate* isolate, Environment* CreateEnvironment(Isolate* isolate,
Handle<Context> context, Handle<Context> context,
int argc, int argc,
const char* const* argv, const char* const* argv,
int exec_argc, int exec_argc,
const char* const* exec_argv) { const char* const* exec_argv) {
Environment* env;
Context::Scope context_scope(context);
env = CreateEnvironment(isolate,
uv_default_loop(),
context,
argc,
argv,
exec_argc,
exec_argv);
LoadEnvironment(env);
return env;
}
static void HandleCloseCb(uv_handle_t* handle) {
Environment* env = reinterpret_cast<Environment*>(handle->data);
env->FinishHandleCleanup(handle);
}
static void HandleCleanup(Environment* env,
uv_handle_t* handle,
void* arg) {
handle->data = env;
uv_close(handle, HandleCloseCb);
}
Environment* CreateEnvironment(Isolate* isolate,
uv_loop_t* loop,
Handle<Context> context,
int argc,
const char* const* argv,
int exec_argc,
const char* const* exec_argv) {
HandleScope handle_scope(isolate); HandleScope handle_scope(isolate);
Context::Scope context_scope(context); Context::Scope context_scope(context);
Environment* env = Environment::New(context); Environment* env = Environment::New(context, loop);
isolate->SetAutorunMicrotasks(false); isolate->SetAutorunMicrotasks(false);
uv_check_init(env->event_loop(), env->immediate_check_handle()); uv_check_init(env->event_loop(), env->immediate_check_handle());
uv_unref( uv_unref(
reinterpret_cast<uv_handle_t*>(env->immediate_check_handle())); reinterpret_cast<uv_handle_t*>(env->immediate_check_handle()));
uv_idle_init(env->event_loop(), env->immediate_idle_handle()); uv_idle_init(env->event_loop(), env->immediate_idle_handle());
// Inform V8's CPU profiler when we're idle. The profiler is sampling-based // Inform V8's CPU profiler when we're idle. The profiler is sampling-based
@ -3623,6 +3659,24 @@ Environment* CreateEnvironment(Isolate* isolate,
uv_unref(reinterpret_cast<uv_handle_t*>(env->idle_prepare_handle())); uv_unref(reinterpret_cast<uv_handle_t*>(env->idle_prepare_handle()));
uv_unref(reinterpret_cast<uv_handle_t*>(env->idle_check_handle())); uv_unref(reinterpret_cast<uv_handle_t*>(env->idle_check_handle()));
// Register handle cleanups
env->RegisterHandleCleanup(
reinterpret_cast<uv_handle_t*>(env->immediate_check_handle()),
HandleCleanup,
NULL);
env->RegisterHandleCleanup(
reinterpret_cast<uv_handle_t*>(env->immediate_idle_handle()),
HandleCleanup,
NULL);
env->RegisterHandleCleanup(
reinterpret_cast<uv_handle_t*>(env->idle_prepare_handle()),
HandleCleanup,
NULL);
env->RegisterHandleCleanup(
reinterpret_cast<uv_handle_t*>(env->idle_check_handle()),
HandleCleanup,
NULL);
if (v8_is_profiling) { if (v8_is_profiling) {
StartProfilerIdleNotifier(env); StartProfilerIdleNotifier(env);
} }
@ -3634,7 +3688,6 @@ Environment* CreateEnvironment(Isolate* isolate,
env->set_process_object(process_object); env->set_process_object(process_object);
SetupProcessObject(env, argc, argv, exec_argc, exec_argv); SetupProcessObject(env, argc, argv, exec_argc, exec_argv);
Load(env);
return env; return env;
} }
@ -3676,34 +3729,41 @@ int Start(int argc, char** argv) {
HandleScope handle_scope(node_isolate); HandleScope handle_scope(node_isolate);
Local<Context> context = Context::New(node_isolate); Local<Context> context = Context::New(node_isolate);
Environment* env = CreateEnvironment( Environment* env = CreateEnvironment(
node_isolate, context, argc, argv, exec_argc, exec_argv); node_isolate,
// Assign env to the debugger's context uv_default_loop(),
if (debugger_running) { context,
HandleScope scope(env->isolate()); argc,
env->AssignToContext(v8::Debug::GetDebugContext()); argv,
} exec_argc,
// This Context::Scope is here so EnableDebug() can look up the current exec_argv);
// environment with Environment::GetCurrent(). Context::Scope context_scope(context);
// TODO(bnoordhuis) Reorder the debugger initialization logic so it can
// be removed. // Start debug agent when argv has --debug
{ if (use_debug_agent)
Context::Scope context_scope(env->context()); StartDebug(env, debug_wait_connect);
bool more;
do { LoadEnvironment(env);
more = uv_run(env->event_loop(), UV_RUN_ONCE);
if (more == false) { // Enable debugger
EmitBeforeExit(env); if (use_debug_agent)
EnableDebug(env);
// Emit `beforeExit` if the loop became alive either after emitting
// event, or after running some callbacks. bool more;
more = uv_loop_alive(env->event_loop()); do {
if (uv_run(env->event_loop(), UV_RUN_NOWAIT) != 0) more = uv_run(env->event_loop(), UV_RUN_ONCE);
more = true; if (more == false) {
} EmitBeforeExit(env);
} while (more == true);
code = EmitExit(env); // Emit `beforeExit` if the loop became alive either after emitting
RunAtExit(env); // event, or after running some callbacks.
} more = uv_loop_alive(env->event_loop());
if (uv_run(env->event_loop(), UV_RUN_NOWAIT) != 0)
more = true;
}
} while (more == true);
code = EmitExit(env);
RunAtExit(env);
env->Dispose(); env->Dispose();
env = NULL; env = NULL;
} }

17
src/node.h

@ -63,6 +63,9 @@
#define NODE_DEPRECATED(msg, fn) V8_DEPRECATED(msg, fn) #define NODE_DEPRECATED(msg, fn) V8_DEPRECATED(msg, fn)
// Forward-declare libuv loop
struct uv_loop_s;
// Forward-declare these functions now to stop MSVS from becoming // Forward-declare these functions now to stop MSVS from becoming
// terminally confused when it's done in node_internals.h // terminally confused when it's done in node_internals.h
namespace node { namespace node {
@ -177,12 +180,26 @@ NODE_EXTERN void Init(int* argc,
class Environment; class Environment;
NODE_EXTERN Environment* CreateEnvironment(v8::Isolate* isolate,
struct uv_loop_s* loop,
v8::Handle<v8::Context> context,
int argc,
const char* const* argv,
int exec_argc,
const char* const* exec_argv);
NODE_EXTERN void LoadEnvironment(Environment* env);
// NOTE: Calling this is the same as calling
// CreateEnvironment() + LoadEnvironment() from above.
// `uv_default_loop()` will be passed as `loop`.
NODE_EXTERN Environment* CreateEnvironment(v8::Isolate* isolate, NODE_EXTERN Environment* CreateEnvironment(v8::Isolate* isolate,
v8::Handle<v8::Context> context, v8::Handle<v8::Context> context,
int argc, int argc,
const char* const* argv, const char* const* argv,
int exec_argc, int exec_argc,
const char* const* exec_argv); const char* const* exec_argv);
NODE_EXTERN void EmitBeforeExit(Environment* env); NODE_EXTERN void EmitBeforeExit(Environment* env);
NODE_EXTERN int EmitExit(Environment* env); NODE_EXTERN int EmitExit(Environment* env);
NODE_EXTERN void RunAtExit(Environment* env); NODE_EXTERN void RunAtExit(Environment* env);

10
src/node.js

@ -56,7 +56,10 @@
startup.processKillAndExit(); startup.processKillAndExit();
startup.processSignalHandlers(); startup.processSignalHandlers();
startup.processChannel(); // Do not initialize channel in debugger agent, it deletes env variable
// and the main thread won't see it.
if (process.argv[1] !== '--debug-agent')
startup.processChannel();
startup.processRawDebug(); startup.processRawDebug();
@ -80,6 +83,11 @@
var d = NativeModule.require('_debugger'); var d = NativeModule.require('_debugger');
d.start(); d.start();
} else if (process.argv[1] == '--debug-agent') {
// Start the debugger agent
var d = NativeModule.require('_debugger_agent');
d.start();
} else if (process._eval != null) { } else if (process._eval != null) {
// User passed '-e' or '--eval' arguments to Node. // User passed '-e' or '--eval' arguments to Node.
evalScript('[eval]'); evalScript('[eval]');

5
src/req_wrap.h

@ -31,9 +31,6 @@
namespace node { namespace node {
// defined in node.cc
extern QUEUE req_wrap_queue;
template <typename T> template <typename T>
class ReqWrap : public AsyncWrap { class ReqWrap : public AsyncWrap {
public: public:
@ -44,7 +41,7 @@ class ReqWrap : public AsyncWrap {
if (env->in_domain()) if (env->in_domain())
object->Set(env->domain_string(), env->domain_array()->Get(0)); object->Set(env->domain_string(), env->domain_array()->Get(0));
QUEUE_INSERT_TAIL(&req_wrap_queue, &req_wrap_queue_); QUEUE_INSERT_TAIL(env->req_wrap_queue(), &req_wrap_queue_);
} }

0
test/simple/test-debug-brk-no-arg.js → test/disabled/test-debug-brk-no-arg.js

Loading…
Cancel
Save