deps: re-implement debugger-agent
authorFedor Indutny <fedor@indutny.com>
Sat, 4 Oct 2014 14:44:39 +0000 (18:44 +0400)
committerFedor Indutny <fedor@indutny.com>
Wed, 8 Oct 2014 11:36:08 +0000 (15:36 +0400)
Reviewed-By: Trevor Norris <trevnorris@gmail.com>
PR-URL: https://github.com/joyent/node/pull/8476

17 files changed:
Makefile
deps/debugger-agent/debugger-agent.gyp [new file with mode: 0644]
deps/debugger-agent/include/debugger-agent.h [new file with mode: 0644]
deps/debugger-agent/lib/_debugger_agent.js [new file with mode: 0644]
deps/debugger-agent/src/agent.cc [new file with mode: 0644]
deps/debugger-agent/src/agent.h [new file with mode: 0644]
lib/_debugger.js
node.gyp
src/cares_wrap.cc
src/env-inl.h
src/env.h
src/handle_wrap.cc
src/node.cc
src/node.h
src/node.js
src/req_wrap.h
test/disabled/test-debug-brk-no-arg.js [moved from test/simple/test-debug-brk-no-arg.js with 100% similarity]

index fb85460..a42d392 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -406,7 +406,7 @@ CPPLINT_EXCLUDE += src/queue.h
 CPPLINT_EXCLUDE += src/tree.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:
        @$(PYTHON) tools/cpplint.py $(CPPLINT_FILES)
diff --git a/deps/debugger-agent/debugger-agent.gyp b/deps/debugger-agent/debugger-agent.gyp
new file mode 100644 (file)
index 0000000..e982068
--- /dev/null
@@ -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",
+    ],
+  }],
+}
diff --git a/deps/debugger-agent/include/debugger-agent.h b/deps/debugger-agent/include/debugger-agent.h
new file mode 100644 (file)
index 0000000..762a687
--- /dev/null
@@ -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_
diff --git a/deps/debugger-agent/lib/_debugger_agent.js b/deps/debugger-agent/lib/_debugger_agent.js
new file mode 100644 (file)
index 0000000..680c5e9
--- /dev/null
@@ -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;
+}
diff --git a/deps/debugger-agent/src/agent.cc b/deps/debugger-agent/src/agent.cc
new file mode 100644 (file)
index 0000000..335737f
--- /dev/null
@@ -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
diff --git a/deps/debugger-agent/src/agent.h b/deps/debugger-agent/src/agent.h
new file mode 100644 (file)
index 0000000..82db5e5
--- /dev/null
@@ -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_
index 4b01d39..41d3fc4 100644 (file)
@@ -249,6 +249,10 @@ Client.prototype._onResponse = function(res) {
     this._removeScript(res.body.body.script);
     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) {
index de48ae8..daf06c7 100644 (file)
--- a/node.gyp
+++ b/node.gyp
@@ -67,6 +67,7 @@
       'lib/util.js',
       'lib/vm.js',
       'lib/zlib.js',
+      'deps/debugger-agent/lib/_debugger_agent.js',
     ],
   },
 
@@ -77,6 +78,7 @@
 
       'dependencies': [
         'node_js2c#host',
+        'deps/debugger-agent/debugger-agent.gyp:debugger-agent',
       ],
 
       'include_dirs': [
index f3911df..356df86 100644 (file)
@@ -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,
                        Handle<Value> unused,
                        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 */
   /* first socket is opened. */
   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, "queryAaaa", Query<QueryAaaaWrap>);
index 4bc5eb0..e6a3d1c 100644 (file)
@@ -74,10 +74,10 @@ inline Environment::IsolateData* Environment::IsolateData::Get(
 }
 
 inline Environment::IsolateData* Environment::IsolateData::GetOrCreate(
-    v8::Isolate* isolate) {
+    v8::Isolate* isolate, uv_loop_t* loop) {
   IsolateData* isolate_data = Get(isolate);
   if (isolate_data == NULL) {
-    isolate_data = new IsolateData(isolate);
+    isolate_data = new IsolateData(isolate, loop);
     isolate->SetData(kIsolateSlot, isolate_data);
   }
   isolate_data->ref_count_ += 1;
@@ -91,8 +91,9 @@ inline void Environment::IsolateData::Put() {
   }
 }
 
-inline Environment::IsolateData::IsolateData(v8::Isolate* isolate)
-    : event_loop_(uv_default_loop()),
+inline Environment::IsolateData::IsolateData(v8::Isolate* isolate,
+                                             uv_loop_t* loop)
+    : event_loop_(loop),
       isolate_(isolate),
 #define V(PropertyName, 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;
 }
 
-inline Environment* Environment::New(v8::Local<v8::Context> context) {
-  Environment* env = new Environment(context);
+inline Environment* Environment::New(v8::Local<v8::Context> context,
+                                     uv_loop_t* loop) {
+  Environment* env = new Environment(context, loop);
   env->AssignToContext(context);
   return env;
 }
@@ -207,12 +209,14 @@ inline Environment* Environment::GetCurrent(v8::Local<v8::Context> context) {
       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_data_(IsolateData::GetOrCreate(context->GetIsolate())),
+      isolate_data_(IsolateData::GetOrCreate(context->GetIsolate(), loop)),
       using_smalloc_alloc_cb_(false),
       using_domains_(false),
       printed_error_(false),
+      debugger_agent_(this),
       context_(context->GetIsolate(), context) {
   // We'll be creating new objects so make sure we've entered the context.
   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()));
   RB_INIT(&cares_task_list_);
   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() {
@@ -233,6 +241,21 @@ inline Environment::~Environment() {
   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() {
   delete this;
 }
@@ -287,6 +310,17 @@ inline uv_check_t* Environment::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 {
   return isolate_data()->event_loop();
 }
index 015dcd2..655e804 100644 (file)
--- a/src/env.h
+++ b/src/env.h
@@ -28,6 +28,7 @@
 #include "uv.h"
 #include "v8.h"
 #include "queue.h"
+#include "debugger-agent.h"
 
 #include <stdint.h>
 
@@ -356,11 +357,34 @@ class Environment {
     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::Local<v8::Context> context);
 
   // 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();
 
   // Defined in src/node_profiler.cc.
@@ -385,6 +409,12 @@ class Environment {
   static inline Environment* from_idle_check_handle(uv_check_t* 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 DomainFlag* domain_flag();
   inline TickInfo* tick_info();
@@ -434,12 +464,19 @@ class Environment {
   ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(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:
   static const int kIsolateSlot = NODE_ISOLATE_SLOT;
 
   class GCInfo;
   class IsolateData;
-  inline explicit Environment(v8::Local<v8::Context> context);
+  inline Environment(v8::Local<v8::Context> context, uv_loop_t* loop);
   inline ~Environment();
   inline IsolateData* isolate_data() const;
   void AfterGarbageCollectionCallback(const GCInfo* before,
@@ -465,6 +502,12 @@ class Environment {
   bool using_domains_;
   QUEUE gc_tracker_queue_;
   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)                                             \
   v8::Persistent<TypeName> PropertyName ## _;
@@ -494,7 +537,8 @@ class Environment {
   // Per-thread, reference-counted singleton.
   class IsolateData {
    public:
-    static inline IsolateData* GetOrCreate(v8::Isolate* isolate);
+    static inline IsolateData* GetOrCreate(v8::Isolate* isolate,
+                                           uv_loop_t* loop);
     inline void Put();
     inline uv_loop_t* event_loop() const;
 
@@ -509,7 +553,7 @@ class Environment {
 
    private:
     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;
 
     // Defined in src/node_profiler.cc.
index f713750..83015f1 100644 (file)
@@ -39,9 +39,6 @@ using v8::Local;
 using v8::Object;
 using v8::Value;
 
-// defined in node.cc
-extern QUEUE handle_wrap_queue;
-
 
 void HandleWrap::Ref(const FunctionCallbackInfo<Value>& args) {
   Environment* env = Environment::GetCurrent(args.GetIsolate());
@@ -100,7 +97,7 @@ HandleWrap::HandleWrap(Environment* env,
   handle__->data = this;
   HandleScope scope(env->isolate());
   Wrap<HandleWrap>(object, this);
-  QUEUE_INSERT_TAIL(&handle_wrap_queue, &handle_wrap_queue_);
+  QUEUE_INSERT_TAIL(env->handle_wrap_queue(), &handle_wrap_queue_);
 }
 
 
index 0e798b1..1736f9f 100644 (file)
@@ -122,10 +122,6 @@ using v8::V8;
 using v8::Value;
 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 force_repl = false;
 static bool trace_deprecation = false;
@@ -1554,13 +1550,14 @@ static Local<Value> ExecuteString(Environment* env,
 
 
 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());
   QUEUE* q = NULL;
   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);
     if (w->persistent().IsEmpty())
       continue;
@@ -1583,7 +1580,7 @@ void GetActiveHandles(const FunctionCallbackInfo<Value>& args) {
 
   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);
     if (w->persistent().IsEmpty() || (w->flags_ & HandleWrap::kUnref))
       continue;
@@ -1967,8 +1964,8 @@ static void Uptime(const FunctionCallbackInfo<Value>& args) {
   HandleScope scope(env->isolate());
   double uptime;
 
-  uv_update_time(uv_default_loop());
-  uptime = uv_now(uv_default_loop()) - prog_start_time;
+  uv_update_time(env->event_loop());
+  uptime = uv_now(env->event_loop()) - prog_start_time;
 
   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());
 
+  V8::SetFatalErrorHandler(node::OnFatalError);
+  V8::AddMessageListener(OnMessage);
+
   // 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
   // source code.)
@@ -3121,40 +3121,33 @@ static void ParseArgs(int* argc,
 
 
 // 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);
 }
 
 
-// Called from the main thread.
-static void EnableDebug(Isolate* isolate, bool wait_connect) {
-  assert(debugger_running == false);
-  Isolate::Scope isolate_scope(isolate);
-  HandleScope handle_scope(isolate);
-  v8::Debug::SetDebugMessageDispatchHandler(DispatchMessagesDebugAgentCallback,
-                                            false);
-  debugger_running = v8::Debug::EnableAgent("node " NODE_VERSION,
-                                            debug_port,
-                                            wait_connect);
+static void StartDebug(Environment* env, bool wait) {
+  CHECK(!debugger_running);
+
+  env->debugger_agent()->set_dispatch_handler(
+        DispatchMessagesDebugAgentCallback);
+  debugger_running = env->debugger_agent()->Start(debug_port, wait);
   if (debugger_running == false) {
     fprintf(stderr, "Starting debugger on port %d failed\n", debug_port);
     fflush(stderr);
     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
-  env->AssignToContext(v8::Debug::GetDebugContext());
+// Called from the main thread.
+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());
   message->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "cmd"),
                FIXED_ONE_BYTE_STRING(env->isolate(), "NODE_DEBUG_ENABLED"));
@@ -3163,6 +3156,9 @@ static void EnableDebug(Isolate* isolate, bool wait_connect) {
     message
   };
   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) {
   if (debugger_running == false) {
     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);
   v8::Debug::ProcessDebugMessages();
@@ -3399,7 +3400,8 @@ static void DebugPause(const FunctionCallbackInfo<Value>& args) {
 
 static void DebugEnd(const FunctionCallbackInfo<Value>& args) {
   if (debugger_running) {
-    v8::Debug::DisableAgent();
+    Environment* env = Environment::GetCurrent(args.GetIsolate());
+    env->debugger_agent()->Stop();
     debugger_running = false;
   }
 }
@@ -3512,13 +3514,7 @@ void Init(int* argc,
   RegisterSignalHandler(SIGTERM, SignalExit, true);
 #endif  // __POSIX__
 
-  V8::SetFatalErrorHandler(node::OnFatalError);
-  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 {
+  if (!use_debug_agent) {
     RegisterDebugSignalHandler();
   }
 }
@@ -3591,22 +3587,62 @@ int EmitExit(Environment* env) {
 }
 
 
+// Just a convenience method
 Environment* CreateEnvironment(Isolate* isolate,
                                Handle<Context> context,
                                int argc,
                                const char* const* argv,
                                int exec_argc,
                                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);
 
   Context::Scope context_scope(context);
-  Environment* env = Environment::New(context);
+  Environment* env = Environment::New(context, loop);
 
   isolate->SetAutorunMicrotasks(false);
 
   uv_check_init(env->event_loop(), env->immediate_check_handle());
   uv_unref(
       reinterpret_cast<uv_handle_t*>(env->immediate_check_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
@@ -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_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) {
     StartProfilerIdleNotifier(env);
   }
@@ -3634,7 +3688,6 @@ Environment* CreateEnvironment(Isolate* isolate,
   env->set_process_object(process_object);
 
   SetupProcessObject(env, argc, argv, exec_argc, exec_argv);
-  Load(env);
 
   return env;
 }
@@ -3676,34 +3729,41 @@ int Start(int argc, char** argv) {
     HandleScope handle_scope(node_isolate);
     Local<Context> context = Context::New(node_isolate);
     Environment* env = CreateEnvironment(
-        node_isolate, context, argc, argv, exec_argc, exec_argv);
-    // Assign env to the debugger's context
-    if (debugger_running) {
-      HandleScope scope(env->isolate());
-      env->AssignToContext(v8::Debug::GetDebugContext());
-    }
-    // This Context::Scope is here so EnableDebug() can look up the current
-    // environment with Environment::GetCurrent().
-    // TODO(bnoordhuis) Reorder the debugger initialization logic so it can
-    // be removed.
-    {
-      Context::Scope context_scope(env->context());
-      bool more;
-      do {
-        more = uv_run(env->event_loop(), UV_RUN_ONCE);
-        if (more == false) {
-          EmitBeforeExit(env);
-
-          // Emit `beforeExit` if the loop became alive either after emitting
-          // 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);
-    }
+        node_isolate,
+        uv_default_loop(),
+        context,
+        argc,
+        argv,
+        exec_argc,
+        exec_argv);
+    Context::Scope context_scope(context);
+
+    // Start debug agent when argv has --debug
+    if (use_debug_agent)
+      StartDebug(env, debug_wait_connect);
+
+    LoadEnvironment(env);
+
+    // Enable debugger
+    if (use_debug_agent)
+      EnableDebug(env);
+
+    bool more;
+    do {
+      more = uv_run(env->event_loop(), UV_RUN_ONCE);
+      if (more == false) {
+        EmitBeforeExit(env);
+
+        // Emit `beforeExit` if the loop became alive either after emitting
+        // 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 = NULL;
   }
index 4eaaa07..ca7cec0 100644 (file)
@@ -63,6 +63,9 @@
 
 #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
 // terminally confused when it's done in node_internals.h
 namespace node {
@@ -178,11 +181,25 @@ NODE_EXTERN void Init(int* argc,
 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,
                                            v8::Handle<v8::Context> context,
                                            int argc,
                                            const char* const* argv,
                                            int exec_argc,
                                            const char* const* exec_argv);
+
+
 NODE_EXTERN void EmitBeforeExit(Environment* env);
 NODE_EXTERN int EmitExit(Environment* env);
 NODE_EXTERN void RunAtExit(Environment* env);
index e7b45ad..19d9506 100644 (file)
     startup.processKillAndExit();
     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();
 
       var d = NativeModule.require('_debugger');
       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) {
       // User passed '-e' or '--eval' arguments to Node.
       evalScript('[eval]');
index 56d63eb..df4ed2c 100644 (file)
@@ -31,9 +31,6 @@
 
 namespace node {
 
-// defined in node.cc
-extern QUEUE req_wrap_queue;
-
 template <typename T>
 class ReqWrap : public AsyncWrap {
  public:
@@ -44,7 +41,7 @@ class ReqWrap : public AsyncWrap {
     if (env->in_domain())
       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_);
   }