summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFedor Indutny <fedor@indutny.com>2014-10-04 18:44:39 +0400
committerFedor Indutny <fedor@indutny.com>2014-10-08 15:36:08 +0400
commit6a610a0f67f091092f1643326eaacb8c31f72259 (patch)
tree65f1811b365ca5e994d7621ff807b44a2854ef6d
parentd71dd638c6bbabd0fd1db46307edd7b0de1750e2 (diff)
downloadandroid-node-v8-6a610a0f67f091092f1643326eaacb8c31f72259.tar.gz
android-node-v8-6a610a0f67f091092f1643326eaacb8c31f72259.tar.bz2
android-node-v8-6a610a0f67f091092f1643326eaacb8c31f72259.zip
deps: re-implement debugger-agent
Reviewed-By: Trevor Norris <trevnorris@gmail.com> PR-URL: https://github.com/joyent/node/pull/8476
-rw-r--r--Makefile2
-rw-r--r--deps/debugger-agent/debugger-agent.gyp24
-rw-r--r--deps/debugger-agent/include/debugger-agent.h109
-rw-r--r--deps/debugger-agent/lib/_debugger_agent.js191
-rw-r--r--deps/debugger-agent/src/agent.cc347
-rw-r--r--deps/debugger-agent/src/agent.h64
-rw-r--r--lib/_debugger.js4
-rw-r--r--node.gyp2
-rw-r--r--src/cares_wrap.cc18
-rw-r--r--src/env-inl.h50
-rw-r--r--src/env.h52
-rw-r--r--src/handle_wrap.cc5
-rw-r--r--src/node.cc202
-rw-r--r--src/node.h17
-rw-r--r--src/node.js10
-rw-r--r--src/req_wrap.h5
-rw-r--r--test/disabled/test-debug-brk-no-arg.js (renamed from test/simple/test-debug-brk-no-arg.js)0
17 files changed, 1009 insertions, 93 deletions
diff --git a/Makefile b/Makefile
index fb854603d8..a42d392073 100644
--- 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
index 0000000000..e98206849a
--- /dev/null
+++ b/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",
+ ],
+ }],
+}
diff --git a/deps/debugger-agent/include/debugger-agent.h b/deps/debugger-agent/include/debugger-agent.h
new file mode 100644
index 0000000000..762a687a0a
--- /dev/null
+++ b/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_
diff --git a/deps/debugger-agent/lib/_debugger_agent.js b/deps/debugger-agent/lib/_debugger_agent.js
new file mode 100644
index 0000000000..680c5e95c4
--- /dev/null
+++ b/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;
+}
diff --git a/deps/debugger-agent/src/agent.cc b/deps/debugger-agent/src/agent.cc
new file mode 100644
index 0000000000..335737ffe9
--- /dev/null
+++ b/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
diff --git a/deps/debugger-agent/src/agent.h b/deps/debugger-agent/src/agent.h
new file mode 100644
index 0000000000..82db5e5e18
--- /dev/null
+++ b/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_
diff --git a/lib/_debugger.js b/lib/_debugger.js
index 4b01d39e5b..41d3fc4210 100644
--- a/lib/_debugger.js
+++ b/lib/_debugger.js
@@ -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) {
diff --git a/node.gyp b/node.gyp
index de48ae8378..daf06c7a7a 100644
--- 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': [
diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc
index f3911df5ce..356df86907 100644
--- a/src/cares_wrap.cc
+++ b/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,
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>);
diff --git a/src/env-inl.h b/src/env-inl.h
index 4bc5eb0986..e6a3d1c5ec 100644
--- a/src/env-inl.h
+++ b/src/env-inl.h
@@ -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();
}
diff --git a/src/env.h b/src/env.h
index 015dcd26e8..655e804cb9 100644
--- 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.
diff --git a/src/handle_wrap.cc b/src/handle_wrap.cc
index f713750d7f..83015f1dfd 100644
--- a/src/handle_wrap.cc
+++ b/src/handle_wrap.cc
@@ -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_);
}
diff --git a/src/node.cc b/src/node.cc
index 0e798b1353..1736f9fbb5 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -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;
}
diff --git a/src/node.h b/src/node.h
index 4eaaa07859..ca7cec0c45 100644
--- a/src/node.h
+++ b/src/node.h
@@ -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);
diff --git a/src/node.js b/src/node.js
index e7b45ad5cc..19d9506446 100644
--- a/src/node.js
+++ b/src/node.js
@@ -56,7 +56,10 @@
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();
@@ -80,6 +83,11 @@
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]');
diff --git a/src/req_wrap.h b/src/req_wrap.h
index 56d63eb711..df4ed2ced1 100644
--- a/src/req_wrap.h
+++ b/src/req_wrap.h
@@ -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_);
}
diff --git a/test/simple/test-debug-brk-no-arg.js b/test/disabled/test-debug-brk-no-arg.js
index f02527cf66..f02527cf66 100644
--- a/test/simple/test-debug-brk-no-arg.js
+++ b/test/disabled/test-debug-brk-no-arg.js