Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

2931 lines (2310 sloc) 74.254 kb
// 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.
#include "node.h"
#include "node_internals.h"
#include "req_wrap.h"
#include "handle_wrap.h"
#include "uv.h"
#include "v8-debug.h"
#if defined HAVE_DTRACE || defined HAVE_ETW
# include "node_dtrace.h"
#endif
#include <locale.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if !defined(_MSC_VER)
#include <strings.h>
#else
#define strcasecmp _stricmp
#endif
#include <limits.h> /* PATH_MAX */
#include <assert.h>
#if !defined(_MSC_VER)
#include <unistd.h> /* setuid, getuid */
#else
#include <direct.h>
#include <process.h>
#define getpid _getpid
#include <io.h>
#define umask _umask
typedef int mode_t;
#endif
#include <errno.h>
#include <sys/types.h>
#include "zlib.h"
#ifdef __POSIX__
# include <pwd.h> /* getpwnam() */
# include <grp.h> /* getgrnam() */
#endif
#include "node_buffer.h"
#ifdef __POSIX__
# include "node_io_watcher.h"
#endif
#include "node_file.h"
#include "node_http_parser.h"
#ifdef __POSIX__
# include "node_signal_watcher.h"
#endif
#include "node_constants.h"
#include "node_javascript.h"
#include "node_version.h"
#include "node_string.h"
#if HAVE_OPENSSL
# include "node_crypto.h"
#endif
#include "node_script.h"
#include "v8_typed_array.h"
using namespace v8;
# ifdef __APPLE__
# include <crt_externs.h>
# define environ (*_NSGetEnviron())
# elif !defined(_MSC_VER)
extern char **environ;
# endif
namespace node {
ngx_queue_t handle_wrap_queue = { &handle_wrap_queue, &handle_wrap_queue };
ngx_queue_t req_wrap_queue = { &req_wrap_queue, &req_wrap_queue };
// declared in req_wrap.h
Persistent<String> process_symbol;
Persistent<String> domain_symbol;
static Persistent<Object> process;
static Persistent<String> errno_symbol;
static Persistent<String> syscall_symbol;
static Persistent<String> errpath_symbol;
static Persistent<String> code_symbol;
static Persistent<String> rss_symbol;
static Persistent<String> heap_total_symbol;
static Persistent<String> heap_used_symbol;
static Persistent<String> listeners_symbol;
static Persistent<String> uncaught_exception_symbol;
static Persistent<String> emit_symbol;
static Persistent<String> enter_symbol;
static Persistent<String> exit_symbol;
static Persistent<String> disposed_symbol;
static bool print_eval = false;
static bool force_repl = false;
static bool trace_deprecation = false;
static char *eval_string = NULL;
static int option_end_index = 0;
static bool use_debug_agent = false;
static bool debug_wait_connect = false;
static int debug_port=5858;
static int max_stack_size = 0;
// used by C++ modules as well
bool no_deprecation = false;
static uv_check_t check_tick_watcher;
static uv_prepare_t prepare_tick_watcher;
static uv_idle_t tick_spinner;
static bool need_tick_cb;
static Persistent<String> tick_callback_sym;
#ifdef OPENSSL_NPN_NEGOTIATED
static bool use_npn = true;
#else
static bool use_npn = false;
#endif
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
static bool use_sni = true;
#else
static bool use_sni = false;
#endif
#ifdef __POSIX__
// Buffer for getpwnam_r(), getgrpam_r() and other misc callers; keep this
// scoped at file-level rather than method-level to avoid excess stack usage.
static char getbuf[PATH_MAX + 1];
#endif
// We need to notify V8 when we're idle so that it can run the garbage
// collector. The interface to this is V8::IdleNotification(). It returns
// true if the heap hasn't be fully compacted, and needs to be run again.
// Returning false means that it doesn't have anymore work to do.
//
// A rather convoluted algorithm has been devised to determine when Node is
// idle. You'll have to figure it out for yourself.
static uv_check_t gc_check;
static uv_idle_t gc_idle;
static uv_timer_t gc_timer;
bool need_gc;
// process-relative uptime base, initialized at start-up
static double prog_start_time;
#define FAST_TICK 700.
#define GC_WAIT_TIME 5000.
#define RPM_SAMPLES 100
#define TICK_TIME(n) tick_times[(tick_time_head - (n)) % RPM_SAMPLES]
static int64_t tick_times[RPM_SAMPLES];
static int tick_time_head;
static void CheckStatus(uv_timer_t* watcher, int status);
static void StartGCTimer () {
if (!uv_is_active((uv_handle_t*) &gc_timer)) {
uv_timer_start(&gc_timer, node::CheckStatus, 5000, 5000);
}
}
static void StopGCTimer () {
if (uv_is_active((uv_handle_t*) &gc_timer)) {
uv_timer_stop(&gc_timer);
}
}
static void Idle(uv_idle_t* watcher, int status) {
assert((uv_idle_t*) watcher == &gc_idle);
if (V8::IdleNotification()) {
uv_idle_stop(&gc_idle);
StopGCTimer();
}
}
// Called directly after every call to select() (or epoll, or whatever)
static void Check(uv_check_t* watcher, int status) {
assert(watcher == &gc_check);
tick_times[tick_time_head] = uv_now(uv_default_loop());
tick_time_head = (tick_time_head + 1) % RPM_SAMPLES;
StartGCTimer();
for (int i = 0; i < (int)(GC_WAIT_TIME/FAST_TICK); i++) {
double d = TICK_TIME(i+1) - TICK_TIME(i+2);
//printf("d = %f\n", d);
// If in the last 5 ticks the difference between
// ticks was less than 0.7 seconds, then continue.
if (d < FAST_TICK) {
//printf("---\n");
return;
}
}
// Otherwise start the gc!
//fprintf(stderr, "start idle 2\n");
uv_idle_start(&gc_idle, node::Idle);
}
static void Tick(void) {
// Avoid entering a V8 scope.
if (!need_tick_cb) return;
need_tick_cb = false;
uv_idle_stop(&tick_spinner);
HandleScope scope;
if (tick_callback_sym.IsEmpty()) {
// Lazily set the symbol
tick_callback_sym = NODE_PSYMBOL("_tickCallback");
}
Local<Value> cb_v = process->Get(tick_callback_sym);
if (!cb_v->IsFunction()) return;
Local<Function> cb = Local<Function>::Cast(cb_v);
TryCatch try_catch;
cb->Call(process, 0, NULL);
if (try_catch.HasCaught()) {
FatalException(try_catch);
}
}
static void Spin(uv_idle_t* handle, int status) {
assert((uv_idle_t*) handle == &tick_spinner);
assert(status == 0);
Tick();
}
static void StartTickSpinner() {
need_tick_cb = true;
// TODO: this tick_spinner shouldn't be necessary. An ev_prepare should be
// sufficent, the problem is only in the case of the very last "tick" -
// there is nothing left to do in the event loop and libev will exit. The
// ev_prepare callback isn't called before exiting. Thus we start this
// tick_spinner to keep the event loop alive long enough to handle it.
uv_idle_start(&tick_spinner, Spin);
}
static Handle<Value> NeedTickCallback(const Arguments& args) {
StartTickSpinner();
return Undefined();
}
static void PrepareTick(uv_prepare_t* handle, int status) {
assert(handle == &prepare_tick_watcher);
assert(status == 0);
Tick();
}
static void CheckTick(uv_check_t* handle, int status) {
assert(handle == &check_tick_watcher);
assert(status == 0);
Tick();
}
static inline const char *errno_string(int errorno) {
#define ERRNO_CASE(e) case e: return #e;
switch (errorno) {
#ifdef EACCES
ERRNO_CASE(EACCES);
#endif
#ifdef EADDRINUSE
ERRNO_CASE(EADDRINUSE);
#endif
#ifdef EADDRNOTAVAIL
ERRNO_CASE(EADDRNOTAVAIL);
#endif
#ifdef EAFNOSUPPORT
ERRNO_CASE(EAFNOSUPPORT);
#endif
#ifdef EAGAIN
ERRNO_CASE(EAGAIN);
#endif
#ifdef EWOULDBLOCK
# if EAGAIN != EWOULDBLOCK
ERRNO_CASE(EWOULDBLOCK);
# endif
#endif
#ifdef EALREADY
ERRNO_CASE(EALREADY);
#endif
#ifdef EBADF
ERRNO_CASE(EBADF);
#endif
#ifdef EBADMSG
ERRNO_CASE(EBADMSG);
#endif
#ifdef EBUSY
ERRNO_CASE(EBUSY);
#endif
#ifdef ECANCELED
ERRNO_CASE(ECANCELED);
#endif
#ifdef ECHILD
ERRNO_CASE(ECHILD);
#endif
#ifdef ECONNABORTED
ERRNO_CASE(ECONNABORTED);
#endif
#ifdef ECONNREFUSED
ERRNO_CASE(ECONNREFUSED);
#endif
#ifdef ECONNRESET
ERRNO_CASE(ECONNRESET);
#endif
#ifdef EDEADLK
ERRNO_CASE(EDEADLK);
#endif
#ifdef EDESTADDRREQ
ERRNO_CASE(EDESTADDRREQ);
#endif
#ifdef EDOM
ERRNO_CASE(EDOM);
#endif
#ifdef EDQUOT
ERRNO_CASE(EDQUOT);
#endif
#ifdef EEXIST
ERRNO_CASE(EEXIST);
#endif
#ifdef EFAULT
ERRNO_CASE(EFAULT);
#endif
#ifdef EFBIG
ERRNO_CASE(EFBIG);
#endif
#ifdef EHOSTUNREACH
ERRNO_CASE(EHOSTUNREACH);
#endif
#ifdef EIDRM
ERRNO_CASE(EIDRM);
#endif
#ifdef EILSEQ
ERRNO_CASE(EILSEQ);
#endif
#ifdef EINPROGRESS
ERRNO_CASE(EINPROGRESS);
#endif
#ifdef EINTR
ERRNO_CASE(EINTR);
#endif
#ifdef EINVAL
ERRNO_CASE(EINVAL);
#endif
#ifdef EIO
ERRNO_CASE(EIO);
#endif
#ifdef EISCONN
ERRNO_CASE(EISCONN);
#endif
#ifdef EISDIR
ERRNO_CASE(EISDIR);
#endif
#ifdef ELOOP
ERRNO_CASE(ELOOP);
#endif
#ifdef EMFILE
ERRNO_CASE(EMFILE);
#endif
#ifdef EMLINK
ERRNO_CASE(EMLINK);
#endif
#ifdef EMSGSIZE
ERRNO_CASE(EMSGSIZE);
#endif
#ifdef EMULTIHOP
ERRNO_CASE(EMULTIHOP);
#endif
#ifdef ENAMETOOLONG
ERRNO_CASE(ENAMETOOLONG);
#endif
#ifdef ENETDOWN
ERRNO_CASE(ENETDOWN);
#endif
#ifdef ENETRESET
ERRNO_CASE(ENETRESET);
#endif
#ifdef ENETUNREACH
ERRNO_CASE(ENETUNREACH);
#endif
#ifdef ENFILE
ERRNO_CASE(ENFILE);
#endif
#ifdef ENOBUFS
ERRNO_CASE(ENOBUFS);
#endif
#ifdef ENODATA
ERRNO_CASE(ENODATA);
#endif
#ifdef ENODEV
ERRNO_CASE(ENODEV);
#endif
#ifdef ENOENT
ERRNO_CASE(ENOENT);
#endif
#ifdef ENOEXEC
ERRNO_CASE(ENOEXEC);
#endif
#ifdef ENOLINK
ERRNO_CASE(ENOLINK);
#endif
#ifdef ENOLCK
# if ENOLINK != ENOLCK
ERRNO_CASE(ENOLCK);
# endif
#endif
#ifdef ENOMEM
ERRNO_CASE(ENOMEM);
#endif
#ifdef ENOMSG
ERRNO_CASE(ENOMSG);
#endif
#ifdef ENOPROTOOPT
ERRNO_CASE(ENOPROTOOPT);
#endif
#ifdef ENOSPC
ERRNO_CASE(ENOSPC);
#endif
#ifdef ENOSR
ERRNO_CASE(ENOSR);
#endif
#ifdef ENOSTR
ERRNO_CASE(ENOSTR);
#endif
#ifdef ENOSYS
ERRNO_CASE(ENOSYS);
#endif
#ifdef ENOTCONN
ERRNO_CASE(ENOTCONN);
#endif
#ifdef ENOTDIR
ERRNO_CASE(ENOTDIR);
#endif
#ifdef ENOTEMPTY
ERRNO_CASE(ENOTEMPTY);
#endif
#ifdef ENOTSOCK
ERRNO_CASE(ENOTSOCK);
#endif
#ifdef ENOTSUP
ERRNO_CASE(ENOTSUP);
#else
# ifdef EOPNOTSUPP
ERRNO_CASE(EOPNOTSUPP);
# endif
#endif
#ifdef ENOTTY
ERRNO_CASE(ENOTTY);
#endif
#ifdef ENXIO
ERRNO_CASE(ENXIO);
#endif
#ifdef EOVERFLOW
ERRNO_CASE(EOVERFLOW);
#endif
#ifdef EPERM
ERRNO_CASE(EPERM);
#endif
#ifdef EPIPE
ERRNO_CASE(EPIPE);
#endif
#ifdef EPROTO
ERRNO_CASE(EPROTO);
#endif
#ifdef EPROTONOSUPPORT
ERRNO_CASE(EPROTONOSUPPORT);
#endif
#ifdef EPROTOTYPE
ERRNO_CASE(EPROTOTYPE);
#endif
#ifdef ERANGE
ERRNO_CASE(ERANGE);
#endif
#ifdef EROFS
ERRNO_CASE(EROFS);
#endif
#ifdef ESPIPE
ERRNO_CASE(ESPIPE);
#endif
#ifdef ESRCH
ERRNO_CASE(ESRCH);
#endif
#ifdef ESTALE
ERRNO_CASE(ESTALE);
#endif
#ifdef ETIME
ERRNO_CASE(ETIME);
#endif
#ifdef ETIMEDOUT
ERRNO_CASE(ETIMEDOUT);
#endif
#ifdef ETXTBSY
ERRNO_CASE(ETXTBSY);
#endif
#ifdef EXDEV
ERRNO_CASE(EXDEV);
#endif
default: return "";
}
}
const char *signo_string(int signo) {
#define SIGNO_CASE(e) case e: return #e;
switch (signo) {
#ifdef SIGHUP
SIGNO_CASE(SIGHUP);
#endif
#ifdef SIGINT
SIGNO_CASE(SIGINT);
#endif
#ifdef SIGQUIT
SIGNO_CASE(SIGQUIT);
#endif
#ifdef SIGILL
SIGNO_CASE(SIGILL);
#endif
#ifdef SIGTRAP
SIGNO_CASE(SIGTRAP);
#endif
#ifdef SIGABRT
SIGNO_CASE(SIGABRT);
#endif
#ifdef SIGIOT
# if SIGABRT != SIGIOT
SIGNO_CASE(SIGIOT);
# endif
#endif
#ifdef SIGBUS
SIGNO_CASE(SIGBUS);
#endif
#ifdef SIGFPE
SIGNO_CASE(SIGFPE);
#endif
#ifdef SIGKILL
SIGNO_CASE(SIGKILL);
#endif
#ifdef SIGUSR1
SIGNO_CASE(SIGUSR1);
#endif
#ifdef SIGSEGV
SIGNO_CASE(SIGSEGV);
#endif
#ifdef SIGUSR2
SIGNO_CASE(SIGUSR2);
#endif
#ifdef SIGPIPE
SIGNO_CASE(SIGPIPE);
#endif
#ifdef SIGALRM
SIGNO_CASE(SIGALRM);
#endif
SIGNO_CASE(SIGTERM);
#ifdef SIGCHLD
SIGNO_CASE(SIGCHLD);
#endif
#ifdef SIGSTKFLT
SIGNO_CASE(SIGSTKFLT);
#endif
#ifdef SIGCONT
SIGNO_CASE(SIGCONT);
#endif
#ifdef SIGSTOP
SIGNO_CASE(SIGSTOP);
#endif
#ifdef SIGTSTP
SIGNO_CASE(SIGTSTP);
#endif
#ifdef SIGTTIN
SIGNO_CASE(SIGTTIN);
#endif
#ifdef SIGTTOU
SIGNO_CASE(SIGTTOU);
#endif
#ifdef SIGURG
SIGNO_CASE(SIGURG);
#endif
#ifdef SIGXCPU
SIGNO_CASE(SIGXCPU);
#endif
#ifdef SIGXFSZ
SIGNO_CASE(SIGXFSZ);
#endif
#ifdef SIGVTALRM
SIGNO_CASE(SIGVTALRM);
#endif
#ifdef SIGPROF
SIGNO_CASE(SIGPROF);
#endif
#ifdef SIGWINCH
SIGNO_CASE(SIGWINCH);
#endif
#ifdef SIGIO
SIGNO_CASE(SIGIO);
#endif
#ifdef SIGPOLL
# if SIGPOLL != SIGIO
SIGNO_CASE(SIGPOLL);
# endif
#endif
#ifdef SIGLOST
SIGNO_CASE(SIGLOST);
#endif
#ifdef SIGPWR
# if SIGPWR != SIGLOST
SIGNO_CASE(SIGPWR);
# endif
#endif
#ifdef SIGSYS
SIGNO_CASE(SIGSYS);
#endif
default: return "";
}
}
Local<Value> ErrnoException(int errorno,
const char *syscall,
const char *msg,
const char *path) {
Local<Value> e;
Local<String> estring = String::NewSymbol(errno_string(errorno));
if (!msg[0]) {
msg = strerror(errorno);
}
Local<String> message = String::NewSymbol(msg);
Local<String> cons1 = String::Concat(estring, String::NewSymbol(", "));
Local<String> cons2 = String::Concat(cons1, message);
if (syscall_symbol.IsEmpty()) {
syscall_symbol = NODE_PSYMBOL("syscall");
errno_symbol = NODE_PSYMBOL("errno");
errpath_symbol = NODE_PSYMBOL("path");
code_symbol = NODE_PSYMBOL("code");
}
if (path) {
Local<String> cons3 = String::Concat(cons2, String::NewSymbol(" '"));
Local<String> cons4 = String::Concat(cons3, String::New(path));
Local<String> cons5 = String::Concat(cons4, String::NewSymbol("'"));
e = Exception::Error(cons5);
} else {
e = Exception::Error(cons2);
}
Local<Object> obj = e->ToObject();
obj->Set(errno_symbol, Integer::New(errorno));
obj->Set(code_symbol, estring);
if (path) obj->Set(errpath_symbol, String::New(path));
if (syscall) obj->Set(syscall_symbol, String::NewSymbol(syscall));
return e;
}
static const char* get_uv_errno_string(int errorno) {
uv_err_t err;
memset(&err, 0, sizeof err);
err.code = (uv_err_code)errorno;
return uv_err_name(err);
}
static const char* get_uv_errno_message(int errorno) {
uv_err_t err;
memset(&err, 0, sizeof err);
err.code = (uv_err_code)errorno;
return uv_strerror(err);
}
// hack alert! copy of ErrnoException, tuned for uv errors
Local<Value> UVException(int errorno,
const char *syscall,
const char *msg,
const char *path) {
if (syscall_symbol.IsEmpty()) {
syscall_symbol = NODE_PSYMBOL("syscall");
errno_symbol = NODE_PSYMBOL("errno");
errpath_symbol = NODE_PSYMBOL("path");
code_symbol = NODE_PSYMBOL("code");
}
if (!msg || !msg[0])
msg = get_uv_errno_message(errorno);
Local<String> estring = String::NewSymbol(get_uv_errno_string(errorno));
Local<String> message = String::NewSymbol(msg);
Local<String> cons1 = String::Concat(estring, String::NewSymbol(", "));
Local<String> cons2 = String::Concat(cons1, message);
Local<Value> e;
Local<String> path_str;
if (path) {
#ifdef _WIN32
if (strncmp(path, "\\\\?\\UNC\\", 8) == 0) {
path_str = String::Concat(String::New("\\\\"), String::New(path + 8));
} else if (strncmp(path, "\\\\?\\", 4) == 0) {
path_str = String::New(path + 4);
} else {
path_str = String::New(path);
}
#else
path_str = String::New(path);
#endif
Local<String> cons3 = String::Concat(cons2, String::NewSymbol(" '"));
Local<String> cons4 = String::Concat(cons3, path_str);
Local<String> cons5 = String::Concat(cons4, String::NewSymbol("'"));
e = Exception::Error(cons5);
} else {
e = Exception::Error(cons2);
}
Local<Object> obj = e->ToObject();
// TODO errno should probably go
obj->Set(errno_symbol, Integer::New(errorno));
obj->Set(code_symbol, estring);
if (path) obj->Set(errpath_symbol, path_str);
if (syscall) obj->Set(syscall_symbol, String::NewSymbol(syscall));
return e;
}
#ifdef _WIN32
// Does about the same as strerror(),
// but supports all windows error messages
static const char *winapi_strerror(const int errorno) {
char *errmsg = NULL;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errmsg, 0, NULL);
if (errmsg) {
// Remove trailing newlines
for (int i = strlen(errmsg) - 1;
i >= 0 && (errmsg[i] == '\n' || errmsg[i] == '\r'); i--) {
errmsg[i] = '\0';
}
return errmsg;
} else {
// FormatMessage failed
return "Unknown error";
}
}
Local<Value> WinapiErrnoException(int errorno,
const char* syscall,
const char* msg,
const char* path) {
Local<Value> e;
if (!msg || !msg[0]) {
msg = winapi_strerror(errorno);
}
Local<String> message = String::NewSymbol(msg);
if (syscall_symbol.IsEmpty()) {
syscall_symbol = NODE_PSYMBOL("syscall");
errno_symbol = NODE_PSYMBOL("errno");
errpath_symbol = NODE_PSYMBOL("path");
code_symbol = NODE_PSYMBOL("code");
}
if (path) {
Local<String> cons1 = String::Concat(message, String::NewSymbol(" '"));
Local<String> cons2 = String::Concat(cons1, String::New(path));
Local<String> cons3 = String::Concat(cons2, String::NewSymbol("'"));
e = Exception::Error(cons3);
} else {
e = Exception::Error(message);
}
Local<Object> obj = e->ToObject();
obj->Set(errno_symbol, Integer::New(errorno));
if (path) obj->Set(errpath_symbol, String::New(path));
if (syscall) obj->Set(syscall_symbol, String::NewSymbol(syscall));
return e;
}
#endif
Handle<Value> FromConstructorTemplate(Persistent<FunctionTemplate>& t,
const Arguments& args) {
HandleScope scope;
const int argc = args.Length();
Local<Value>* argv = new Local<Value>[argc];
for (int i = 0; i < argc; ++i) {
argv[i] = args[i];
}
Local<Object> instance = t->GetFunction()->NewInstance(argc, argv);
delete[] argv;
return scope.Close(instance);
}
// MakeCallback may only be made directly off the event loop.
// That is there can be no JavaScript stack frames underneath it.
// (Is there any way to assert that?)
//
// Maybe make this a method of a node::Handle super class
//
Handle<Value>
MakeCallback(const Handle<Object> object,
const char* method,
int argc,
Handle<Value> argv[]) {
HandleScope scope;
Handle<Value> ret =
MakeCallback(object, String::NewSymbol(method), argc, argv);
return scope.Close(ret);
}
Handle<Value>
MakeCallback(const Handle<Object> object,
const Handle<String> symbol,
int argc,
Handle<Value> argv[]) {
HandleScope scope;
Local<Value> callback_v = object->Get(symbol);
if (!callback_v->IsFunction()) {
String::Utf8Value method(symbol);
// XXX: If the object has a domain attached, handle it there?
// At least, would be good to get *some* sort of indication
// of how we got here, even if it's not catchable.
fprintf(stderr, "Non-function in MakeCallback. method = %s\n", *method);
abort();
}
Local<Function> callback = Local<Function>::Cast(callback_v);
return scope.Close(MakeCallback(object, callback, argc, argv));
}
Handle<Value>
MakeCallback(const Handle<Object> object,
const Handle<Function> callback,
int argc,
Handle<Value> argv[]) {
HandleScope scope;
// TODO Hook for long stack traces to be made here.
TryCatch try_catch;
if (enter_symbol.IsEmpty()) {
enter_symbol = NODE_PSYMBOL("enter");
exit_symbol = NODE_PSYMBOL("exit");
disposed_symbol = NODE_PSYMBOL("_disposed");
}
Local<Value> domain_v = object->Get(domain_symbol);
Local<Object> domain;
Local<Function> enter;
Local<Function> exit;
if (!domain_v->IsUndefined()) {
domain = domain_v->ToObject();
if (domain->Get(disposed_symbol)->BooleanValue()) {
// domain has been disposed of.
return Undefined();
}
enter = Local<Function>::Cast(domain->Get(enter_symbol));
enter->Call(domain, 0, NULL);
}
if (try_catch.HasCaught()) {
FatalException(try_catch);
return Undefined();
}
Local<Value> ret = callback->Call(object, argc, argv);
if (try_catch.HasCaught()) {
FatalException(try_catch);
return Undefined();
}
if (!domain_v->IsUndefined()) {
exit = Local<Function>::Cast(domain->Get(exit_symbol));
exit->Call(domain, 0, NULL);
}
if (try_catch.HasCaught()) {
FatalException(try_catch);
return Undefined();
}
return scope.Close(ret);
}
void SetErrno(uv_err_t err) {
HandleScope scope;
if (errno_symbol.IsEmpty()) {
errno_symbol = NODE_PSYMBOL("errno");
}
if (err.code == UV_UNKNOWN) {
char errno_buf[100];
snprintf(errno_buf, 100, "Unknown system errno %d", err.sys_errno_);
Context::GetCurrent()->Global()->Set(errno_symbol, String::New(errno_buf));
} else {
Context::GetCurrent()->Global()->Set(errno_symbol,
String::NewSymbol(uv_err_name(err)));
}
}
enum encoding ParseEncoding(Handle<Value> encoding_v, enum encoding _default) {
HandleScope scope;
if (!encoding_v->IsString()) return _default;
String::Utf8Value encoding(encoding_v);
if (strcasecmp(*encoding, "utf8") == 0) {
return UTF8;
} else if (strcasecmp(*encoding, "utf-8") == 0) {
return UTF8;
} else if (strcasecmp(*encoding, "ascii") == 0) {
return ASCII;
} else if (strcasecmp(*encoding, "base64") == 0) {
return BASE64;
} else if (strcasecmp(*encoding, "ucs2") == 0) {
return UCS2;
} else if (strcasecmp(*encoding, "ucs-2") == 0) {
return UCS2;
} else if (strcasecmp(*encoding, "binary") == 0) {
return BINARY;
} else if (strcasecmp(*encoding, "hex") == 0) {
return HEX;
} else if (strcasecmp(*encoding, "raw") == 0) {
if (!no_deprecation) {
fprintf(stderr, "'raw' (array of integers) has been removed. "
"Use 'binary'.\n");
}
return BINARY;
} else if (strcasecmp(*encoding, "raws") == 0) {
if (!no_deprecation) {
fprintf(stderr, "'raws' encoding has been renamed to 'binary'. "
"Please update your code.\n");
}
return BINARY;
} else {
return _default;
}
}
Local<Value> Encode(const void *buf, size_t len, enum encoding encoding) {
HandleScope scope;
if (!len) return scope.Close(String::Empty());
if (encoding == BINARY) {
const unsigned char *cbuf = static_cast<const unsigned char*>(buf);
uint16_t * twobytebuf = new uint16_t[len];
for (size_t i = 0; i < len; i++) {
// XXX is the following line platform independent?
twobytebuf[i] = cbuf[i];
}
Local<String> chunk = String::New(twobytebuf, len);
delete [] twobytebuf; // TODO use ExternalTwoByteString?
return scope.Close(chunk);
}
// utf8 or ascii encoding
Local<String> chunk = String::New((const char*)buf, len);
return scope.Close(chunk);
}
// Returns -1 if the handle was not valid for decoding
ssize_t DecodeBytes(v8::Handle<v8::Value> val, enum encoding encoding) {
HandleScope scope;
if (val->IsArray()) {
fprintf(stderr, "'raw' encoding (array of integers) has been removed. "
"Use 'binary'.\n");
assert(0);
return -1;
}
if (encoding == BINARY && Buffer::HasInstance(val)) {
return Buffer::Length(val->ToObject());
}
Local<String> str = val->ToString();
if (encoding == UTF8) return str->Utf8Length();
else if (encoding == UCS2) return str->Length() * 2;
else if (encoding == HEX) return str->Length() / 2;
return str->Length();
}
#ifndef MIN
# define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
// Returns number of bytes written.
ssize_t DecodeWrite(char *buf,
size_t buflen,
v8::Handle<v8::Value> val,
enum encoding encoding) {
HandleScope scope;
// XXX
// A lot of improvement can be made here. See:
// http://code.google.com/p/v8/issues/detail?id=270
// http://groups.google.com/group/v8-dev/browse_thread/thread/dba28a81d9215291/ece2b50a3b4022c
// http://groups.google.com/group/v8-users/browse_thread/thread/1f83b0ba1f0a611
if (val->IsArray()) {
fprintf(stderr, "'raw' encoding (array of integers) has been removed. "
"Use 'binary'.\n");
assert(0);
return -1;
}
Local<String> str = val->ToString();
if (encoding == UTF8) {
str->WriteUtf8(buf, buflen, NULL, String::HINT_MANY_WRITES_EXPECTED);
return buflen;
}
if (encoding == ASCII) {
str->WriteAscii(buf, 0, buflen, String::HINT_MANY_WRITES_EXPECTED);
return buflen;
}
// THIS IS AWFUL!!! FIXME
assert(encoding == BINARY);
uint16_t * twobytebuf = new uint16_t[buflen];
str->Write(twobytebuf, 0, buflen, String::HINT_MANY_WRITES_EXPECTED);
for (size_t i = 0; i < buflen; i++) {
unsigned char *b = reinterpret_cast<unsigned char*>(&twobytebuf[i]);
buf[i] = b[0];
}
delete [] twobytebuf;
return buflen;
}
void DisplayExceptionLine (TryCatch &try_catch) {
HandleScope scope;
Handle<Message> message = try_catch.Message();
uv_tty_reset_mode();
fprintf(stderr, "\n");
if (!message.IsEmpty()) {
// Print (filename):(line number): (message).
String::Utf8Value filename(message->GetScriptResourceName());
const char* filename_string = *filename;
int linenum = message->GetLineNumber();
fprintf(stderr, "%s:%i\n", filename_string, linenum);
// Print line of source code.
String::Utf8Value sourceline(message->GetSourceLine());
const char* sourceline_string = *sourceline;
// This removes from the printed error messages the module wrap prefix:
// "function (function (exports, __filename, ...) {"
// that is around all scripts that are run as modules.
// Even better would be to get support into V8 for wrappers that
// shouldn't be reported to users.
int offset = 0;
if (linenum == 1 && 0 == strncmp(sourceline_string, MODULE_WRAP_PREFIX,
sizeof(MODULE_WRAP_PREFIX) - 1)) {
offset += sizeof(MODULE_WRAP_PREFIX) - 1;
}
fprintf(stderr, "%s\n", sourceline_string + offset);
// Print wavy underline (GetUnderline is deprecated).
int start = message->GetStartColumn();
for (int i = offset; i < start; i++) {
fputc((sourceline_string[i] == '\t') ? '\t' : ' ', stderr);
}
int end = message->GetEndColumn();
for (int i = start; i < end; i++) {
fputc('^', stderr);
}
fputc('\n', stderr);
}
}
static void ReportException(TryCatch &try_catch, bool show_line) {
HandleScope scope;
if (show_line) DisplayExceptionLine(try_catch);
String::Utf8Value trace(try_catch.StackTrace());
// range errors have a trace member set to undefined
if (trace.length() > 0 && !try_catch.StackTrace()->IsUndefined()) {
fprintf(stderr, "%s\n", *trace);
} else {
// this really only happens for RangeErrors, since they're the only
// kind that won't have all this info in the trace, or when non-Error
// objects are thrown manually.
Local<Value> er = try_catch.Exception();
bool isErrorObject = er->IsObject() &&
!(er->ToObject()->Get(String::New("message"))->IsUndefined()) &&
!(er->ToObject()->Get(String::New("name"))->IsUndefined());
if (isErrorObject) {
String::Utf8Value name(er->ToObject()->Get(String::New("name")));
fprintf(stderr, "%s: ", *name);
}
String::Utf8Value msg(!isErrorObject ? er
: er->ToObject()->Get(String::New("message")));
fprintf(stderr, "%s\n", *msg);
}
fflush(stderr);
}
// Executes a str within the current v8 context.
Local<Value> ExecuteString(Handle<String> source, Handle<Value> filename) {
HandleScope scope;
TryCatch try_catch;
Local<v8::Script> script = v8::Script::Compile(source, filename);
if (script.IsEmpty()) {
ReportException(try_catch, true);
exit(1);
}
Local<Value> result = script->Run();
if (result.IsEmpty()) {
ReportException(try_catch, true);
exit(1);
}
return scope.Close(result);
}
static Handle<Value> GetActiveRequests(const Arguments& args) {
HandleScope scope;
Local<Array> ary = Array::New();
ngx_queue_t* q = NULL;
int i = 0;
ngx_queue_foreach(q, &req_wrap_queue) {
ReqWrap<uv_req_t>* w = container_of(q, ReqWrap<uv_req_t>, req_wrap_queue_);
if (w->object_.IsEmpty()) continue;
ary->Set(i++, w->object_);
}
return scope.Close(ary);
}
// Non-static, friend of HandleWrap. Could have been a HandleWrap method but
// implemented here for consistency with GetActiveRequests().
Handle<Value> GetActiveHandles(const Arguments& args) {
HandleScope scope;
Local<Array> ary = Array::New();
ngx_queue_t* q = NULL;
int i = 0;
Local<String> owner_sym = String::New("owner");
ngx_queue_foreach(q, &handle_wrap_queue) {
HandleWrap* w = container_of(q, HandleWrap, handle_wrap_queue_);
if (w->object_.IsEmpty() || w->unref_) continue;
Local<Value> obj = w->object_->Get(owner_sym);
if (obj->IsUndefined()) obj = *w->object_;
ary->Set(i++, obj);
}
return scope.Close(ary);
}
static Handle<Value> Abort(const Arguments& args) {
abort();
return Undefined();
}
static Handle<Value> Chdir(const Arguments& args) {
HandleScope scope;
if (args.Length() != 1 || !args[0]->IsString()) {
return ThrowException(Exception::Error(String::New("Bad argument.")));
}
String::Utf8Value path(args[0]);
uv_err_t r = uv_chdir(*path);
if (r.code != UV_OK) {
return ThrowException(UVException(r.code, "uv_chdir"));
}
return Undefined();
}
static Handle<Value> Cwd(const Arguments& args) {
HandleScope scope;
#ifdef _WIN32
/* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */
char buf[MAX_PATH * 4 + 1];
#else
char buf[PATH_MAX + 1];
#endif
uv_err_t r = uv_cwd(buf, ARRAY_SIZE(buf) - 1);
if (r.code != UV_OK) {
return ThrowException(UVException(r.code, "uv_cwd"));
}
buf[ARRAY_SIZE(buf) - 1] = '\0';
Local<String> cwd = String::New(buf);
return scope.Close(cwd);
}
static Handle<Value> Umask(const Arguments& args) {
HandleScope scope;
unsigned int old;
if (args.Length() < 1 || args[0]->IsUndefined()) {
old = umask(0);
umask((mode_t)old);
} else if(!args[0]->IsInt32() && !args[0]->IsString()) {
return ThrowException(Exception::TypeError(
String::New("argument must be an integer or octal string.")));
} else {
int oct;
if(args[0]->IsInt32()) {
oct = args[0]->Uint32Value();
} else {
oct = 0;
String::Utf8Value str(args[0]);
// Parse the octal string.
for (int i = 0; i < str.length(); i++) {
char c = (*str)[i];
if (c > '7' || c < '0') {
return ThrowException(Exception::TypeError(
String::New("invalid octal string")));
}
oct *= 8;
oct += c - '0';
}
}
old = umask(static_cast<mode_t>(oct));
}
return scope.Close(Uint32::New(old));
}
#ifdef __POSIX__
static Handle<Value> GetUid(const Arguments& args) {
HandleScope scope;
int uid = getuid();
return scope.Close(Integer::New(uid));
}
static Handle<Value> GetGid(const Arguments& args) {
HandleScope scope;
int gid = getgid();
return scope.Close(Integer::New(gid));
}
static Handle<Value> SetGid(const Arguments& args) {
HandleScope scope;
if (args.Length() < 1) {
return ThrowException(Exception::Error(
String::New("setgid requires 1 argument")));
}
int gid;
if (args[0]->IsNumber()) {
gid = args[0]->Int32Value();
} else if (args[0]->IsString()) {
String::Utf8Value grpnam(args[0]);
struct group grp, *grpp = NULL;
int err;
if ((err = getgrnam_r(*grpnam, &grp, getbuf, ARRAY_SIZE(getbuf), &grpp)) ||
grpp == NULL) {
if (errno == 0)
return ThrowException(Exception::Error(
String::New("setgid group id does not exist")));
else
return ThrowException(ErrnoException(errno, "getgrnam_r"));
}
gid = grpp->gr_gid;
} else {
return ThrowException(Exception::Error(
String::New("setgid argument must be a number or a string")));
}
int result;
if ((result = setgid(gid)) != 0) {
return ThrowException(ErrnoException(errno, "setgid"));
}
return Undefined();
}
static Handle<Value> SetUid(const Arguments& args) {
HandleScope scope;
if (args.Length() < 1) {
return ThrowException(Exception::Error(
String::New("setuid requires 1 argument")));
}
int uid;
if (args[0]->IsNumber()) {
uid = args[0]->Int32Value();
} else if (args[0]->IsString()) {
String::Utf8Value pwnam(args[0]);
struct passwd pwd, *pwdp = NULL;
int err;
if ((err = getpwnam_r(*pwnam, &pwd, getbuf, ARRAY_SIZE(getbuf), &pwdp)) ||
pwdp == NULL) {
if (errno == 0)
return ThrowException(Exception::Error(
String::New("setuid user id does not exist")));
else
return ThrowException(ErrnoException(errno, "getpwnam_r"));
}
uid = pwdp->pw_uid;
} else {
return ThrowException(Exception::Error(
String::New("setuid argument must be a number or a string")));
}
int result;
if ((result = setuid(uid)) != 0) {
return ThrowException(ErrnoException(errno, "setuid"));
}
return Undefined();
}
#endif // __POSIX__
v8::Handle<v8::Value> Exit(const v8::Arguments& args) {
HandleScope scope;
exit(args[0]->IntegerValue());
return Undefined();
}
static void CheckStatus(uv_timer_t* watcher, int status) {
assert(watcher == &gc_timer);
// check memory
if (!uv_is_active((uv_handle_t*) &gc_idle)) {
HeapStatistics stats;
V8::GetHeapStatistics(&stats);
if (stats.total_heap_size() > 1024 * 1024 * 128) {
// larger than 128 megs, just start the idle watcher
uv_idle_start(&gc_idle, node::Idle);
return;
}
}
double d = uv_now(uv_default_loop()) - TICK_TIME(3);
//printfb("timer d = %f\n", d);
if (d >= GC_WAIT_TIME - 1.) {
//fprintf(stderr, "start idle\n");
uv_idle_start(&gc_idle, node::Idle);
}
}
static Handle<Value> Uptime(const Arguments& args) {
HandleScope scope;
double uptime;
uv_err_t err = uv_uptime(&uptime);
if (err.code != UV_OK) {
return Undefined();
}
return scope.Close(Number::New(uptime - prog_start_time));
}
v8::Handle<v8::Value> UVCounters(const v8::Arguments& args) {
HandleScope scope;
uv_counters_t* c = &uv_default_loop()->counters;
Local<Object> obj = Object::New();
#define setc(name) \
obj->Set(String::New(#name), Integer::New(static_cast<int32_t>(c->name)));
setc(eio_init)
setc(req_init)
setc(handle_init)
setc(stream_init)
setc(tcp_init)
setc(udp_init)
setc(pipe_init)
setc(tty_init)
setc(prepare_init)
setc(check_init)
setc(idle_init)
setc(async_init)
setc(timer_init)
setc(process_init)
setc(fs_event_init)
#undef setc
return scope.Close(obj);
}
v8::Handle<v8::Value> MemoryUsage(const v8::Arguments& args) {
HandleScope scope;
size_t rss;
uv_err_t err = uv_resident_set_memory(&rss);
if (err.code != UV_OK) {
return ThrowException(UVException(err.code, "uv_resident_set_memory"));
}
Local<Object> info = Object::New();
if (rss_symbol.IsEmpty()) {
rss_symbol = NODE_PSYMBOL("rss");
heap_total_symbol = NODE_PSYMBOL("heapTotal");
heap_used_symbol = NODE_PSYMBOL("heapUsed");
}
info->Set(rss_symbol, Number::New(rss));
// V8 memory usage
HeapStatistics v8_heap_stats;
V8::GetHeapStatistics(&v8_heap_stats);
info->Set(heap_total_symbol,
Integer::NewFromUnsigned(v8_heap_stats.total_heap_size()));
info->Set(heap_used_symbol,
Integer::NewFromUnsigned(v8_heap_stats.used_heap_size()));
return scope.Close(info);
}
Handle<Value> Kill(const Arguments& args) {
HandleScope scope;
if (args.Length() != 2) {
return ThrowException(Exception::Error(String::New("Bad argument.")));
}
int pid = args[0]->IntegerValue();
int sig = args[1]->Int32Value();
uv_err_t err = uv_kill(pid, sig);
if (err.code != UV_OK) {
SetErrno(err);
return scope.Close(Integer::New(-1));
}
return Undefined();
}
// used in Hrtime() below
#define NANOS_PER_SEC 1000000000
// Hrtime exposes libuv's uv_hrtime() high-resolution timer.
// The value returned by uv_hrtime() is a 64-bit int representing nanoseconds,
// so this function instead returns an Array with 2 entries representing seconds
// and nanoseconds, to avoid any integer overflow possibility.
// Pass in an Array from a previous hrtime() call to instead get a time diff.
Handle<Value> Hrtime(const v8::Arguments& args) {
HandleScope scope;
uint64_t t = uv_hrtime();
if (args.Length() > 0) {
// return a time diff tuple
Local<Array> inArray = Local<Array>::Cast(args[0]);
uint64_t seconds = inArray->Get(0)->Uint32Value();
uint64_t nanos = inArray->Get(1)->Uint32Value();
t -= (seconds * NANOS_PER_SEC) + nanos;
}
Local<Array> tuple = Array::New(2);
tuple->Set(0, Integer::NewFromUnsigned(t / NANOS_PER_SEC));
tuple->Set(1, Integer::NewFromUnsigned(t % NANOS_PER_SEC));
return scope.Close(tuple);
}
typedef void (UV_DYNAMIC* extInit)(Handle<Object> exports);
// DLOpen is node.dlopen(). Used to load 'module.node' dynamically shared
// objects.
Handle<Value> DLOpen(const v8::Arguments& args) {
HandleScope scope;
char symbol[1024], *base, *pos;
uv_lib_t lib;
node_module_struct compat_mod;
int r;
if (args.Length() < 2) {
Local<Value> exception = Exception::Error(
String::New("process.dlopen takes exactly 2 arguments."));
return ThrowException(exception);
}
String::Utf8Value filename(args[0]); // Cast
Local<Object> target = args[1]->ToObject(); // Cast
if (uv_dlopen(*filename, &lib)) {
Local<String> errmsg = String::New(uv_dlerror(&lib));
#ifdef _WIN32
// Windows needs to add the filename into the error message
errmsg = String::Concat(errmsg, args[0]->ToString());
#endif
return ThrowException(Exception::Error(errmsg));
}
String::Utf8Value path(args[0]);
base = *path;
/* Find the shared library filename within the full path. */
#ifdef __POSIX__
pos = strrchr(base, '/');
if (pos != NULL) {
base = pos + 1;
}
#else // Windows
for (;;) {
pos = strpbrk(base, "\\/:");
if (pos == NULL) {
break;
}
base = pos + 1;
}
#endif
/* Strip the .node extension. */
pos = strrchr(base, '.');
if (pos != NULL) {
*pos = '\0';
}
/* Add the `_module` suffix to the extension name. */
r = snprintf(symbol, sizeof symbol, "%s_module", base);
if (r <= 0 || static_cast<size_t>(r) >= sizeof symbol) {
Local<Value> exception =
Exception::Error(String::New("Out of memory."));
return ThrowException(exception);
}
// Get the init() function from the dynamically shared object.
node_module_struct *mod;
if (uv_dlsym(&lib, symbol, reinterpret_cast<void**>(&mod))) {
/* Start Compatibility hack: Remove once everyone is using NODE_MODULE macro */
memset(&compat_mod, 0, sizeof compat_mod);
mod = &compat_mod;
mod->version = NODE_MODULE_VERSION;
if (uv_dlsym(&lib, "init", reinterpret_cast<void**>(&mod->register_func))) {
Local<String> errmsg = String::New(uv_dlerror(&lib));
uv_dlclose(&lib);
return ThrowException(Exception::Error(errmsg));
}
/* End Compatibility hack */
}
if (mod->version != NODE_MODULE_VERSION) {
Local<Value> exception = Exception::Error(
String::New("Module version mismatch, refusing to load."));
return ThrowException(exception);
}
// Execute the C++ module
mod->register_func(target);
// Tell coverity that 'handle' should not be freed when we return.
// coverity[leaked_storage]
return Undefined();
}
static void OnFatalError(const char* location, const char* message) {
if (location) {
fprintf(stderr, "FATAL ERROR: %s %s\n", location, message);
} else {
fprintf(stderr, "FATAL ERROR: %s\n", message);
}
exit(1);
}
void FatalException(TryCatch &try_catch) {
HandleScope scope;
if (listeners_symbol.IsEmpty()) {
listeners_symbol = NODE_PSYMBOL("listeners");
uncaught_exception_symbol = NODE_PSYMBOL("uncaughtException");
emit_symbol = NODE_PSYMBOL("emit");
}
Local<Value> listeners_v = process->Get(listeners_symbol);
assert(listeners_v->IsFunction());
Local<Function> listeners = Local<Function>::Cast(listeners_v);
Local<String> uncaught_exception_symbol_l = Local<String>::New(uncaught_exception_symbol);
Local<Value> argv[1] = { uncaught_exception_symbol_l };
Local<Value> ret = listeners->Call(process, 1, argv);
assert(ret->IsArray());
Local<Array> listener_array = Local<Array>::Cast(ret);
uint32_t length = listener_array->Length();
// Report and exit if process has no "uncaughtException" listener
if (length == 0) {
ReportException(try_catch, true);
exit(1);
}
// Otherwise fire the process "uncaughtException" event
Local<Value> emit_v = process->Get(emit_symbol);
assert(emit_v->IsFunction());
Local<Function> emit = Local<Function>::Cast(emit_v);
Local<Value> error = try_catch.Exception();
Local<Value> event_argv[2] = { uncaught_exception_symbol_l, error };
TryCatch event_try_catch;
emit->Call(process, 2, event_argv);
if (event_try_catch.HasCaught()) {
// the uncaught exception event threw, so we must exit.
ReportException(event_try_catch, true);
exit(1);
}
// This makes sure uncaught exceptions don't interfere with process.nextTick
StartTickSpinner();
}
Persistent<Object> binding_cache;
Persistent<Array> module_load_list;
static Handle<Value> Binding(const Arguments& args) {
HandleScope scope;
Local<String> module = args[0]->ToString();
String::Utf8Value module_v(module);
node_module_struct* modp;
if (binding_cache.IsEmpty()) {
binding_cache = Persistent<Object>::New(Object::New());
}
Local<Object> exports;
if (binding_cache->Has(module)) {
exports = binding_cache->Get(module)->ToObject();
return scope.Close(exports);
}
// Append a string to process.moduleLoadList
char buf[1024];
snprintf(buf, 1024, "Binding %s", *module_v);
uint32_t l = module_load_list->Length();
module_load_list->Set(l, String::New(buf));
if ((modp = get_builtin_module(*module_v)) != NULL) {
exports = Object::New();
modp->register_func(exports);
binding_cache->Set(module, exports);
} else if (!strcmp(*module_v, "constants")) {
exports = Object::New();
DefineConstants(exports);
binding_cache->Set(module, exports);
#ifdef __POSIX__
} else if (!strcmp(*module_v, "io_watcher")) {
exports = Object::New();
IOWatcher::Initialize(exports);
binding_cache->Set(module, exports);
#endif
} else if (!strcmp(*module_v, "natives")) {
exports = Object::New();
DefineJavaScript(exports);
binding_cache->Set(module, exports);
} else {
return ThrowException(Exception::Error(String::New("No such module")));
}
return scope.Close(exports);
}
static Handle<Value> ProcessTitleGetter(Local<String> property,
const AccessorInfo& info) {
HandleScope scope;
char buffer[512];
uv_get_process_title(buffer, sizeof(buffer));
return scope.Close(String::New(buffer));
}
static void ProcessTitleSetter(Local<String> property,
Local<Value> value,
const AccessorInfo& info) {
HandleScope scope;
String::Utf8Value title(value);
// TODO: protect with a lock
uv_set_process_title(*title);
}
static Handle<Value> EnvGetter(Local<String> property,
const AccessorInfo& info) {
HandleScope scope;
#ifdef __POSIX__
String::Utf8Value key(property);
const char* val = getenv(*key);
if (val) {
return scope.Close(String::New(val));
}
#else // _WIN32
String::Value key(property);
WCHAR buffer[32767]; // The maximum size allowed for environment variables.
DWORD result = GetEnvironmentVariableW(reinterpret_cast<WCHAR*>(*key),
buffer,
ARRAY_SIZE(buffer));
// If result >= sizeof buffer the buffer was too small. That should never
// happen. If result == 0 and result != ERROR_SUCCESS the variable was not
// not found.
if ((result > 0 || GetLastError() == ERROR_SUCCESS) &&
result < ARRAY_SIZE(buffer)) {
return scope.Close(String::New(reinterpret_cast<uint16_t*>(buffer), result));
}
#endif
// Not found. Fetch from prototype.
return info.Data().As<Object>()->Get(property);
}
static Handle<Value> EnvSetter(Local<String> property,
Local<Value> value,
const AccessorInfo& info) {
HandleScope scope;
#ifdef __POSIX__
String::Utf8Value key(property);
String::Utf8Value val(value);
setenv(*key, *val, 1);
#else // _WIN32
String::Value key(property);
String::Value val(value);
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
// Environment variables that start with '=' are read-only.
if (key_ptr[0] != L'=') {
SetEnvironmentVariableW(key_ptr, reinterpret_cast<WCHAR*>(*val));
}
#endif
// Whether it worked or not, always return rval.
return scope.Close(value);
}
static Handle<Integer> EnvQuery(Local<String> property,
const AccessorInfo& info) {
HandleScope scope;
#ifdef __POSIX__
String::Utf8Value key(property);
if (getenv(*key)) {
return scope.Close(Integer::New(None));
}
#else // _WIN32
String::Value key(property);
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
if (GetEnvironmentVariableW(key_ptr, NULL, 0) > 0 ||
GetLastError() == ERROR_SUCCESS) {
if (key_ptr[0] == L'=') {
// Environment variables that start with '=' are hidden and read-only.
return scope.Close(Integer::New(v8::ReadOnly ||
v8::DontDelete ||
v8::DontEnum));
} else {
return scope.Close(Integer::New(None));
}
}
#endif
// Not found
return scope.Close(Handle<Integer>());
}
static Handle<Boolean> EnvDeleter(Local<String> property,
const AccessorInfo& info) {
HandleScope scope;
#ifdef __POSIX__
String::Utf8Value key(property);
if (!getenv(*key)) return False();
unsetenv(*key); // can't check return value, it's void on some platforms
return True();
#else
String::Value key(property);
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
if (key_ptr[0] == L'=' || !SetEnvironmentVariableW(key_ptr, NULL)) {
// Deletion failed. Return true if the key wasn't there in the first place,
// false if it is still there.
bool rv = GetEnvironmentVariableW(key_ptr, NULL, NULL) == 0 &&
GetLastError() != ERROR_SUCCESS;
return scope.Close(Boolean::New(rv));
}
return True();
#endif
}
static Handle<Array> EnvEnumerator(const AccessorInfo& info) {
HandleScope scope;
#ifdef __POSIX__
int size = 0;
while (environ[size]) size++;
Local<Array> env = Array::New(size);
for (int i = 0; i < size; ++i) {
const char* var = environ[i];
const char* s = strchr(var, '=');
const int length = s ? s - var : strlen(var);
env->Set(i, String::New(var, length));
}
#else // _WIN32
WCHAR* environment = GetEnvironmentStringsW();
if (environment == NULL) {
// This should not happen.
return scope.Close(Handle<Array>());
}
Local<Array> env = Array::New();
WCHAR* p = environment;
int i = 0;
while (*p != NULL) {
WCHAR *s;
if (*p == L'=') {
// If the key starts with '=' it is a hidden environment variable.
p += wcslen(p) + 1;
continue;
} else {
s = wcschr(p, L'=');
}
if (!s) {
s = p + wcslen(p);
}
env->Set(i++, String::New(reinterpret_cast<uint16_t*>(p), s - p));
p = s + wcslen(s) + 1;
}
FreeEnvironmentStringsW(environment);
#endif
return scope.Close(env);
}
static Handle<Object> GetFeatures() {
HandleScope scope;
Local<Object> obj = Object::New();
obj->Set(String::NewSymbol("debug"),
#if defined(DEBUG) && DEBUG
True()
#else
False()
#endif
);
obj->Set(String::NewSymbol("uv"), True());
obj->Set(String::NewSymbol("ipv6"), True()); // TODO ping libuv
obj->Set(String::NewSymbol("tls_npn"), Boolean::New(use_npn));
obj->Set(String::NewSymbol("tls_sni"), Boolean::New(use_sni));
obj->Set(String::NewSymbol("tls"),
Boolean::New(get_builtin_module("crypto") != NULL));
return scope.Close(obj);
}
static Handle<Value> DebugPortGetter(Local<String> property,
const AccessorInfo& info) {
HandleScope scope;
return scope.Close(Integer::NewFromUnsigned(debug_port));
}
static void DebugPortSetter(Local<String> property,
Local<Value> value,
const AccessorInfo& info) {
HandleScope scope;
debug_port = value->NumberValue();
}
static Handle<Value> DebugProcess(const Arguments& args);
static Handle<Value> DebugPause(const Arguments& args);
static Handle<Value> DebugEnd(const Arguments& args);
Handle<Object> SetupProcessObject(int argc, char *argv[]) {
HandleScope scope;
int i, j;
Local<FunctionTemplate> process_template = FunctionTemplate::New();
process_template->SetClassName(String::NewSymbol("process"));
process = Persistent<Object>::New(process_template->GetFunction()->NewInstance());
process->SetAccessor(String::New("title"),
ProcessTitleGetter,
ProcessTitleSetter);
// process.version
process->Set(String::NewSymbol("version"), String::New(NODE_VERSION));
// process.moduleLoadList
module_load_list = Persistent<Array>::New(Array::New());
process->Set(String::NewSymbol("moduleLoadList"), module_load_list);
// process.versions
Local<Object> versions = Object::New();
process->Set(String::NewSymbol("versions"), versions);
versions->Set(String::NewSymbol("http_parser"), String::New(
NODE_STRINGIFY(HTTP_PARSER_VERSION_MAJOR) "."
NODE_STRINGIFY(HTTP_PARSER_VERSION_MINOR)));
// +1 to get rid of the leading 'v'
versions->Set(String::NewSymbol("node"), String::New(NODE_VERSION+1));
versions->Set(String::NewSymbol("v8"), String::New(V8::GetVersion()));
versions->Set(String::NewSymbol("ares"), String::New(ARES_VERSION_STR));
versions->Set(String::NewSymbol("uv"), String::New(
NODE_STRINGIFY(UV_VERSION_MAJOR) "."
NODE_STRINGIFY(UV_VERSION_MINOR)));
versions->Set(String::NewSymbol("zlib"), String::New(ZLIB_VERSION));
#if HAVE_OPENSSL
// Stupid code to slice out the version string.
int c, l = strlen(OPENSSL_VERSION_TEXT);
for (i = j = 0; i < l; i++) {
c = OPENSSL_VERSION_TEXT[i];
if ('0' <= c && c <= '9') {
for (j = i + 1; j < l; j++) {
c = OPENSSL_VERSION_TEXT[j];
if (c == ' ') break;
}
break;
}
}
versions->Set(String::NewSymbol("openssl"),
String::New(OPENSSL_VERSION_TEXT + i, j - i));
#endif
// process.arch
process->Set(String::NewSymbol("arch"), String::New(ARCH));
// process.platform
process->Set(String::NewSymbol("platform"), String::New(PLATFORM));
// process.argv
Local<Array> arguments = Array::New(argc - option_end_index + 1);
arguments->Set(Integer::New(0), String::New(argv[0]));
for (j = 1, i = option_end_index; i < argc; j++, i++) {
Local<String> arg = String::New(argv[i]);
arguments->Set(Integer::New(j), arg);
}
// assign it
process->Set(String::NewSymbol("argv"), arguments);
// process.execArgv
Local<Array> execArgv = Array::New(option_end_index - 1);
for (j = 1, i = 0; j < option_end_index; j++, i++) {
execArgv->Set(Integer::New(i), String::New(argv[j]));
}
// assign it
process->Set(String::NewSymbol("execArgv"), execArgv);
// create process.env
Local<ObjectTemplate> envTemplate = ObjectTemplate::New();
envTemplate->SetNamedPropertyHandler(EnvGetter,
EnvSetter,
EnvQuery,
EnvDeleter,
EnvEnumerator,
Object::New());
Local<Object> env = envTemplate->NewInstance();
process->Set(String::NewSymbol("env"), env);
process->Set(String::NewSymbol("pid"), Integer::New(getpid()));
process->Set(String::NewSymbol("features"), GetFeatures());
//Add in the module wrap signatures
process->Set(String::NewSymbol("_moduleWrapPrefix"), String::New(MODULE_WRAP_PREFIX),
static_cast<PropertyAttribute>(DontEnum | DontDelete | ReadOnly));
process->Set(String::NewSymbol("_moduleWrapSuffix"), String::New(MODULE_WRAP_SUFFIX),
static_cast<PropertyAttribute>(DontEnum | DontDelete | ReadOnly));
// -e, --eval
if (eval_string) {
process->Set(String::NewSymbol("_eval"), String::New(eval_string));
}
// -p, --print
if (print_eval) {
process->Set(String::NewSymbol("_print_eval"), True());
}
// -i, --interactive
if (force_repl) {
process->Set(String::NewSymbol("_forceRepl"), True());
}
// --no-deprecation
if (no_deprecation) {
process->Set(String::NewSymbol("noDeprecation"), True());
}
// --trace-deprecation
if (trace_deprecation) {
process->Set(String::NewSymbol("traceDeprecation"), True());
}
size_t size = 2*PATH_MAX;
char* execPath = new char[size];
if (uv_exepath(execPath, &size) != 0) {
// as a last ditch effort, fallback on argv[0] ?
process->Set(String::NewSymbol("execPath"), String::New(argv[0]));
} else {
process->Set(String::NewSymbol("execPath"), String::New(execPath, size));
}
delete [] execPath;
process->SetAccessor(String::New("debugPort"),
DebugPortGetter,
DebugPortSetter);
// define various internal methods
NODE_SET_METHOD(process, "_getActiveRequests", GetActiveRequests);
NODE_SET_METHOD(process, "_getActiveHandles", GetActiveHandles);
NODE_SET_METHOD(process, "_needTickCallback", NeedTickCallback);
NODE_SET_METHOD(process, "reallyExit", Exit);
NODE_SET_METHOD(process, "abort", Abort);
NODE_SET_METHOD(process, "chdir", Chdir);
NODE_SET_METHOD(process, "cwd", Cwd);
NODE_SET_METHOD(process, "umask", Umask);
#ifdef __POSIX__
NODE_SET_METHOD(process, "getuid", GetUid);
NODE_SET_METHOD(process, "setuid", SetUid);
NODE_SET_METHOD(process, "setgid", SetGid);
NODE_SET_METHOD(process, "getgid", GetGid);
#endif // __POSIX__
NODE_SET_METHOD(process, "_kill", Kill);
NODE_SET_METHOD(process, "_debugProcess", DebugProcess);
NODE_SET_METHOD(process, "_debugPause", DebugPause);
NODE_SET_METHOD(process, "_debugEnd", DebugEnd);
NODE_SET_METHOD(process, "hrtime", Hrtime);
NODE_SET_METHOD(process, "dlopen", DLOpen);
NODE_SET_METHOD(process, "uptime", Uptime);
NODE_SET_METHOD(process, "memoryUsage", MemoryUsage);
NODE_SET_METHOD(process, "uvCounters", UVCounters);
NODE_SET_METHOD(process, "binding", Binding);
return process;
}
static void AtExit() {
uv_tty_reset_mode();
}
static void SignalExit(int signal) {
uv_tty_reset_mode();
_exit(1);
}
void Load(Handle<Object> process_l) {
// 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.)
// The node.js file returns a function 'f'
atexit(AtExit);
TryCatch try_catch;
Local<Value> f_value = ExecuteString(MainSource(),
IMMUTABLE_STRING("node.js"));
if (try_catch.HasCaught()) {
ReportException(try_catch, true);
exit(10);
}
assert(f_value->IsFunction());
Local<Function> f = Local<Function>::Cast(f_value);
// Now we call 'f' with the 'process' variable that we've built up with
// all our bindings. Inside node.js we'll take care of assigning things to
// their places.
// We start the process this way in order to be more modular. Developers
// who do not like how 'src/node.js' setups the module system but do like
// Node's I/O bindings may want to replace 'f' with their own function.
// Add a reference to the global object
Local<Object> global = v8::Context::GetCurrent()->Global();
Local<Value> args[1] = { Local<Value>::New(process_l) };
#if defined HAVE_DTRACE || defined HAVE_ETW
InitDTrace(global);
#endif
f->Call(global, 1, args);
if (try_catch.HasCaught()) {
ReportException(try_catch, true);
exit(11);
}
}
static void PrintHelp();
static void ParseDebugOpt(const char* arg) {
const char *p = 0;
use_debug_agent = true;
if (!strcmp (arg, "--debug-brk")) {
debug_wait_connect = true;
return;
} else if (!strcmp(arg, "--debug")) {
return;
} else if (strstr(arg, "--debug-brk=") == arg) {
debug_wait_connect = true;
p = 1 + strchr(arg, '=');
debug_port = atoi(p);
} else if (strstr(arg, "--debug=") == arg) {
p = 1 + strchr(arg, '=');
debug_port = atoi(p);
}
if (p && debug_port > 1024 && debug_port < 65536)
return;
fprintf(stderr, "Bad debug option.\n");
if (p) fprintf(stderr, "Debug port must be in range 1025 to 65535.\n");
PrintHelp();
exit(1);
}
static void PrintHelp() {
printf("Usage: node [options] [ -e script | script.js ] [arguments] \n"
" node debug script.js [arguments] \n"
"\n"
"Options:\n"
" -v, --version print node's version\n"
" -e, --eval script evaluate script\n"
" -p, --print print result of --eval\n"
" -i, --interactive always enter the REPL even if stdin\n"
" does not appear to be a terminal\n"
" --no-deprecation silence deprecation warnings\n"
" --trace-deprecation show stack traces on deprecations\n"
" --v8-options print v8 command line options\n"
" --max-stack-size=val set max v8 stack size (bytes)\n"
"\n"
"Environment variables:\n"
#ifdef _WIN32
"NODE_PATH ';'-separated list of directories\n"
#else
"NODE_PATH ':'-separated list of directories\n"
#endif
" prefixed to the module search path.\n"
"NODE_MODULE_CONTEXTS Set to 1 to load modules in their own\n"
" global contexts.\n"
"NODE_DISABLE_COLORS Set to 1 to disable colors in the REPL\n"
"\n"
"Documentation can be found at http://nodejs.org/\n");
}
// Parse node command line arguments.
static void ParseArgs(int argc, char **argv) {
int i;
// TODO use parse opts
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (strstr(arg, "--debug") == arg) {
ParseDebugOpt(arg);
argv[i] = const_cast<char*>("");
} else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-v") == 0) {
printf("%s\n", NODE_VERSION);
exit(0);
} else if (strstr(arg, "--max-stack-size=") == arg) {
const char *p = 0;
p = 1 + strchr(arg, '=');
max_stack_size = atoi(p);
argv[i] = const_cast<char*>("");
} else if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) {
PrintHelp();
exit(0);
} else if (strcmp(arg, "--eval") == 0 || strcmp(arg, "-e") == 0 ||
strcmp(arg, "-pe") == 0) {
if (argc <= i + 1) {
fprintf(stderr, "Error: --eval requires an argument\n");
exit(1);
}
if (arg[1] == 'p') {
print_eval = true;
}
argv[i] = const_cast<char*>("");
eval_string = argv[++i];
} else if (strcmp(arg, "--print") == 0 || strcmp(arg, "-p") == 0) {
print_eval = true;
argv[i] = const_cast<char*>("");
} else if (strcmp(arg, "--interactive") == 0 || strcmp(arg, "-i") == 0) {
force_repl = true;
argv[i] = const_cast<char*>("");
} else if (strcmp(arg, "--v8-options") == 0) {
argv[i] = const_cast<char*>("--help");
} else if (strcmp(arg, "--no-deprecation") == 0) {
argv[i] = const_cast<char*>("");
no_deprecation = true;
} else if (strcmp(arg, "--trace-deprecation") == 0) {
argv[i] = const_cast<char*>("");
trace_deprecation = true;
} else if (argv[i][0] != '-') {
break;
}
}
option_end_index = i;
}
static Isolate* node_isolate = NULL;
static volatile bool debugger_running = false;
static void EnableDebug(bool wait_connect) {
// If we're called from another thread, make sure to enter the right
// v8 isolate.
node_isolate->Enter();
// Start the debug thread and it's associated TCP server on port 5858.
bool r = v8::Debug::EnableAgent("node " NODE_VERSION,
debug_port,
wait_connect);
// Crappy check that everything went well. FIXME
assert(r);
// Print out some information.
fprintf(stderr, "debugger listening on port %d\n", debug_port);
fflush(stderr);
debugger_running = true;
node_isolate->Exit();
}
#ifdef __POSIX__
static void EnableDebugSignalHandler(int signal) {
// Break once process will return execution to v8
v8::Debug::DebugBreak(node_isolate);
if (!debugger_running) {
fprintf(stderr, "Hit SIGUSR1 - starting debugger agent.\n");
EnableDebug(false);
}
}
static void RegisterSignalHandler(int signal, void (*handler)(int)) {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = handler;
sigfillset(&sa.sa_mask);
sigaction(signal, &sa, NULL);
}
Handle<Value> DebugProcess(const Arguments& args) {
HandleScope scope;
if (args.Length() != 1) {
return ThrowException(Exception::Error(
String::New("Invalid number of arguments.")));
}
pid_t pid;
int r;
pid = args[0]->IntegerValue();
r = kill(pid, SIGUSR1);
if (r != 0) {
return ThrowException(ErrnoException(errno, "kill"));
}
return Undefined();
}
#endif // __POSIX__
#ifdef _WIN32
DWORD WINAPI EnableDebugThreadProc(void* arg) {
// Break once process will return execution to v8
if (!debugger_running) {
for (int i = 0; i < 1; i++) {
fprintf(stderr, "Starting debugger agent.\r\n");
fflush(stderr);
EnableDebug(false);
}
}
v8::Debug::DebugBreak();
return 0;
}
static int GetDebugSignalHandlerMappingName(DWORD pid, wchar_t* buf,
size_t buf_len) {
return _snwprintf(buf, buf_len, L"node-debug-handler-%u", pid);
}
static int RegisterDebugSignalHandler() {
wchar_t mapping_name[32];
HANDLE mapping_handle;
DWORD pid;
LPTHREAD_START_ROUTINE* handler;
pid = GetCurrentProcessId();
if (GetDebugSignalHandlerMappingName(pid,
mapping_name,
ARRAY_SIZE(mapping_name)) < 0) {
return -1;
}
mapping_handle = CreateFileMappingW(INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
sizeof *handler,
mapping_name);
if (mapping_handle == NULL) {
return -1;
}
handler = reinterpret_cast<LPTHREAD_START_ROUTINE*>(
MapViewOfFile(mapping_handle,
FILE_MAP_ALL_ACCESS,
0,
0,
sizeof *handler));
if (handler == NULL) {
CloseHandle(mapping_handle);
return -1;
}
*handler = EnableDebugThreadProc;
UnmapViewOfFile((void*) handler);
return 0;
}
static Handle<Value> DebugProcess(const Arguments& args) {
HandleScope scope;
Handle<Value> rv = Undefined();
DWORD pid;
HANDLE process = NULL;
HANDLE thread = NULL;
HANDLE mapping = NULL;
wchar_t mapping_name[32];
LPTHREAD_START_ROUTINE* handler = NULL;
if (args.Length() != 1) {
rv = ThrowException(Exception::Error(String::New("Invalid number of arguments.")));
goto out;
}
pid = (DWORD) args[0]->IntegerValue();
process = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION |
PROCESS_VM_OPERATION | PROCESS_VM_WRITE |
PROCESS_VM_READ,
FALSE,
pid);
if (process == NULL) {
rv = ThrowException(WinapiErrnoException(GetLastError(), "OpenProcess"));
goto out;
}
if (GetDebugSignalHandlerMappingName(pid,
mapping_name,
ARRAY_SIZE(mapping_name)) < 0) {
rv = ThrowException(ErrnoException(errno, "sprintf"));
goto out;
}
mapping = OpenFileMappingW(FILE_MAP_READ, FALSE, mapping_name);
if (mapping == NULL) {
rv = ThrowException(WinapiErrnoException(GetLastError(),
"OpenFileMappingW"));
goto out;
}
handler = reinterpret_cast<LPTHREAD_START_ROUTINE*>(
MapViewOfFile(mapping,
FILE_MAP_READ,
0,
0,
sizeof *handler));
if (handler == NULL || *handler == NULL) {
rv = ThrowException(WinapiErrnoException(GetLastError(), "MapViewOfFile"));
goto out;
}
thread = CreateRemoteThread(process,
NULL,
0,
*handler,
NULL,
0,
NULL);
if (thread == NULL) {
rv = ThrowException(WinapiErrnoException(GetLastError(),
"CreateRemoteThread"));
goto out;
}
// Wait for the thread to terminate
if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0) {
rv = ThrowException(WinapiErrnoException(GetLastError(),
"WaitForSingleObject"));
goto out;
}
out:
if (process != NULL) {
CloseHandle(process);
}
if (thread != NULL) {
CloseHandle(thread);
}
if (handler != NULL) {
UnmapViewOfFile(handler);
}
if (mapping != NULL) {
CloseHandle(mapping);
}
return Undefined();
}
#endif // _WIN32
static Handle<Value> DebugPause(const Arguments& args) {
v8::Debug::DebugBreak(node_isolate);
return Undefined();
}
static Handle<Value> DebugEnd(const Arguments& args) {
if (debugger_running) {
v8::Debug::DisableAgent();
debugger_running = false;
}
return Undefined();
}
char** Init(int argc, char *argv[]) {
// Initialize prog_start_time to get relative uptime.
uv_uptime(&prog_start_time);
// Make inherited handles noninheritable.
uv_disable_stdio_inheritance();
// Parse a few arguments which are specific to Node.
node::ParseArgs(argc, argv);
// Parse the rest of the args (up to the 'option_end_index' (where '--' was
// in the command line))
int v8argc = option_end_index;
char **v8argv = argv;
if (debug_wait_connect) {
// v8argv is a copy of argv up to the script file argument +2 if --debug-brk
// to expose the v8 debugger js object so that node.js can set
// a breakpoint on the first line of the startup script
v8argc += 2;
v8argv = new char*[v8argc];
memcpy(v8argv, argv, sizeof(argv) * option_end_index);
v8argv[option_end_index] = const_cast<char*>("--expose_debug_as");
v8argv[option_end_index + 1] = const_cast<char*>("v8debug");
}
// For the normal stack which moves from high to low addresses when frames
// are pushed, we can compute the limit as stack_size bytes below the
// the address of a stack variable (e.g. &stack_var) as an approximation
// of the start of the stack (we're assuming that we haven't pushed a lot
// of frames yet).
if (max_stack_size != 0) {
uint32_t stack_var;
ResourceConstraints constraints;
uint32_t *stack_limit = &stack_var - (max_stack_size / sizeof(uint32_t));
constraints.set_stack_limit(stack_limit);
SetResourceConstraints(&constraints); // Must be done before V8::Initialize
}
V8::SetFlagsFromCommandLine(&v8argc, v8argv, false);
#ifdef __POSIX__
// Ignore SIGPIPE
RegisterSignalHandler(SIGPIPE, SIG_IGN);
RegisterSignalHandler(SIGINT, SignalExit);
RegisterSignalHandler(SIGTERM, SignalExit);
#endif // __POSIX__
uv_prepare_init(uv_default_loop(), &prepare_tick_watcher);
uv_prepare_start(&prepare_tick_watcher, PrepareTick);
uv_unref(reinterpret_cast<uv_handle_t*>(&prepare_tick_watcher));
uv_check_init(uv_default_loop(), &check_tick_watcher);
uv_check_start(&check_tick_watcher, node::CheckTick);
uv_unref(reinterpret_cast<uv_handle_t*>(&check_tick_watcher));
uv_idle_init(uv_default_loop(), &tick_spinner);
uv_check_init(uv_default_loop(), &gc_check);
uv_check_start(&gc_check, node::Check);
uv_unref(reinterpret_cast<uv_handle_t*>(&gc_check));
uv_idle_init(uv_default_loop(), &gc_idle);
uv_unref(reinterpret_cast<uv_handle_t*>(&gc_idle));
uv_timer_init(uv_default_loop(), &gc_timer);
uv_unref(reinterpret_cast<uv_handle_t*>(&gc_timer));
V8::SetFatalErrorHandler(node::OnFatalError);
// Fetch a reference to the main isolate, so we have a reference to it
// even when we need it to access it from another (debugger) thread.
node_isolate = Isolate::GetCurrent();
// If the --debug flag was specified then initialize the debug thread.
if (use_debug_agent) {
EnableDebug(debug_wait_connect);
} else {
#ifdef _WIN32
RegisterDebugSignalHandler();
#else // Posix
RegisterSignalHandler(SIGUSR1, EnableDebugSignalHandler);
#endif // __POSIX__
}
return argv;
}
struct AtExitCallback {
AtExitCallback* next_;
void (*cb_)(void* arg);
void* arg_;
};
static AtExitCallback* at_exit_functions_;
void RunAtExit() {
AtExitCallback* p = at_exit_functions_;
at_exit_functions_ = NULL;
while (p) {
AtExitCallback* q = p->next_;
p->cb_(p->arg_);
delete p;
p = q;
}
}
void AtExit(void (*cb)(void* arg), void* arg) {
AtExitCallback* p = new AtExitCallback;
p->cb_ = cb;
p->arg_ = arg;
p->next_ = at_exit_functions_;
at_exit_functions_ = p;
}
void EmitExit(v8::Handle<v8::Object> process_l) {
// process.emit('exit')
process_l->Set(String::NewSymbol("_exiting"), True());
Local<Value> emit_v = process_l->Get(String::New("emit"));
assert(emit_v->IsFunction());
Local<Function> emit = Local<Function>::Cast(emit_v);
Local<Value> args[] = { String::New("exit"), Integer::New(0) };
TryCatch try_catch;
emit->Call(process_l, 2, args);
if (try_catch.HasCaught()) {
FatalException(try_catch);
}
}
static char **copy_argv(int argc, char **argv) {
size_t strlen_sum;
char **argv_copy;
char *argv_data;
size_t len;
int i;
strlen_sum = 0;
for(i = 0; i < argc; i++) {
strlen_sum += strlen(argv[i]) + 1;
}
argv_copy = (char **) malloc(sizeof(char *) * (argc + 1) + strlen_sum);
if (!argv_copy) {
return NULL;
}
argv_data = (char *) argv_copy + sizeof(char *) * (argc + 1);
for(i = 0; i < argc; i++) {
argv_copy[i] = argv_data;
len = strlen(argv[i]) + 1;
memcpy(argv_data, argv[i], len);
argv_data += len;
}
argv_copy[argc] = NULL;
return argv_copy;
}
int Start(int argc, char *argv[]) {
// Hack aroung with the argv pointer. Used for process.title = "blah".
argv = uv_setup_args(argc, argv);
// Logic to duplicate argv as Init() modifies arguments
// that are passed into it.
char **argv_copy = copy_argv(argc, argv);
// This needs to run *before* V8::Initialize()
// Use copy here as to not modify the original argv:
Init(argc, argv_copy);
V8::Initialize();
{
Locker locker;
HandleScope handle_scope;
// Create the one and only Context.
Persistent<Context> context = Context::New();
Context::Scope context_scope(context);
process_symbol = NODE_PSYMBOL("process");
domain_symbol = NODE_PSYMBOL("domain");
// Use original argv, as we're just copying values out of it.
Handle<Object> process_l = SetupProcessObject(argc, argv);
v8_typed_array::AttachBindings(context->Global());
// Create all the objects, load modules, do everything.
// so your next reading stop should be node::Load()!
Load(process_l);
// All our arguments are loaded. We've evaluated all of the scripts. We
// might even have created TCP servers. Now we enter the main eventloop. If
// there are no watchers on the loop (except for the ones that were
// uv_unref'd) then this function exits. As long as there are active
// watchers, it blocks.
uv_run(uv_default_loop());
EmitExit(process_l);
RunAtExit();
#ifndef NDEBUG
context.Dispose();
#endif
}
#ifndef NDEBUG
// Clean up. Not strictly necessary.
V8::Dispose();
#endif // NDEBUG
// Clean up the copy:
free(argv_copy);
return 0;
}
} // namespace node
Jump to Line
Something went wrong with that request. Please try again.