aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFedor Indutny <fedor.indutny@gmail.com>2012-05-16 23:04:24 +0700
committerFedor Indutny <fedor.indutny@gmail.com>2012-06-01 20:52:13 +0400
commitaf98fc9d5f20a8c5dc0db95121f18355337762f1 (patch)
tree26348c986cec965b3f6c34edcd1d9a743aa4c106
parent30a0e58d63a8fb48ee47472a52450539e0656df9 (diff)
downloadandroid-node-v8-af98fc9d5f20a8c5dc0db95121f18355337762f1.tar.gz
android-node-v8-af98fc9d5f20a8c5dc0db95121f18355337762f1.tar.bz2
android-node-v8-af98fc9d5f20a8c5dc0db95121f18355337762f1.zip
child_process: new stdio API for .spawn() method
-rw-r--r--lib/child_process.js236
-rw-r--r--node.gyp3
-rw-r--r--src/node.js5
-rw-r--r--src/process_wrap.cc100
-rw-r--r--src/tcp_wrap.cc12
-rw-r--r--src/tcp_wrap.h2
-rw-r--r--src/tty_wrap.cc184
-rw-r--r--src/tty_wrap.h58
-rw-r--r--src/udp_wrap.cc52
-rw-r--r--src/udp_wrap.h60
-rw-r--r--test/simple/test-process-wrap.js6
11 files changed, 472 insertions, 246 deletions
diff --git a/lib/child_process.js b/lib/child_process.js
index 2e67ebce1c..f79481dcfe 100644
--- a/lib/child_process.js
+++ b/lib/child_process.js
@@ -25,17 +25,38 @@ var Process = process.binding('process_wrap').Process;
var util = require('util');
var constants; // if (!constants) constants = process.binding('constants');
-var Pipe;
+var handleWraps = {};
+function handleWrapGetter(name, callback) {
+ var cons;
+
+ Object.defineProperty(handleWraps, name, {
+ get: function() {
+ if (cons !== undefined) return cons;
+ return cons = callback();
+ }
+ });
+}
+
+handleWrapGetter('Pipe', function() {
+ return process.binding('pipe_wrap').Pipe;
+});
+
+handleWrapGetter('TTY', function() {
+ return process.binding('tty_wrap').TTY;
+});
+
+handleWrapGetter('TCP', function() {
+ return process.binding('tcp_wrap').TCP;
+});
+
+handleWrapGetter('UDP', function() {
+ return process.binding('udp_wrap').UDP;
+});
// constructors for lazy loading
function createPipe(ipc) {
- // Lazy load
- if (!Pipe) {
- Pipe = process.binding('pipe_wrap').Pipe;
- }
-
- return new Pipe(ipc);
+ return new handleWraps.Pipe(ipc);
}
function createSocket(pipe, readable) {
@@ -414,39 +435,18 @@ exports.fork = function(modulePath /*, args, options*/) {
execArgv = options.execArgv || process.execArgv;
args = execArgv.concat([modulePath], args);
- // Don't allow stdinStream and customFds since a stdin channel will be used
- if (options.stdinStream) {
- throw new Error('stdinStream not allowed for fork()');
- }
-
- if (options.customFds) {
- throw new Error('customFds not allowed for fork()');
- }
-
// Leave stdin open for the IPC channel. stdout and stderr should be the
// same as the parent's if silent isn't set.
- options.customFds = (options.silent ? [-1, -1, -1] : [-1, 1, 2]);
-
- // Just need to set this - child process won't actually use the fd.
- // For backwards compat - this can be changed to 'NODE_CHANNEL' before v0.6.
- options.env = util._extend({}, options.env || process.env);
- options.env.NODE_CHANNEL_FD = 42;
+ options.stdio = options.silent ? ['ipc', 'pipe', 'pipe'] : ['ipc', 1, 2];
- // stdin is the IPC channel.
- options.stdinStream = createPipe(true);
-
- var child = spawn(process.execPath, args, options);
-
- setupChannel(child, options.stdinStream);
-
- return child;
+ return spawn(process.execPath, args, options);
};
-exports._forkChild = function() {
+exports._forkChild = function(fd) {
// set process.send()
var p = createPipe(true);
- p.open(0);
+ p.open(fd);
setupChannel(process, p);
};
@@ -591,6 +591,11 @@ var spawn = exports.spawn = function(file, args, options) {
}
var child = new ChildProcess();
+ if (options && options.customFds && !options.stdio) {
+ options.stdio = options.customFds.map(function(fd) {
+ return fd === -1 ? 'pipe' : fd;
+ });
+ }
child.spawn({
file: file,
@@ -598,8 +603,7 @@ var spawn = exports.spawn = function(file, args, options) {
cwd: options ? options.cwd : null,
windowsVerbatimArguments: !!(options && options.windowsVerbatimArguments),
envPairs: envPairs,
- customFds: options ? options.customFds : null,
- stdinStream: options ? options.stdinStream : null,
+ stdio: options ? options.stdio : null,
uid: options ? options.uid : null,
gid: options ? options.gid : null
});
@@ -658,46 +662,111 @@ function ChildProcess() {
util.inherits(ChildProcess, EventEmitter);
-function setStreamOption(name, index, options) {
- // Skip if we already have options.stdinStream
- if (options[name]) return;
+function getHandleWrapType(stream) {
+ if (stream instanceof handleWraps.Pipe) return 'pipe';
+ if (stream instanceof handleWraps.TTY) return 'tty';
+ if (stream instanceof handleWraps.TCP) return 'tcp';
+ if (stream instanceof handleWraps.UDP) return 'udp';
- if (options.customFds &&
- typeof options.customFds[index] == 'number' &&
- options.customFds[index] !== -1) {
- if (options.customFds[index] === index) {
- options[name] = null;
- } else {
- throw new Error('customFds not yet supported');
- }
- } else {
- options[name] = createPipe();
- }
+ return false;
}
ChildProcess.prototype.spawn = function(options) {
- var self = this;
-
- setStreamOption('stdinStream', 0, options);
- setStreamOption('stdoutStream', 1, options);
- setStreamOption('stderrStream', 2, options);
+ var self = this,
+ ipc,
+ ipcFd,
+ // If no `stdio` option was given - use default
+ stdio = options.stdio || 'pipe';
+
+ // Replace shortcut with an array
+ if (typeof stdio === 'string') {
+ switch (stdio) {
+ case 'ignore': stdio = ['ignore', 'ignore', 'ignore']; break;
+ case 'pipe': stdio = ['pipe', 'pipe', 'pipe']; break;
+ case 'inherit': stdio = [0, 1, 2]; break;
+ default: throw new TypeError('Incorrect value of stdio option: ' + stdio);
+ }
+ } else if (!Array.isArray(stdio)) {
+ throw new TypeError('Incorrect value of stdio option: ' + stdio);
+ }
- var r = this._handle.spawn(options);
+ // At least 3 stdio will be created
+ if (stdio.length < 3) {
+ stdio = stdio.concat(new Array(3 - stdio.length));
+ }
- if (r) {
- if (options.stdinStream) {
- options.stdinStream.close();
+ // Translate stdio into C++-readable form
+ // (i.e. PipeWraps or fds)
+ stdio = stdio.reduce(function(acc, stdio, i) {
+ function cleanup() {
+ acc.filter(function(stdio) {
+ return stdio.type === 'pipe' || stdio.type === 'ipc';
+ }).forEach(function(stdio) {
+ stdio.handle.close();
+ });
}
- if (options.stdoutStream) {
- options.stdoutStream.close();
+ // Defaults
+ if (stdio === undefined || stdio === null) {
+ stdio = i < 3 ? 'pipe' : 'ignore';
}
- if (options.stderrStream) {
- options.stderrStream.close();
+ if (stdio === 'ignore') {
+ acc.push({type: 'ignore'});
+ } else if (stdio === 'pipe' || typeof stdio === 'number' && stdio < 0) {
+ acc.push({type: 'pipe', handle: createPipe()});
+ } else if (stdio === 'ipc') {
+ if (ipc !== undefined) {
+ // Cleanup previously created pipes
+ cleanup();
+ throw Error('Child process can have only one IPC pipe');
+ }
+
+ ipc = createPipe(true);
+ ipcFd = i;
+
+ acc.push({ type: 'pipe', handle: ipc });
+ } else if (typeof stdio === 'number' || typeof stdio.fd === 'number') {
+ acc.push({ type: 'fd', fd: stdio.fd || stdio });
+ } else if (getHandleWrapType(stdio) || getHandleWrapType(stdio.handle) ||
+ getHandleWrapType(stdio._handle)) {
+ var handle = getHandleWrapType(stdio) ?
+ stdio :
+ getHandleWrapType(stdio.handle) ? stdio.handle : stdio._handle;
+
+ acc.push({
+ type: 'wrap',
+ wrapType: getHandleWrapType(handle),
+ handle: handle
+ });
+ } else {
+ // Cleanup
+ cleanup();
+ throw new TypeError('Incorrect value for stdio stream: ' + stdio);
}
+ return acc;
+ }, []);
+
+ options.stdio = stdio;
+
+ if (ipc !== undefined) {
+ // Let child process know about opened IPC channel
+ options.envPairs = options.envPairs || [];
+ options.envPairs.push('NODE_CHANNEL_FD=' + ipcFd);
+ }
+
+ var r = this._handle.spawn(options);
+
+ if (r) {
+ // Close all opened fds on error
+ stdio.forEach(function(stdio) {
+ if (stdio.type === 'pipe') {
+ stdio.handle.close();
+ }
+ });
+
this._handle.close();
this._handle = null;
throw errnoException(errno, 'spawn');
@@ -705,25 +774,36 @@ ChildProcess.prototype.spawn = function(options) {
this.pid = this._handle.pid;
- if (options.stdinStream) {
- this.stdin = createSocket(options.stdinStream, false);
- }
+ stdio.forEach(function(stdio, i) {
+ if (stdio.type === 'ignore') return;
- if (options.stdoutStream) {
- this.stdout = createSocket(options.stdoutStream, true);
- this._closesNeeded++;
- this.stdout.on('close', function() {
- maybeClose(self);
- });
- }
+ if (stdio.handle) {
+ // when i === 0 - we're dealing with stdin
+ // (which is the only one writable pipe)
+ stdio.socket = createSocket(stdio.handle, i > 0);
- if (options.stderrStream) {
- this.stderr = createSocket(options.stderrStream, true);
- this._closesNeeded++;
- this.stderr.on('close', function() {
- maybeClose(self);
- });
- }
+ if (i > 0) {
+ self._closesNeeded++;
+ stdio.socket.on('close', function() {
+ maybeClose(self);
+ });
+ }
+ }
+ });
+
+ this.stdin = stdio.length >= 1 && stdio[0].socket !== undefined ?
+ stdio[0].socket : null;
+ this.stdout = stdio.length >= 2 && stdio[1].socket !== undefined ?
+ stdio[1].socket : null;
+ this.stderr = stdio.length >= 3 && stdio[2].socket !== undefined ?
+ stdio[2].socket : null;
+
+ this.stdio = stdio.map(function(stdio) {
+ return stdio.socket === undefined ? null : stdio.socket;
+ });
+
+ // Add .send() method and start listening for IPC data
+ if (ipc !== undefined) setupChannel(this, ipc);
return r;
};
diff --git a/node.gyp b/node.gyp
index 824b61e49f..0aacc5fb04 100644
--- a/node.gyp
+++ b/node.gyp
@@ -109,6 +109,9 @@
'src/node_version.h',
'src/ngx-queue.h',
'src/pipe_wrap.h',
+ 'src/tty_wrap.h',
+ 'src/tcp_wrap.h',
+ 'src/udp_wrap.h',
'src/req_wrap.h',
'src/slab_allocator.h',
'src/stream_wrap.h',
diff --git a/src/node.js b/src/node.js
index fd5c6760b7..ba0786d6ea 100644
--- a/src/node.js
+++ b/src/node.js
@@ -482,7 +482,8 @@
// If we were spawned with env NODE_CHANNEL_FD then load that up and
// start parsing data from that stream.
if (process.env.NODE_CHANNEL_FD) {
- assert(parseInt(process.env.NODE_CHANNEL_FD) >= 0);
+ var fd = parseInt(process.env.NODE_CHANNEL_FD, 10);
+ assert(fd >= 0);
// Make sure it's not accidentally inherited by child processes.
delete process.env.NODE_CHANNEL_FD;
@@ -494,7 +495,7 @@
// FIXME is this really necessary?
process.binding('tcp_wrap');
- cp._forkChild();
+ cp._forkChild(fd);
assert(process.send);
}
}
diff --git a/src/process_wrap.cc b/src/process_wrap.cc
index ce29853565..101d89b3fd 100644
--- a/src/process_wrap.cc
+++ b/src/process_wrap.cc
@@ -22,6 +22,10 @@
#include "node.h"
#include "handle_wrap.h"
#include "pipe_wrap.h"
+#include "tty_wrap.h"
+#include "tcp_wrap.h"
+#include "udp_wrap.h"
+
#include <string.h>
#include <stdlib.h>
@@ -36,6 +40,7 @@ using v8::HandleScope;
using v8::FunctionTemplate;
using v8::String;
using v8::Array;
+using v8::Number;
using v8::Function;
using v8::TryCatch;
using v8::Context;
@@ -82,6 +87,56 @@ class ProcessWrap : public HandleWrap {
ProcessWrap(Handle<Object> object) : HandleWrap(object, NULL) { }
~ProcessWrap() { }
+ static void ParseStdioOptions(Local<Object> js_options,
+ uv_process_options_t* options) {
+ Local<Array> stdios = js_options
+ ->Get(String::NewSymbol("stdio")).As<Array>();
+ int len = stdios->Length();
+ options->stdio = new uv_stdio_container_t[len];
+ options->stdio_count = len;
+
+ for (int i = 0; i < len; i++) {
+ Local<Object> stdio = stdios
+ ->Get(Number::New(static_cast<double>(i))).As<Object>();
+ Local<Value> type = stdio->Get(String::NewSymbol("type"));
+
+ if (type->Equals(String::NewSymbol("ignore"))) {
+ options->stdio[i].flags = UV_IGNORE;
+ } else if (type->Equals(String::NewSymbol("pipe"))) {
+ options->stdio[i].flags = UV_CREATE_PIPE;
+ options->stdio[i].data.stream = reinterpret_cast<uv_stream_t*>(
+ PipeWrap::Unwrap(stdio
+ ->Get(String::NewSymbol("handle")).As<Object>())->UVHandle());
+ } else if (type->Equals(String::NewSymbol("wrap"))) {
+ uv_stream_t* stream = NULL;
+ Local<Value> wrapType = stdio->Get(String::NewSymbol("wrapType"));
+ if (wrapType->Equals(String::NewSymbol("pipe"))) {
+ stream = reinterpret_cast<uv_stream_t*>(PipeWrap::Unwrap(stdio
+ ->Get(String::NewSymbol("handle")).As<Object>())->UVHandle());
+ } else if (wrapType->Equals(String::NewSymbol("tty"))) {
+ stream = reinterpret_cast<uv_stream_t*>(TTYWrap::Unwrap(stdio
+ ->Get(String::NewSymbol("handle")).As<Object>())->UVHandle());
+ } else if (wrapType->Equals(String::NewSymbol("tcp"))) {
+ stream = reinterpret_cast<uv_stream_t*>(TCPWrap::Unwrap(stdio
+ ->Get(String::NewSymbol("handle")).As<Object>())->UVHandle());
+ } else if (wrapType->Equals(String::NewSymbol("udp"))) {
+ stream = reinterpret_cast<uv_stream_t*>(UDPWrap::Unwrap(stdio
+ ->Get(String::NewSymbol("handle")).As<Object>())->UVHandle());
+ }
+ assert(stream != NULL);
+
+ options->stdio[i].flags = UV_INHERIT_STREAM;
+ options->stdio[i].data.stream = stream;
+ } else {
+ int fd = static_cast<int>(
+ stdio->Get(String::NewSymbol("fd"))->IntegerValue());
+
+ options->stdio[i].flags = UV_INHERIT_FD;
+ options->stdio[i].data.fd = fd;
+ }
+ }
+ }
+
static Handle<Value> Spawn(const Arguments& args) {
HandleScope scope;
@@ -169,47 +224,8 @@ class ProcessWrap : public HandleWrap {
options.env[envc] = NULL;
}
- uv_stdio_container_t stdio[3];
- memset(stdio, 0, sizeof(stdio));
-
- options.stdio = stdio;
- options.stdio_count = 3;
- options.stdio[0].flags = UV_IGNORE;
- options.stdio[1].flags = UV_IGNORE;
- options.stdio[2].flags = UV_IGNORE;
-
- // options.stdin_stream
- Local<Value> stdin_stream_v = js_options->Get(
- String::NewSymbol("stdinStream"));
- if (!stdin_stream_v.IsEmpty() && stdin_stream_v->IsObject()) {
- PipeWrap* stdin_wrap = PipeWrap::Unwrap(stdin_stream_v->ToObject());
- options.stdio[0].flags = static_cast<uv_stdio_flags>(
- UV_CREATE_PIPE | UV_WRITABLE_PIPE);
- options.stdio[0].data.stream = reinterpret_cast<uv_stream_t*>(
- stdin_wrap->UVHandle());
- }
-
- // options.stdout_stream
- Local<Value> stdout_stream_v = js_options->Get(
- String::NewSymbol("stdoutStream"));
- if (!stdout_stream_v.IsEmpty() && stdout_stream_v->IsObject()) {
- PipeWrap* stdout_wrap = PipeWrap::Unwrap(stdout_stream_v->ToObject());
- options.stdio[1].flags = static_cast<uv_stdio_flags>(
- UV_CREATE_PIPE | UV_READABLE_PIPE);
- options.stdio[1].data.stream = reinterpret_cast<uv_stream_t*>(
- stdout_wrap->UVHandle());
- }
-
- // options.stderr_stream
- Local<Value> stderr_stream_v = js_options->Get(
- String::NewSymbol("stderrStream"));
- if (!stderr_stream_v.IsEmpty() && stderr_stream_v->IsObject()) {
- PipeWrap* stderr_wrap = PipeWrap::Unwrap(stderr_stream_v->ToObject());
- options.stdio[2].flags = static_cast<uv_stdio_flags>(
- UV_CREATE_PIPE | UV_READABLE_PIPE);
- options.stdio[2].data.stream = reinterpret_cast<uv_stream_t*>(
- stderr_wrap->UVHandle());
- }
+ // options.stdio
+ ParseStdioOptions(js_options, &options);
// options.windows_verbatim_arguments
if (js_options->Get(String::NewSymbol("windowsVerbatimArguments"))->
@@ -238,6 +254,8 @@ class ProcessWrap : public HandleWrap {
delete [] options.env;
}
+ delete[] options.stdio;
+
return scope.Close(Integer::New(r));
}
diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc
index 3fc8aa80cb..5e6f1c2ec1 100644
--- a/src/tcp_wrap.cc
+++ b/src/tcp_wrap.cc
@@ -128,6 +128,18 @@ void TCPWrap::Initialize(Handle<Object> target) {
}
+TCPWrap* TCPWrap::Unwrap(Local<Object> obj) {
+ assert(!obj.IsEmpty());
+ assert(obj->InternalFieldCount() > 0);
+ return static_cast<TCPWrap*>(obj->GetPointerFromInternalField(0));
+}
+
+
+uv_tcp_t* TCPWrap::UVHandle() {
+ return &handle_;
+}
+
+
Handle<Value> TCPWrap::New(const Arguments& args) {
// This constructor should not be exposed to public javascript.
// Therefore we assert that we are not trying to call this as a
diff --git a/src/tcp_wrap.h b/src/tcp_wrap.h
index 26e061f483..9b8cd29140 100644
--- a/src/tcp_wrap.h
+++ b/src/tcp_wrap.h
@@ -31,6 +31,8 @@ class TCPWrap : public StreamWrap {
static TCPWrap* Unwrap(v8::Local<v8::Object> obj);
static void Initialize(v8::Handle<v8::Object> target);
+ uv_tcp_t* UVHandle();
+
private:
TCPWrap(v8::Handle<v8::Object> object);
~TCPWrap();
diff --git a/src/tty_wrap.cc b/src/tty_wrap.cc
index de43a4f23c..f1a189c91e 100644
--- a/src/tty_wrap.cc
+++ b/src/tty_wrap.cc
@@ -24,6 +24,7 @@
#include "req_wrap.h"
#include "handle_wrap.h"
#include "stream_wrap.h"
+#include "tty_wrap.h"
namespace node {
@@ -42,128 +43,141 @@ using v8::Arguments;
using v8::Integer;
using v8::Undefined;
-class TTYWrap : StreamWrap {
- public:
- static void Initialize(Handle<Object> target) {
- StreamWrap::Initialize(target);
- HandleScope scope;
+void TTYWrap::Initialize(Handle<Object> target) {
+ StreamWrap::Initialize(target);
- Local<FunctionTemplate> t = FunctionTemplate::New(New);
- t->SetClassName(String::NewSymbol("TTY"));
+ HandleScope scope;
- t->InstanceTemplate()->SetInternalFieldCount(1);
+ Local<FunctionTemplate> t = FunctionTemplate::New(New);
+ t->SetClassName(String::NewSymbol("TTY"));
- NODE_SET_PROTOTYPE_METHOD(t, "close", HandleWrap::Close);
- NODE_SET_PROTOTYPE_METHOD(t, "unref", HandleWrap::Unref);
+ t->InstanceTemplate()->SetInternalFieldCount(1);
- NODE_SET_PROTOTYPE_METHOD(t, "readStart", StreamWrap::ReadStart);
- NODE_SET_PROTOTYPE_METHOD(t, "readStop", StreamWrap::ReadStop);
+ NODE_SET_PROTOTYPE_METHOD(t, "close", HandleWrap::Close);
+ NODE_SET_PROTOTYPE_METHOD(t, "unref", HandleWrap::Unref);
- NODE_SET_PROTOTYPE_METHOD(t, "writeBuffer", StreamWrap::WriteBuffer);
- NODE_SET_PROTOTYPE_METHOD(t, "writeAsciiString", StreamWrap::WriteAsciiString);
- NODE_SET_PROTOTYPE_METHOD(t, "writeUtf8String", StreamWrap::WriteUtf8String);
- NODE_SET_PROTOTYPE_METHOD(t, "writeUcs2String", StreamWrap::WriteUcs2String);
+ NODE_SET_PROTOTYPE_METHOD(t, "readStart", StreamWrap::ReadStart);
+ NODE_SET_PROTOTYPE_METHOD(t, "readStop", StreamWrap::ReadStop);
- NODE_SET_PROTOTYPE_METHOD(t, "getWindowSize", TTYWrap::GetWindowSize);
- NODE_SET_PROTOTYPE_METHOD(t, "setRawMode", SetRawMode);
+ NODE_SET_PROTOTYPE_METHOD(t, "writeBuffer", StreamWrap::WriteBuffer);
+ NODE_SET_PROTOTYPE_METHOD(t, "writeAsciiString", StreamWrap::WriteAsciiString);
+ NODE_SET_PROTOTYPE_METHOD(t, "writeUtf8String", StreamWrap::WriteUtf8String);
+ NODE_SET_PROTOTYPE_METHOD(t, "writeUcs2String", StreamWrap::WriteUcs2String);
- NODE_SET_METHOD(target, "isTTY", IsTTY);
- NODE_SET_METHOD(target, "guessHandleType", GuessHandleType);
+ NODE_SET_PROTOTYPE_METHOD(t, "getWindowSize", TTYWrap::GetWindowSize);
+ NODE_SET_PROTOTYPE_METHOD(t, "setRawMode", SetRawMode);
- target->Set(String::NewSymbol("TTY"), t->GetFunction());
- }
+ NODE_SET_METHOD(target, "isTTY", IsTTY);
+ NODE_SET_METHOD(target, "guessHandleType", GuessHandleType);
- private:
- static Handle<Value> GuessHandleType(const Arguments& args) {
- HandleScope scope;
- int fd = args[0]->Int32Value();
- assert(fd >= 0);
+ target->Set(String::NewSymbol("TTY"), t->GetFunction());
+}
- uv_handle_type t = uv_guess_handle(fd);
- switch (t) {
- case UV_TTY:
- return scope.Close(String::New("TTY"));
+TTYWrap* TTYWrap::Unwrap(Local<Object> obj) {
+ assert(!obj.IsEmpty());
+ assert(obj->InternalFieldCount() > 0);
+ return static_cast<TTYWrap*>(obj->GetPointerFromInternalField(0));
+}
- case UV_NAMED_PIPE:
- return scope.Close(String::New("PIPE"));
- case UV_FILE:
- return scope.Close(String::New("FILE"));
+uv_tty_t* TTYWrap::UVHandle() {
+ return &handle_;
+}
- default:
- assert(0);
- return v8::Undefined();
- }
- }
- static Handle<Value> IsTTY(const Arguments& args) {
- HandleScope scope;
- int fd = args[0]->Int32Value();
- assert(fd >= 0);
- return uv_guess_handle(fd) == UV_TTY ? v8::True() : v8::False();
- }
+Handle<Value> TTYWrap::GuessHandleType(const Arguments& args) {
+ HandleScope scope;
+ int fd = args[0]->Int32Value();
+ assert(fd >= 0);
+
+ uv_handle_type t = uv_guess_handle(fd);
- static Handle<Value> GetWindowSize(const Arguments& args) {
- HandleScope scope;
+ switch (t) {
+ case UV_TTY:
+ return scope.Close(String::New("TTY"));
- UNWRAP(TTYWrap)
+ case UV_NAMED_PIPE:
+ return scope.Close(String::New("PIPE"));
- int width, height;
- int r = uv_tty_get_winsize(&wrap->handle_, &width, &height);
+ case UV_FILE:
+ return scope.Close(String::New("FILE"));
- if (r) {
- SetErrno(uv_last_error(uv_default_loop()));
+ default:
+ assert(0);
return v8::Undefined();
- }
+ }
+}
- Local<v8::Array> a = v8::Array::New(2);
- a->Set(0, Integer::New(width));
- a->Set(1, Integer::New(height));
- return scope.Close(a);
- }
+Handle<Value> TTYWrap::IsTTY(const Arguments& args) {
+ HandleScope scope;
+ int fd = args[0]->Int32Value();
+ assert(fd >= 0);
+ return uv_guess_handle(fd) == UV_TTY ? v8::True() : v8::False();
+}
- static Handle<Value> SetRawMode(const Arguments& args) {
- HandleScope scope;
- UNWRAP(TTYWrap)
+Handle<Value> TTYWrap::GetWindowSize(const Arguments& args) {
+ HandleScope scope;
- int r = uv_tty_set_mode(&wrap->handle_, args[0]->IsTrue());
+ UNWRAP(TTYWrap)
- if (r) {
- SetErrno(uv_last_error(uv_default_loop()));
- }
+ int width, height;
+ int r = uv_tty_get_winsize(&wrap->handle_, &width, &height);
- return scope.Close(Integer::New(r));
+ if (r) {
+ SetErrno(uv_last_error(uv_default_loop()));
+ return v8::Undefined();
}
- static Handle<Value> New(const Arguments& args) {
- HandleScope scope;
+ Local<v8::Array> a = v8::Array::New(2);
+ a->Set(0, Integer::New(width));
+ a->Set(1, Integer::New(height));
- // This constructor should not be exposed to public javascript.
- // Therefore we assert that we are not trying to call this as a
- // normal function.
- assert(args.IsConstructCall());
+ return scope.Close(a);
+}
- int fd = args[0]->Int32Value();
- assert(fd >= 0);
- TTYWrap* wrap = new TTYWrap(args.This(), fd, args[1]->IsTrue());
- assert(wrap);
- wrap->UpdateWriteQueueSize();
+Handle<Value> TTYWrap::SetRawMode(const Arguments& args) {
+ HandleScope scope;
- return scope.Close(args.This());
- }
+ UNWRAP(TTYWrap)
+
+ int r = uv_tty_set_mode(&wrap->handle_, args[0]->IsTrue());
- TTYWrap(Handle<Object> object, int fd, bool readable)
- : StreamWrap(object, (uv_stream_t*)&handle_) {
- uv_tty_init(uv_default_loop(), &handle_, fd, readable);
+ if (r) {
+ SetErrno(uv_last_error(uv_default_loop()));
}
- uv_tty_t handle_;
-};
+ return scope.Close(Integer::New(r));
+}
+
+
+Handle<Value> TTYWrap::New(const Arguments& args) {
+ HandleScope scope;
+
+ // This constructor should not be exposed to public javascript.
+ // Therefore we assert that we are not trying to call this as a
+ // normal function.
+ assert(args.IsConstructCall());
+
+ int fd = args[0]->Int32Value();
+ assert(fd >= 0);
+
+ TTYWrap* wrap = new TTYWrap(args.This(), fd, args[1]->IsTrue());
+ assert(wrap);
+ wrap->UpdateWriteQueueSize();
+
+ return scope.Close(args.This());
+}
+
+
+TTYWrap::TTYWrap(Handle<Object> object, int fd, bool readable)
+ : StreamWrap(object, (uv_stream_t*)&handle_) {
+ uv_tty_init(uv_default_loop(), &handle_, fd, readable);
+}
} // namespace node
diff --git a/src/tty_wrap.h b/src/tty_wrap.h
new file mode 100644
index 0000000000..4a3341a660
--- /dev/null
+++ b/src/tty_wrap.h
@@ -0,0 +1,58 @@
+// Copyright Joyent, Inc. 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 TTY_WRAP_H_
+#define TTY_WRAP_H_
+
+#include "handle_wrap.h"
+#include "stream_wrap.h"
+
+namespace node {
+
+using v8::Object;
+using v8::Handle;
+using v8::Local;
+using v8::Value;
+using v8::Arguments;
+
+
+class TTYWrap : StreamWrap {
+ public:
+ static void Initialize(Handle<Object> target);
+ static TTYWrap* Unwrap(Local<Object> obj);
+
+ uv_tty_t* UVHandle();
+
+ private:
+ TTYWrap(Handle<Object> object, int fd, bool readable);
+
+ static Handle<Value> GuessHandleType(const Arguments& args);
+ static Handle<Value> IsTTY(const Arguments& args);
+ static Handle<Value> GetWindowSize(const Arguments& args);
+ static Handle<Value> SetRawMode(const Arguments& args);
+ static Handle<Value> New(const Arguments& args);
+
+ uv_tty_t handle_;
+};
+
+} // namespace node
+
+#endif // TTY_WRAP_H_
diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc
index 39ab750215..b0f8bad1f8 100644
--- a/src/udp_wrap.cc
+++ b/src/udp_wrap.cc
@@ -24,6 +24,7 @@
#include "slab_allocator.h"
#include "req_wrap.h"
#include "handle_wrap.h"
+#include "udp_wrap.h"
#include <stdlib.h>
@@ -60,45 +61,6 @@ static Persistent<String> onmessage_sym;
static SlabAllocator slab_allocator(SLAB_SIZE);
-class UDPWrap: public HandleWrap {
-public:
- static void Initialize(Handle<Object> target);
- static Handle<Value> New(const Arguments& args);
- static Handle<Value> Bind(const Arguments& args);
- static Handle<Value> Send(const Arguments& args);
- static Handle<Value> Bind6(const Arguments& args);
- static Handle<Value> Send6(const Arguments& args);
- static Handle<Value> RecvStart(const Arguments& args);
- static Handle<Value> RecvStop(const Arguments& args);
- static Handle<Value> GetSockName(const Arguments& args);
- static Handle<Value> AddMembership(const Arguments& args);
- static Handle<Value> DropMembership(const Arguments& args);
- static Handle<Value> SetMulticastTTL(const Arguments& args);
- static Handle<Value> SetMulticastLoopback(const Arguments& args);
- static Handle<Value> SetBroadcast(const Arguments& args);
- static Handle<Value> SetTTL(const Arguments& args);
-
-private:
- UDPWrap(Handle<Object> object);
- virtual ~UDPWrap();
-
- static Handle<Value> DoBind(const Arguments& args, int family);
- static Handle<Value> DoSend(const Arguments& args, int family);
- static Handle<Value> SetMembership(const Arguments& args,
- uv_membership membership);
-
- static uv_buf_t OnAlloc(uv_handle_t* handle, size_t suggested_size);
- static void OnSend(uv_udp_send_t* req, int status);
- static void OnRecv(uv_udp_t* handle,
- ssize_t nread,
- uv_buf_t buf,
- struct sockaddr* addr,
- unsigned flags);
-
- uv_udp_t handle_;
-};
-
-
UDPWrap::UDPWrap(Handle<Object> object): HandleWrap(object,
(uv_handle_t*)&handle_) {
int r = uv_udp_init(uv_default_loop(), &handle_);
@@ -426,6 +388,18 @@ void UDPWrap::OnRecv(uv_udp_t* handle,
}
+UDPWrap* UDPWrap::Unwrap(Local<Object> obj) {
+ assert(!obj.IsEmpty());
+ assert(obj->InternalFieldCount() > 0);
+ return static_cast<UDPWrap*>(obj->GetPointerFromInternalField(0));
+}
+
+
+uv_udp_t* UDPWrap::UVHandle() {
+ return &handle_;
+}
+
+
} // namespace node
NODE_MODULE(node_udp_wrap, node::UDPWrap::Initialize)
diff --git a/src/udp_wrap.h b/src/udp_wrap.h
new file mode 100644
index 0000000000..9ca2eaea90
--- /dev/null
+++ b/src/udp_wrap.h
@@ -0,0 +1,60 @@
+#ifndef UDP_WRAP_H_
+#define UDP_WRAP_H_
+
+#include "node.h"
+#include "req_wrap.h"
+#include "handle_wrap.h"
+
+namespace node {
+
+using v8::Object;
+using v8::Handle;
+using v8::Local;
+using v8::Value;
+using v8::String;
+using v8::Arguments;
+
+class UDPWrap: public HandleWrap {
+ public:
+ static void Initialize(Handle<Object> target);
+ static Handle<Value> New(const Arguments& args);
+ static Handle<Value> Bind(const Arguments& args);
+ static Handle<Value> Send(const Arguments& args);
+ static Handle<Value> Bind6(const Arguments& args);
+ static Handle<Value> Send6(const Arguments& args);
+ static Handle<Value> RecvStart(const Arguments& args);
+ static Handle<Value> RecvStop(const Arguments& args);
+ static Handle<Value> GetSockName(const Arguments& args);
+ static Handle<Value> AddMembership(const Arguments& args);
+ static Handle<Value> DropMembership(const Arguments& args);
+ static Handle<Value> SetMulticastTTL(const Arguments& args);
+ static Handle<Value> SetMulticastLoopback(const Arguments& args);
+ static Handle<Value> SetBroadcast(const Arguments& args);
+ static Handle<Value> SetTTL(const Arguments& args);
+ static UDPWrap* Unwrap(Local<Object> obj);
+
+ uv_udp_t* UVHandle();
+
+ private:
+ UDPWrap(Handle<Object> object);
+ virtual ~UDPWrap();
+
+ static Handle<Value> DoBind(const Arguments& args, int family);
+ static Handle<Value> DoSend(const Arguments& args, int family);
+ static Handle<Value> SetMembership(const Arguments& args,
+ uv_membership membership);
+
+ static uv_buf_t OnAlloc(uv_handle_t* handle, size_t suggested_size);
+ static void OnSend(uv_udp_send_t* req, int status);
+ static void OnRecv(uv_udp_t* handle,
+ ssize_t nread,
+ uv_buf_t buf,
+ struct sockaddr* addr,
+ unsigned flags);
+
+ uv_udp_t handle_;
+};
+
+} // namespace node
+
+#endif // UDP_WRAP_H_
diff --git a/test/simple/test-process-wrap.js b/test/simple/test-process-wrap.js
index 1abf196ee9..655c828158 100644
--- a/test/simple/test-process-wrap.js
+++ b/test/simple/test-process-wrap.js
@@ -58,7 +58,11 @@ pipe.onread = function(b, off, len) {
p.spawn({
file: process.execPath,
args: [process.execPath, '-v'],
- stdoutStream: pipe
+ stdio: [
+ { type: 'ignore' },
+ { type: 'pipe', handle: pipe },
+ { type: 'ignore' }
+ ]
});