@@ -59,7 +59,7 @@ <h2>Contributor License Agreement (&quot;Agreement&quot;)</h2>

<p>
If you have questions about these terms, please contact us
at isaacs@joyent.com.
at legal@joyent.com.


<h3>Corporate Contributions:</h3>
@@ -157,7 +157,7 @@ <h2 id="signelectronically">Sign Electronically</h2>

<p>
Scanned agreements may also be emailed in PDF format to
isaacs@joyent.com. You should also keep a copy for your own
legal@joyent.com. You should also keep a copy for your own
records.

<pre>
@@ -165,7 +165,6 @@ function Client() {

this.currentFrame = NO_FRAME;
this.currentSourceLine = -1;
this.currentSource = null;
this.handles = {};
this.scripts = {};
this.breakpoints = [];
@@ -232,6 +232,9 @@ TLSSocket.prototype._init = function(socket) {
} else {
this.ssl.onhandshakestart = function() {};
this.ssl.onhandshakedone = this._finishInit.bind(this);

if (options.session)
this.ssl.setSession(options.session);
}

this.ssl.onerror = function(err) {
@@ -321,6 +324,10 @@ TLSSocket.prototype.setMaxSendFragment = function setMaxSendFragment(size) {
return this.ssl.setMaxSendFragment(size) == 1;
};

TLSSocket.prototype.getTLSTicket = function getTLSTicket() {
return this.ssl.getTLSTicket();
};

TLSSocket.prototype._handleTimeout = function() {
this._tlsError(new Error('TLS handshake timeout'));
};
@@ -620,6 +627,7 @@ Server.prototype.setOptions = function(options) {
if (!util.isUndefined(options.ecdhCurve))
this.ecdhCurve = options.ecdhCurve;
if (options.sessionTimeout) this.sessionTimeout = options.sessionTimeout;
if (options.ticketKeys) this.ticketKeys = options.ticketKeys;
var secureOptions = options.secureOptions || 0;
if (options.honorCipherOrder) {
secureOptions |= constants.SSL_OP_CIPHER_SERVER_PREFERENCE;
@@ -747,6 +755,7 @@ exports.connect = function(/* [port, host], options, cb */) {
isServer: false,
requestCert: true,
rejectUnauthorized: options.rejectUnauthorized,
session: options.session,
NPNProtocols: NPN.NPNProtocols
});
result = socket;
@@ -1110,6 +1110,7 @@ function inStatWatchers(filename) {

fs.watchFile = function(filename) {
nullCheck(filename);
filename = pathModule.resolve(filename);
var stat;
var listener;

@@ -1144,6 +1145,7 @@ fs.watchFile = function(filename) {

fs.unwatchFile = function(filename, listener) {
nullCheck(filename);
filename = pathModule.resolve(filename);
if (!inStatWatchers(filename)) return;

var stat = statWatchers[filename];
@@ -73,7 +73,8 @@ exports.writer = util.inspect;
exports._builtinLibs = ['assert', 'buffer', 'child_process', 'cluster',
'crypto', 'dgram', 'dns', 'domain', 'events', 'fs', 'http', 'https', 'net',
'os', 'path', 'punycode', 'querystring', 'readline', 'stream',
'string_decoder', 'tls', 'tty', 'url', 'util', 'vm', 'zlib', 'smalloc'];
'string_decoder', 'tls', 'tty', 'url', 'util', 'vm', 'zlib', 'smalloc',
'tracing'];


function REPLServer(prompt, stream, eval_, useGlobal, ignoreUndefined) {
@@ -30,19 +30,20 @@ var TIMEOUT_MAX = 2147483647; // 2^31-1

var debug = require('util').debuglog('timer');

var asyncFlags = process._asyncFlags;
var runAsyncQueue = process._runAsyncQueue;
var loadAsyncQueue = process._loadAsyncQueue;
var unloadAsyncQueue = process._unloadAsyncQueue;
var tracing = require('tracing');
var asyncFlags = tracing._asyncFlags;
var runAsyncQueue = tracing._runAsyncQueue;
var loadAsyncQueue = tracing._loadAsyncQueue;
var unloadAsyncQueue = tracing._unloadAsyncQueue;

// Same as in AsyncListener in env.h
var kHasListener = 0;

// Do a little housekeeping.
delete process._asyncFlags;
delete process._runAsyncQueue;
delete process._loadAsyncQueue;
delete process._unloadAsyncQueue;
delete tracing._asyncFlags;
delete tracing._runAsyncQueue;
delete tracing._loadAsyncQueue;
delete tracing._unloadAsyncQueue;


// IDLE TIMEOUTS
@@ -0,0 +1,396 @@
// 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.

var EventEmitter = require('events');
var v8binding, process;

// This needs to be loaded early, and before the "process" object is made
// global. So allow src/node.js to pass the process object in during
// initialization.
exports._nodeInitialization = function nodeInitialization(pobj) {
process = pobj;
v8binding = process.binding('v8');

// Finish setting up the v8 Object.
v8.getHeapStatistics = v8binding.getHeapStatistics;

// Part of the AsyncListener setup to share objects/callbacks with the
// native layer.
process._setupAsyncListener(asyncFlags,
runAsyncQueue,
loadAsyncQueue,
unloadAsyncQueue);

// Do a little housekeeping.
delete exports._nodeInitialization;
};


// v8

var v8 = exports.v8 = new EventEmitter();


function emitGC(before, after) {
v8.emit('gc', before, after);
}


v8.on('newListener', function(name) {
if (name === 'gc' && EventEmitter.listenerCount(this, name) === 0) {
v8binding.startGarbageCollectionTracking(emitGC);
}
});


v8.on('removeListener', function(name) {
if (name === 'gc' && EventEmitter.listenerCount(this, name) === 0) {
v8binding.stopGarbageCollectionTracking();
}
});


// AsyncListener

// new Array() is used here because it is more efficient for sparse
// arrays. Please *do not* change these to simple bracket notation.

// Track the active queue of AsyncListeners that have been added.
var asyncQueue = new Array();

// Keep the stack of all contexts that have been loaded in the
// execution chain of asynchronous events.
var contextStack = new Array();
var currentContext = undefined;

// Incremental uid for new AsyncListener instances.
var alUid = 0;

// Stateful flags shared with Environment for quick JS/C++
// communication.
var asyncFlags = {};

// Prevent accidentally suppressed thrown errors from before/after.
var inAsyncTick = false;

// To prevent infinite recursion when an error handler also throws
// flag when an error is currenly being handled.
var inErrorTick = false;

// Needs to be the same as src/env.h
var kHasListener = 0;
var kWatchedProviders = 1;

// Flags to determine what async listeners are available.
var HAS_CREATE_AL = 1 << 0;
var HAS_BEFORE_AL = 1 << 1;
var HAS_AFTER_AL = 1 << 2;
var HAS_ERROR_AL = 1 << 3;

// _errorHandler is scoped so it's also accessible by _fatalException.
exports._errorHandler = errorHandler;

// Needs to be accessible from lib/timers.js so they know when async
// listeners are currently in queue. They'll be cleaned up once
// references there are made.
exports._asyncFlags = asyncFlags;
exports._runAsyncQueue = runAsyncQueue;
exports._loadAsyncQueue = loadAsyncQueue;
exports._unloadAsyncQueue = unloadAsyncQueue;

// Public API.
exports.createAsyncListener = createAsyncListener;
exports.addAsyncListener = addAsyncListener;
exports.removeAsyncListener = removeAsyncListener;

// Load the currently executing context as the current context, and
// create a new asyncQueue that can receive any added queue items
// during the executing of the callback.
function loadContext(ctx) {
contextStack.push(currentContext);
currentContext = ctx;

asyncFlags[kHasListener] = 1;
}

function unloadContext() {
currentContext = contextStack.pop();

if (currentContext === undefined && asyncQueue.length === 0)
asyncFlags[kHasListener] = 0;
}

// Run all the async listeners attached when an asynchronous event is
// instantiated.
function runAsyncQueue(context) {
var queue = new Array();
var data = new Array();
var ccQueue, i, item, queueItem, value;

context._asyncQueue = queue;
context._asyncData = data;
context._asyncFlags = 0;

inAsyncTick = true;

// First run through all callbacks in the currentContext. These may
// add new AsyncListeners to the asyncQueue during execution. Hence
// why they need to be evaluated first.
if (currentContext) {
ccQueue = currentContext._asyncQueue;
context._asyncFlags |= currentContext._asyncFlags;
for (i = 0; i < ccQueue.length; i++) {
queueItem = ccQueue[i];
queue[queue.length] = queueItem;
if ((queueItem.callback_flags & HAS_CREATE_AL) === 0) {
data[queueItem.uid] = queueItem.data;
continue;
}
value = queueItem.create(queueItem.data);
data[queueItem.uid] = (value === undefined) ? queueItem.data : value;
}
}

// Then run through all items in the asyncQueue
if (asyncQueue) {
for (i = 0; i < asyncQueue.length; i++) {
queueItem = asyncQueue[i];
// Quick way to check if an AL instance with the same uid was
// already run from currentContext.
if (data[queueItem.uid] !== undefined)
continue;
queue[queue.length] = queueItem;
context._asyncFlags |= queueItem.callback_flags;
if ((queueItem.callback_flags & HAS_CREATE_AL) === 0) {
data[queueItem.uid] = queueItem.data;
continue;
}
value = queueItem.create(queueItem.data);
data[queueItem.uid] = (value === undefined) ? queueItem.data : value;
}
}

inAsyncTick = false;
}

// Load the AsyncListener queue attached to context and run all
// "before" callbacks, if they exist.
function loadAsyncQueue(context) {
loadContext(context);

if ((context._asyncFlags & HAS_BEFORE_AL) === 0)
return;

var queue = context._asyncQueue;
var data = context._asyncData;
var i, queueItem;

inAsyncTick = true;
for (i = 0; i < queue.length; i++) {
queueItem = queue[i];
if ((queueItem.callback_flags & HAS_BEFORE_AL) > 0)
queueItem.before(context, data[queueItem.uid]);
}
inAsyncTick = false;
}

// Unload the AsyncListener queue attached to context and run all
// "after" callbacks, if they exist.
function unloadAsyncQueue(context) {
if ((context._asyncFlags & HAS_AFTER_AL) === 0) {
unloadContext();
return;
}

var queue = context._asyncQueue;
var data = context._asyncData;
var i, queueItem;

inAsyncTick = true;
for (i = 0; i < queue.length; i++) {
queueItem = queue[i];
if ((queueItem.callback_flags & HAS_AFTER_AL) > 0)
queueItem.after(context, data[queueItem.uid]);
}
inAsyncTick = false;

unloadContext();
}

// Handle errors that are thrown while in the context of an
// AsyncListener. If an error is thrown from an AsyncListener
// callback error handlers will be called once more to report
// the error, then the application will die forcefully.
function errorHandler(er) {
if (inErrorTick)
return false;

var handled = false;
var i, queueItem, threw;

inErrorTick = true;

// First process error callbacks from the current context.
if (currentContext && (currentContext._asyncFlags & HAS_ERROR_AL) > 0) {
var queue = currentContext._asyncQueue;
var data = currentContext._asyncData;
for (i = 0; i < queue.length; i++) {
queueItem = queue[i];
if ((queueItem.callback_flags & HAS_ERROR_AL) === 0)
continue;
try {
threw = true;
// While it would be possible to pass in currentContext, if
// the error is thrown from the "create" callback then there's
// a chance the object hasn't been fully constructed.
handled = queueItem.error(data[queueItem.uid], er) || handled;
threw = false;
} finally {
// If the error callback thew then die quickly. Only allow the
// exit events to be processed.
if (threw) {
process._exiting = true;
process.emit('exit', 1);
}
}
}
}

// Now process callbacks from any existing queue.
if (asyncQueue) {
for (i = 0; i < asyncQueue.length; i++) {
queueItem = asyncQueue[i];
if ((queueItem.callback_flags & HAS_ERROR_AL) === 0 ||
(data && data[queueItem.uid] !== undefined))
continue;
try {
threw = true;
handled = queueItem.error(queueItem.data, er) || handled;
threw = false;
} finally {
// If the error callback thew then die quickly. Only allow the
// exit events to be processed.
if (threw) {
process._exiting = true;
process.emit('exit', 1);
}
}
}
}

inErrorTick = false;

unloadContext();

// TODO(trevnorris): If the error was handled, should the after callbacks
// be fired anyways?

return handled && !inAsyncTick;
}

// Instance function of an AsyncListener object.
function AsyncListenerInst(callbacks, data) {
if (typeof callbacks.create === 'function') {
this.create = callbacks.create;
this.callback_flags |= HAS_CREATE_AL;
}
if (typeof callbacks.before === 'function') {
this.before = callbacks.before;
this.callback_flags |= HAS_BEFORE_AL;
}
if (typeof callbacks.after === 'function') {
this.after = callbacks.after;
this.callback_flags |= HAS_AFTER_AL;
}
if (typeof callbacks.error === 'function') {
this.error = callbacks.error;
this.callback_flags |= HAS_ERROR_AL;
}

this.uid = ++alUid;
this.data = data === undefined ? null : data;
}
AsyncListenerInst.prototype.create = undefined;
AsyncListenerInst.prototype.before = undefined;
AsyncListenerInst.prototype.after = undefined;
AsyncListenerInst.prototype.error = undefined;
AsyncListenerInst.prototype.data = undefined;
AsyncListenerInst.prototype.uid = 0;
AsyncListenerInst.prototype.callback_flags = 0;

// Create new async listener object. Useful when instantiating a new
// object and want the listener instance, but not add it to the stack.
// If an existing AsyncListenerInst is passed then any new "data" is
// ignored.
function createAsyncListener(callbacks, data) {
if (typeof callbacks !== 'object' || callbacks == null)
throw new TypeError('callbacks argument must be an object');

if (callbacks instanceof AsyncListenerInst)
return callbacks;
else
return new AsyncListenerInst(callbacks, data);
}

// Add a listener to the current queue.
function addAsyncListener(callbacks, data) {
// Fast track if a new AsyncListenerInst has to be created.
if (!(callbacks instanceof AsyncListenerInst)) {
callbacks = createAsyncListener(callbacks, data);
asyncQueue.push(callbacks);
asyncFlags[kHasListener] = 1;
return callbacks;
}

var inQueue = false;
// The asyncQueue will be small. Probably always <= 3 items.
for (var i = 0; i < asyncQueue.length; i++) {
if (callbacks === asyncQueue[i]) {
inQueue = true;
break;
}
}

// Make sure the callback doesn't already exist in the queue.
if (!inQueue) {
asyncQueue.push(callbacks);
asyncFlags[kHasListener] = 1;
}

return callbacks;
}

// Remove listener from the current queue. Though this will not remove
// the listener from the current context. So callback propagation will
// continue.
function removeAsyncListener(obj) {
for (var i = 0; i < asyncQueue.length; i++) {
if (obj === asyncQueue[i]) {
asyncQueue.splice(i, 1);
break;
}
}

if (asyncQueue.length > 0 || currentContext !== undefined)
asyncFlags[kHasListener] = 1;
else
asyncFlags[kHasListener] = 0;
}
@@ -239,6 +239,28 @@ function formatValue(ctx, value, recurseTimes) {
keys = Object.getOwnPropertyNames(value);
}

// This could be a boxed primitive (new String(), etc.), check valueOf()
// NOTE: Avoid calling `valueOf` on `Date` instance because it will return
// a number which, when object has some additional user-stored `keys`,
// will be printed out.
var formatted;
var raw = value;
try {
// the .valueOf() call can fail for a multitude of reasons
if (!isDate(value))
raw = value.valueOf();
} catch (e) {
// ignore...
}

if (isString(raw)) {
// for boxed Strings, we have to remove the 0-n indexed entries,
// since they just noisey up the output and are redundant
keys = keys.filter(function(key) {
return !(key >= 0 && key < raw.length);
});
}

// Some type of object without properties can be shortcutted.
if (keys.length === 0) {
if (isFunction(value)) {
@@ -254,6 +276,19 @@ function formatValue(ctx, value, recurseTimes) {
if (isError(value)) {
return formatError(value);
}
// now check the `raw` value to handle boxed primitives
if (isString(raw)) {
formatted = formatPrimitiveNoColor(ctx, raw);
return ctx.stylize('[String: ' + formatted + ']', 'string');
}
if (isNumber(raw)) {
formatted = formatPrimitiveNoColor(ctx, raw);
return ctx.stylize('[Number: ' + formatted + ']', 'number');
}
if (isBoolean(raw)) {
formatted = formatPrimitiveNoColor(ctx, raw);
return ctx.stylize('[Boolean: ' + formatted + ']', 'boolean');
}
}

var base = '', array = false, braces = ['{', '}'];
@@ -285,6 +320,24 @@ function formatValue(ctx, value, recurseTimes) {
base = ' ' + formatError(value);
}

// Make boxed primitive Strings look like such
if (isString(raw)) {
formatted = formatPrimitiveNoColor(ctx, raw);
base = ' ' + '[String: ' + formatted + ']';
}

// Make boxed primitive Numbers look like such
if (isNumber(raw)) {
formatted = formatPrimitiveNoColor(ctx, raw);
base = ' ' + '[Number: ' + formatted + ']';
}

// Make boxed primitive Booleans look like such
if (isBoolean(raw)) {
formatted = formatPrimitiveNoColor(ctx, raw);
base = ' ' + '[Boolean: ' + formatted + ']';
}

if (keys.length === 0 && (!array || value.length == 0)) {
return braces[0] + base + braces[1];
}
@@ -338,6 +391,15 @@ function formatPrimitive(ctx, value) {
}


function formatPrimitiveNoColor(ctx, value) {
var stylize = ctx.stylize;
ctx.stylize = stylizeNoColor;
var str = formatPrimitive(ctx, value);
ctx.stylize = stylize;
return str;
}


function formatError(value) {
return '[' + Error.prototype.toString.call(value) + ']';
}
@@ -112,55 +112,83 @@ exports.deflate = function(buffer, opts, callback) {
callback = opts;
opts = {};
}
zlibBuffer(new Deflate(opts), buffer, callback);
return zlibBuffer(new Deflate(opts), buffer, callback);
};

exports.deflateSync = function(buffer, opts) {
return zlibBufferSync(new Deflate(opts), buffer);
};

exports.gzip = function(buffer, opts, callback) {
if (util.isFunction(opts)) {
callback = opts;
opts = {};
}
zlibBuffer(new Gzip(opts), buffer, callback);
return zlibBuffer(new Gzip(opts), buffer, callback);
};

exports.gzipSync = function(buffer, opts) {
return zlibBufferSync(new Gzip(opts), buffer);
};

exports.deflateRaw = function(buffer, opts, callback) {
if (util.isFunction(opts)) {
callback = opts;
opts = {};
}
zlibBuffer(new DeflateRaw(opts), buffer, callback);
return zlibBuffer(new DeflateRaw(opts), buffer, callback);
};

exports.deflateRawSync = function(buffer, opts) {
return zlibBufferSync(new DeflateRaw(opts), buffer);
};

exports.unzip = function(buffer, opts, callback) {
if (util.isFunction(opts)) {
callback = opts;
opts = {};
}
zlibBuffer(new Unzip(opts), buffer, callback);
return zlibBuffer(new Unzip(opts), buffer, callback);
};

exports.unzipSync = function(buffer, opts) {
return zlibBufferSync(new Unzip(opts), buffer);
};

exports.inflate = function(buffer, opts, callback) {
if (util.isFunction(opts)) {
callback = opts;
opts = {};
}
zlibBuffer(new Inflate(opts), buffer, callback);
return zlibBuffer(new Inflate(opts), buffer, callback);
};

exports.inflateSync = function(buffer, opts) {
return zlibBufferSync(new Inflate(opts), buffer);
};

exports.gunzip = function(buffer, opts, callback) {
if (util.isFunction(opts)) {
callback = opts;
opts = {};
}
zlibBuffer(new Gunzip(opts), buffer, callback);
return zlibBuffer(new Gunzip(opts), buffer, callback);
};

exports.gunzipSync = function(buffer, opts) {
return zlibBufferSync(new Gunzip(opts), buffer);
};

exports.inflateRaw = function(buffer, opts, callback) {
if (util.isFunction(opts)) {
callback = opts;
opts = {};
}
zlibBuffer(new InflateRaw(opts), buffer, callback);
return zlibBuffer(new InflateRaw(opts), buffer, callback);
};

exports.inflateRawSync = function(buffer, opts) {
return zlibBufferSync(new InflateRaw(opts), buffer);
};

function zlibBuffer(engine, buffer, callback) {
@@ -196,6 +224,16 @@ function zlibBuffer(engine, buffer, callback) {
}
}

function zlibBufferSync(engine, buffer) {
if (util.isString(buffer))
buffer = new Buffer(buffer);
if (!util.isBuffer(buffer))
throw new TypeError('Not a string or buffer');

var flushFlag = binding.Z_FINISH;

return engine._processChunk(buffer, flushFlag);
}

// generic zlib
// minimal 2-byte header
@@ -453,10 +491,48 @@ Zlib.prototype._transform = function(chunk, encoding, cb) {
}
}

var self = this;
this._processChunk(chunk, flushFlag, cb);
};

Zlib.prototype._processChunk = function(chunk, flushFlag, cb) {
var availInBefore = chunk && chunk.length;
var availOutBefore = this._chunkSize - this._offset;
var inOff = 0;

var self = this;

var async = util.isFunction(cb);

if (!async) {
var buffers = [];
var nread = 0;

var error;
this.on('error', function(er) {
error = er;
});

do {
var res = this._binding.writeSync(flushFlag,
chunk, // in
inOff, // in_off
availInBefore, // in_len
this._buffer, // out
this._offset, //out_off
availOutBefore); // out_len
} while (!this._hadError && callback(res[0], res[1]));

if (this._hadError) {
throw error;
}

var buf = Buffer.concat(buffers, nread);
this.close();

return buf;
}

var req = this._binding.write(flushFlag,
chunk, // in
inOff, // in_off
@@ -468,7 +544,6 @@ Zlib.prototype._transform = function(chunk, encoding, cb) {
req.buffer = chunk;
req.callback = callback;

var self = this;
function callback(availInAfter, availOutAfter) {
if (self._hadError)
return;
@@ -480,7 +555,12 @@ Zlib.prototype._transform = function(chunk, encoding, cb) {
var out = self._buffer.slice(self._offset, self._offset + have);
self._offset += have;
// serve some output to the consumer.
self.push(out);
if (async) {
self.push(out);
} else {
buffers.push(out);
nread += out.length;
}
}

// exhausted the output buffer, or used all the input create a new one.
@@ -498,6 +578,9 @@ Zlib.prototype._transform = function(chunk, encoding, cb) {
inOff += (availInBefore - availInAfter);
availInBefore = availInAfter;

if (!async)
return true;

var newReq = self._binding.write(flushFlag,
chunk,
inOff,
@@ -510,6 +593,9 @@ Zlib.prototype._transform = function(chunk, encoding, cb) {
return;
}

if (!async)
return false;

// finished with the chunk.
cb();
}
@@ -56,6 +56,7 @@
'lib/string_decoder.js',
'lib/sys.js',
'lib/timers.js',
'lib/tracing.js',
'lib/tls.js',
'lib/_tls_legacy.js',
'lib/_tls_wrap.js',
@@ -96,6 +97,7 @@
'src/node_javascript.cc',
'src/node_main.cc',
'src/node_os.cc',
'src/node_v8.cc',
'src/node_stat_watcher.cc',
'src/node_watchdog.cc',
'src/node_zlib.cc',
@@ -35,9 +35,12 @@

namespace node {

inline AsyncWrap::AsyncWrap(Environment* env, v8::Handle<v8::Object> object)
inline AsyncWrap::AsyncWrap(Environment* env,
v8::Handle<v8::Object> object,
ProviderType provider)
: BaseObject(env, object),
async_flags_(NO_OPTIONS) {
async_flags_(NO_OPTIONS),
provider_type_(provider) {
if (!env->has_async_listener())
return;

@@ -56,14 +59,13 @@ inline AsyncWrap::AsyncWrap(Environment* env, v8::Handle<v8::Object> object)
inline AsyncWrap::~AsyncWrap() {
}


inline uint32_t AsyncWrap::async_flags() const {
return async_flags_;
inline uint32_t AsyncWrap::provider_type() const {
return provider_type_;
}


inline bool AsyncWrap::has_async_listener() {
return async_flags() & HAS_ASYNC_LISTENER;
return async_flags_ & HAS_ASYNC_LISTENER;
}


@@ -35,14 +35,37 @@ class AsyncWrap : public BaseObject {
HAS_ASYNC_LISTENER = 1
};

inline AsyncWrap(Environment* env, v8::Handle<v8::Object> object);
enum ProviderType {
PROVIDER_NONE = 1 << 0,
PROVIDER_CARES = 1 << 1,
PROVIDER_CONNECTWRAP = 1 << 2,
PROVIDER_CRYPTO = 1 << 3,
PROVIDER_FSEVENTWRAP = 1 << 4,
PROVIDER_GETADDRINFOREQWRAP = 1 << 5,
PROVIDER_PIPEWRAP = 1 << 6,
PROVIDER_PROCESSWRAP = 1 << 7,
PROVIDER_REQWRAP = 1 << 8,
PROVIDER_SHUTDOWNWRAP = 1 << 9,
PROVIDER_SIGNALWRAP = 1 << 10,
PROVIDER_STATWATCHER = 1 << 11,
PROVIDER_TCPWRAP = 1 << 12,
PROVIDER_TIMERWRAP = 1 << 13,
PROVIDER_TLSWRAP = 1 << 14,
PROVIDER_TTYWRAP = 1 << 15,
PROVIDER_UDPWRAP = 1 << 16,
PROVIDER_ZLIB = 1 << 17
};

inline ~AsyncWrap();
inline AsyncWrap(Environment* env,
v8::Handle<v8::Object> object,
ProviderType provider);

inline uint32_t async_flags() const;
inline ~AsyncWrap();

inline bool has_async_listener();

inline uint32_t provider_type() const;

// Only call these within a valid HandleScope.
inline v8::Handle<v8::Value> MakeCallback(const v8::Handle<v8::Function> cb,
int argc,
@@ -65,6 +88,7 @@ class AsyncWrap : public BaseObject {
v8::Handle<v8::Value>* argv);

uint32_t async_flags_;
uint32_t provider_type_;
};

} // namespace node
@@ -224,7 +224,7 @@ static Local<Array> HostentToNames(struct hostent* host) {
class QueryWrap : public AsyncWrap {
public:
QueryWrap(Environment* env, Local<Object> req_wrap_obj)
: AsyncWrap(env, req_wrap_obj) {
: AsyncWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_CARES) {
}

virtual ~QueryWrap() {
@@ -997,7 +997,10 @@ static void GetAddrInfo(const FunctionCallbackInfo<Value>& args) {
abort();
}

GetAddrInfoReqWrap* req_wrap = new GetAddrInfoReqWrap(env, req_wrap_obj);
GetAddrInfoReqWrap* req_wrap =
new GetAddrInfoReqWrap(env,
req_wrap_obj,
AsyncWrap::PROVIDER_GETADDRINFOREQWRAP);

struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
@@ -33,9 +33,48 @@

namespace node {

inline Environment::GCInfo::GCInfo()
: type_(static_cast<v8::GCType>(0)),
flags_(static_cast<v8::GCCallbackFlags>(0)),
timestamp_(0) {
}

inline Environment::GCInfo::GCInfo(v8::Isolate* isolate,
v8::GCType type,
v8::GCCallbackFlags flags,
uint64_t timestamp)
: type_(type),
flags_(flags),
timestamp_(timestamp) {
isolate->GetHeapStatistics(&stats_);
}

inline v8::GCType Environment::GCInfo::type() const {
return type_;
}

inline v8::GCCallbackFlags Environment::GCInfo::flags() const {
return flags_;
}

inline v8::HeapStatistics* Environment::GCInfo::stats() const {
// TODO(bnoordhuis) Const-ify once https://codereview.chromium.org/63693005
// lands and makes it way into a stable release.
return const_cast<v8::HeapStatistics*>(&stats_);
}

inline uint64_t Environment::GCInfo::timestamp() const {
return timestamp_;
}

inline Environment::IsolateData* Environment::IsolateData::Get(
v8::Isolate* isolate) {
return static_cast<IsolateData*>(isolate->GetData());
}

inline Environment::IsolateData* Environment::IsolateData::GetOrCreate(
v8::Isolate* isolate) {
IsolateData* isolate_data = static_cast<IsolateData*>(isolate->GetData());
IsolateData* isolate_data = Get(isolate);
if (isolate_data == NULL) {
isolate_data = new IsolateData(isolate);
isolate->SetData(isolate_data);
@@ -59,6 +98,7 @@ inline Environment::IsolateData::IsolateData(v8::Isolate* isolate)
PER_ISOLATE_STRING_PROPERTIES(V)
#undef V
ref_count_(0) {
QUEUE_INIT(&gc_tracker_queue_);
}

inline uv_loop_t* Environment::IsolateData::event_loop() const {
@@ -86,6 +126,10 @@ inline bool Environment::AsyncListener::has_listener() const {
return fields_[kHasListener] > 0;
}

inline uint32_t Environment::AsyncListener::watched_providers() const {
return fields_[kWatchedProviders];
}

inline Environment::DomainFlag::DomainFlag() {
for (int i = 0; i < kFieldsCount; ++i) fields_[i] = 0;
}
@@ -180,13 +224,15 @@ inline Environment::Environment(v8::Local<v8::Context> context)
isolate_data_(IsolateData::GetOrCreate(context->GetIsolate())),
using_smalloc_alloc_cb_(false),
using_domains_(false),
printed_error_(false),
context_(context->GetIsolate(), context) {
// We'll be creating new objects so make sure we've entered the context.
v8::HandleScope handle_scope(isolate());
v8::Context::Scope context_scope(context);
set_binding_cache_object(v8::Object::New());
set_module_load_list_array(v8::Array::New());
RB_INIT(&cares_task_list_);
QUEUE_INIT(&gc_tracker_queue_);
}

inline Environment::~Environment() {
@@ -212,6 +258,11 @@ inline bool Environment::has_async_listener() const {
return const_cast<Environment*>(this)->async_listener()->has_listener();
}

inline uint32_t Environment::watched_providers() const {
// The const_cast is okay, it doesn't violate conceptual const-ness.
return const_cast<Environment*>(this)->async_listener()->watched_providers();
}

inline bool Environment::in_domain() const {
// The const_cast is okay, it doesn't violate conceptual const-ness.
return using_domains() &&
@@ -280,6 +331,14 @@ inline void Environment::set_using_domains(bool value) {
using_domains_ = value;
}

inline bool Environment::printed_error() const {
return printed_error_;
}

inline void Environment::set_printed_error(bool value) {
printed_error_ = value;
}

inline Environment* Environment::from_cares_timer_handle(uv_timer_t* handle) {
return CONTAINER_OF(handle, Environment, cares_timer_handle_);
}
@@ -27,6 +27,7 @@
#include "util.h"
#include "uv.h"
#include "v8.h"
#include "queue.h"

#include <stdint.h>

@@ -52,9 +53,9 @@ namespace node {
// for the sake of convenience.
#define PER_ISOLATE_STRING_PROPERTIES(V) \
V(address_string, "address") \
V(atime_string, "atime") \
V(async, "async") \
V(async_queue_string, "_asyncQueue") \
V(async, "async") \
V(atime_string, "atime") \
V(birthtime_string, "birthtime") \
V(blksize_string, "blksize") \
V(blocks_string, "blocks") \
@@ -78,16 +79,20 @@ namespace node {
V(family_string, "family") \
V(fatal_exception_string, "_fatalException") \
V(fingerprint_string, "fingerprint") \
V(flags_string, "flags") \
V(gid_string, "gid") \
V(handle_string, "handle") \
V(headers_string, "headers") \
V(heap_size_limit_string, "heap_size_limit") \
V(heap_total_string, "heapTotal") \
V(heap_used_string, "heapUsed") \
V(immediate_callback_string, "_immediateCallback") \
V(ino_string, "ino") \
V(ipv4_string, "IPv4") \
V(ipv6_string, "IPv6") \
V(issuer_string, "issuer") \
V(mark_sweep_compact_string, "mark-sweep-compact") \
V(message_string, "message") \
V(method_string, "method") \
V(mode_string, "mode") \
V(modulus_string, "modulus") \
@@ -111,25 +116,35 @@ namespace node {
V(onstop_string, "onstop") \
V(path_string, "path") \
V(port_string, "port") \
V(processed_string, "processed") \
V(rdev_string, "rdev") \
V(rename_string, "rename") \
V(rss_string, "rss") \
V(scavenge_string, "scavenge") \
V(serial_number_string, "serialNumber") \
V(servername_string, "servername") \
V(session_id_string, "sessionId") \
V(should_keep_alive_string, "shouldKeepAlive") \
V(size_string, "size") \
V(smalloc_p_string, "_smalloc_p") \
V(sni_context_err_string, "Invalid SNI context") \
V(sni_context_string, "sni_context") \
V(stack_string, "stack") \
V(status_code_string, "statusCode") \
V(status_message_string, "statusMessage") \
V(subject_string, "subject") \
V(subjectaltname_string, "subjectaltname") \
V(syscall_string, "syscall") \
V(timestamp_string, "timestamp") \
V(tls_ticket_string, "tlsTicket") \
V(total_heap_size_executable_string, "total_heap_size_executable") \
V(total_heap_size_string, "total_heap_size") \
V(total_physical_size_string, "total_physical_size") \
V(type_string, "type") \
V(uid_string, "uid") \
V(upgrade_string, "upgrade") \
V(url_string, "url") \
V(used_heap_size_string, "used_heap_size") \
V(valid_from_string, "valid_from") \
V(valid_to_string, "valid_to") \
V(version_major_string, "versionMajor") \
@@ -145,6 +160,7 @@ namespace node {
V(buffer_constructor_function, v8::Function) \
V(context, v8::Context) \
V(domain_array, v8::Array) \
V(gc_info_callback_function, v8::Function) \
V(module_load_list_array, v8::Array) \
V(pipe_constructor_template, v8::FunctionTemplate) \
V(process_object, v8::Object) \
@@ -178,13 +194,15 @@ class Environment {
inline uint32_t* fields();
inline int fields_count() const;
inline bool has_listener() const;
inline uint32_t watched_providers() const;

private:
friend class Environment; // So we can call the constructor.
inline AsyncListener();

enum Fields {
kHasListener,
kWatchedProviders,
kFieldsCount
};

@@ -251,10 +269,15 @@ class Environment {
static inline Environment* New(v8::Local<v8::Context> context);
inline void Dispose();

// Defined in src/node_profiler.cc.
void StartGarbageCollectionTracking(v8::Local<v8::Function> callback);
void StopGarbageCollectionTracking();

inline v8::Isolate* isolate() const;
inline uv_loop_t* event_loop() const;
inline bool has_async_listener() const;
inline bool in_domain() const;
inline uint32_t watched_providers() const;

static inline Environment* from_immediate_check_handle(uv_check_t* handle);
inline uv_check_t* immediate_check_handle();
@@ -282,6 +305,9 @@ class Environment {
inline bool using_domains() const;
inline void set_using_domains(bool value);

inline bool printed_error() const;
inline void set_printed_error(bool value);

// Strings are shared across shared contexts. The getters simply proxy to
// the per-isolate primitive.
#define V(PropertyName, StringValue) \
@@ -296,10 +322,13 @@ class Environment {
#undef V

private:
class GCInfo;
class IsolateData;
inline explicit Environment(v8::Local<v8::Context> context);
inline ~Environment();
inline IsolateData* isolate_data() const;
void AfterGarbageCollectionCallback(const GCInfo* before,
const GCInfo* after);

enum ContextEmbedderDataIndex {
kContextEmbedderDataIndex = NODE_CONTEXT_EMBEDDER_DATA_INDEX
@@ -319,28 +348,65 @@ class Environment {
ares_task_list cares_task_list_;
bool using_smalloc_alloc_cb_;
bool using_domains_;
QUEUE gc_tracker_queue_;
bool printed_error_;

#define V(PropertyName, TypeName) \
v8::Persistent<TypeName> PropertyName ## _;
ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V)
#undef V

class GCInfo {
public:
inline GCInfo();
inline GCInfo(v8::Isolate* isolate,
v8::GCType type,
v8::GCCallbackFlags flags,
uint64_t timestamp);
inline v8::GCType type() const;
inline v8::GCCallbackFlags flags() const;
// TODO(bnoordhuis) Const-ify once https://codereview.chromium.org/63693005
// lands and makes it way into a stable release.
inline v8::HeapStatistics* stats() const;
inline uint64_t timestamp() const;
private:
v8::GCType type_;
v8::GCCallbackFlags flags_;
v8::HeapStatistics stats_;
uint64_t timestamp_;
};

// Per-thread, reference-counted singleton.
class IsolateData {
public:
static inline IsolateData* GetOrCreate(v8::Isolate* isolate);
inline void Put();
inline uv_loop_t* event_loop() const;

// Defined in src/node_profiler.cc.
void StartGarbageCollectionTracking(Environment* env);
void StopGarbageCollectionTracking(Environment* env);

#define V(PropertyName, StringValue) \
inline v8::Local<v8::String> PropertyName() const;
PER_ISOLATE_STRING_PROPERTIES(V)
#undef V

private:
inline static IsolateData* Get(v8::Isolate* isolate);
inline explicit IsolateData(v8::Isolate* isolate);
inline v8::Isolate* isolate() const;

// Defined in src/node_profiler.cc.
static void BeforeGarbageCollection(v8::Isolate* isolate,
v8::GCType type,
v8::GCCallbackFlags flags);
static void AfterGarbageCollection(v8::Isolate* isolate,
v8::GCType type,
v8::GCCallbackFlags flags);
void BeforeGarbageCollection(v8::GCType type, v8::GCCallbackFlags flags);
void AfterGarbageCollection(v8::GCType type, v8::GCCallbackFlags flags);

uv_loop_t* const event_loop_;
v8::Isolate* const isolate_;

@@ -350,6 +416,9 @@ class Environment {
#undef V

unsigned int ref_count_;
QUEUE gc_tracker_queue_;
GCInfo gc_info_before_;
GCInfo gc_info_after_;

DISALLOW_COPY_AND_ASSIGN(IsolateData);
};
@@ -65,7 +65,10 @@ class FSEventWrap: public HandleWrap {


FSEventWrap::FSEventWrap(Environment* env, Handle<Object> object)
: HandleWrap(env, object, reinterpret_cast<uv_handle_t*>(&handle_)) {
: HandleWrap(env,
object,
reinterpret_cast<uv_handle_t*>(&handle_),
AsyncWrap::PROVIDER_FSEVENTWRAP) {
initialized_ = false;
}

@@ -90,8 +90,9 @@ void HandleWrap::Close(const FunctionCallbackInfo<Value>& args) {

HandleWrap::HandleWrap(Environment* env,
Handle<Object> object,
uv_handle_t* handle)
: AsyncWrap(env, object),
uv_handle_t* handle,
AsyncWrap::ProviderType provider)
: AsyncWrap(env, object, provider),
flags_(0),
handle__(handle) {
handle__->data = this;
@@ -36,8 +36,8 @@ namespace node {
// - Do not throw from handle methods. Set errno.
//
// - MakeCallback may only be made directly off the event loop.
// That is there can be no JavaScript stack frames underneith it.
// (Is there anyway to assert that?)
// That is there can be no JavaScript stack frames underneath it.
// (Is there any way to assert that?)
//
// - No use of v8::WeakReferenceCallback. The close callback signifies that
// we're done with a handle - external resources can be freed.
@@ -62,7 +62,8 @@ class HandleWrap : public AsyncWrap {
protected:
HandleWrap(Environment* env,
v8::Handle<v8::Object> object,
uv_handle_t* handle);
uv_handle_t* handle,
AsyncWrap::ProviderType provider);
virtual ~HandleWrap();

private:

Large diffs are not rendered by default.

@@ -86,18 +86,18 @@ NODE_EXTERN v8::Local<v8::Value> UVException(int errorno,
*/

NODE_EXTERN v8::Handle<v8::Value> MakeCallback(
const v8::Handle<v8::Object> recv,
v8::Handle<v8::Object> recv,
const char* method,
int argc,
v8::Handle<v8::Value>* argv);
NODE_EXTERN v8::Handle<v8::Value> MakeCallback(
const v8::Handle<v8::Object> object,
const v8::Handle<v8::String> symbol,
v8::Handle<v8::Object> recv,
v8::Handle<v8::String> symbol,
int argc,
v8::Handle<v8::Value>* argv);
NODE_EXTERN v8::Handle<v8::Value> MakeCallback(
const v8::Handle<v8::Object> object,
const v8::Handle<v8::Function> callback,
v8::Handle<v8::Object> recv,
v8::Handle<v8::Function> callback,
int argc,
v8::Handle<v8::Value>* argv);

@@ -190,7 +190,6 @@ enum encoding {ASCII, UTF8, BASE64, UCS2, BINARY, HEX, BUFFER};
enum encoding ParseEncoding(v8::Handle<v8::Value> encoding_v,
enum encoding _default = BINARY);
NODE_EXTERN void FatalException(const v8::TryCatch& try_catch);
void DisplayExceptionLine(v8::Handle<v8::Message> message);

NODE_EXTERN v8::Local<v8::Value> Encode(const void *buf, size_t len,
enum encoding encoding = BINARY);
@@ -26,7 +26,6 @@
// of the startup process, so many dependencies are invoked lazily.
(function(process) {
this.global = this;
var _errorHandler;

function startup() {
var EventEmitter = NativeModule.require('events').EventEmitter;
@@ -40,14 +39,16 @@

process.EventEmitter = EventEmitter; // process.EventEmitter is deprecated

// Setup the tracing module
NativeModule.require('tracing')._nodeInitialization(process);

// do this good and early, since it handles errors.
startup.processFatal();

startup.globalVariables();
startup.globalTimeouts();
startup.globalConsole();

startup.processAsyncListener();
startup.processAssert();
startup.processConfig();
startup.processNextTick();
@@ -220,6 +221,11 @@
};

startup.processFatal = function() {
var tracing = NativeModule.require('tracing');
var _errorHandler = tracing._errorHandler;
// Cleanup
delete tracing._errorHandler;

process._fatalException = function(er) {
// First run through error handlers from asyncListener.
var caught = _errorHandler(er);
@@ -255,338 +261,6 @@
};
};

startup.processAsyncListener = function() {
// new Array() is used here because it is more efficient for sparse
// arrays. Please *do not* change these to simple bracket notation.

// Track the active queue of AsyncListeners that have been added.
var asyncQueue = new Array();

// Keep the stack of all contexts that have been loaded in the
// execution chain of asynchronous events.
var contextStack = new Array();
var currentContext = undefined;

// Incremental uid for new AsyncListener instances.
var alUid = 0;

// Stateful flags shared with Environment for quick JS/C++
// communication.
var asyncFlags = {};

// Prevent accidentally suppressed thrown errors from before/after.
var inAsyncTick = false;

// To prevent infinite recursion when an error handler also throws
// flag when an error is currenly being handled.
var inErrorTick = false;

// Needs to be the same as src/env.h
var kHasListener = 0;

// Flags to determine what async listeners are available.
var HAS_CREATE_AL = 1 << 0;
var HAS_BEFORE_AL = 1 << 1;
var HAS_AFTER_AL = 1 << 2;
var HAS_ERROR_AL = 1 << 3;

// _errorHandler is scoped so it's also accessible by _fatalException.
_errorHandler = errorHandler;

// Needs to be accessible from lib/timers.js so they know when async
// listeners are currently in queue. They'll be cleaned up once
// references there are made.
process._asyncFlags = asyncFlags;
process._runAsyncQueue = runAsyncQueue;
process._loadAsyncQueue = loadAsyncQueue;
process._unloadAsyncQueue = unloadAsyncQueue;

// Public API.
process.createAsyncListener = createAsyncListener;
process.addAsyncListener = addAsyncListener;
process.removeAsyncListener = removeAsyncListener;

// Setup shared objects/callbacks with native layer.
process._setupAsyncListener(asyncFlags,
runAsyncQueue,
loadAsyncQueue,
unloadAsyncQueue);

// Load the currently executing context as the current context, and
// create a new asyncQueue that can receive any added queue items
// during the executing of the callback.
function loadContext(ctx) {
contextStack.push(currentContext);
currentContext = ctx;

asyncFlags[kHasListener] = 1;
}

function unloadContext() {
currentContext = contextStack.pop();

if (currentContext === undefined && asyncQueue.length === 0)
asyncFlags[kHasListener] = 0;
}

// Run all the async listeners attached when an asynchronous event is
// instantiated.
function runAsyncQueue(context) {
var queue = new Array();
var data = new Array();
var ccQueue, i, item, queueItem, value;

context._asyncQueue = queue;
context._asyncData = data;
context._asyncFlags = 0;

inAsyncTick = true;

// First run through all callbacks in the currentContext. These may
// add new AsyncListeners to the asyncQueue during execution. Hence
// why they need to be evaluated first.
if (currentContext) {
ccQueue = currentContext._asyncQueue;
context._asyncFlags |= currentContext._asyncFlags;
for (i = 0; i < ccQueue.length; i++) {
queueItem = ccQueue[i];
queue[queue.length] = queueItem;
if ((queueItem.flags & HAS_CREATE_AL) === 0) {
data[queueItem.uid] = queueItem.data;
continue;
}
value = queueItem.create(queueItem.data);
data[queueItem.uid] = (value === undefined) ? queueItem.data : value;
}
}

// Then run through all items in the asyncQueue
if (asyncQueue) {
for (i = 0; i < asyncQueue.length; i++) {
queueItem = asyncQueue[i];
// Quick way to check if an AL instance with the same uid was
// already run from currentContext.
if (data[queueItem.uid] !== undefined)
continue;
queue[queue.length] = queueItem;
context._asyncFlags |= queueItem.flags;
if ((queueItem.flags & HAS_CREATE_AL) === 0) {
data[queueItem.uid] = queueItem.data;
continue;
}
value = queueItem.create(queueItem.data);
data[queueItem.uid] = (value === undefined) ? queueItem.data : value;
}
}

inAsyncTick = false;
}

// Load the AsyncListener queue attached to context and run all
// "before" callbacks, if they exist.
function loadAsyncQueue(context) {
loadContext(context);

if ((context._asyncFlags & HAS_BEFORE_AL) === 0)
return;

var queue = context._asyncQueue;
var data = context._asyncData;
var i, queueItem;

inAsyncTick = true;
for (i = 0; i < queue.length; i++) {
queueItem = queue[i];
if ((queueItem.flags & HAS_BEFORE_AL) > 0)
queueItem.before(context, data[queueItem.uid]);
}
inAsyncTick = false;
}

// Unload the AsyncListener queue attached to context and run all
// "after" callbacks, if they exist.
function unloadAsyncQueue(context) {
if ((context._asyncFlags & HAS_AFTER_AL) === 0) {
unloadContext();
return;
}

var queue = context._asyncQueue;
var data = context._asyncData;
var i, queueItem;

inAsyncTick = true;
for (i = 0; i < queue.length; i++) {
queueItem = queue[i];
if ((queueItem.flags & HAS_AFTER_AL) > 0)
queueItem.after(context, data[queueItem.uid]);
}
inAsyncTick = false;

unloadContext();
}

// Handle errors that are thrown while in the context of an
// AsyncListener. If an error is thrown from an AsyncListener
// callback error handlers will be called once more to report
// the error, then the application will die forcefully.
function errorHandler(er) {
if (inErrorTick)
return false;

var handled = false;
var i, queueItem, threw;

inErrorTick = true;

// First process error callbacks from the current context.
if (currentContext && (currentContext._asyncFlags & HAS_ERROR_AL) > 0) {
var queue = currentContext._asyncQueue;
var data = currentContext._asyncData;
for (i = 0; i < queue.length; i++) {
queueItem = queue[i];
if ((queueItem.flags & HAS_ERROR_AL) === 0)
continue;
try {
threw = true;
// While it would be possible to pass in currentContext, if
// the error is thrown from the "create" callback then there's
// a chance the object hasn't been fully constructed.
handled = queueItem.error(data[queueItem.uid], er) || handled;
threw = false;
} finally {
// If the error callback thew then die quickly. Only allow the
// exit events to be processed.
if (threw) {
process._exiting = true;
process.emit('exit', 1);
}
}
}
}

// Now process callbacks from any existing queue.
if (asyncQueue) {
for (i = 0; i < asyncQueue.length; i++) {
queueItem = asyncQueue[i];
if ((queueItem.flags & HAS_ERROR_AL) === 0 ||
(data && data[queueItem.uid] !== undefined))
continue;
try {
threw = true;
handled = queueItem.error(queueItem.data, er) || handled;
threw = false;
} finally {
// If the error callback thew then die quickly. Only allow the
// exit events to be processed.
if (threw) {
process._exiting = true;
process.emit('exit', 1);
}
}
}
}

inErrorTick = false;

unloadContext();

// TODO(trevnorris): If the error was handled, should the after callbacks
// be fired anyways?

return handled && !inAsyncTick;
}

// Instance function of an AsyncListener object.
function AsyncListenerInst(callbacks, data) {
if (typeof callbacks.create === 'function') {
this.create = callbacks.create;
this.flags |= HAS_CREATE_AL;
}
if (typeof callbacks.before === 'function') {
this.before = callbacks.before;
this.flags |= HAS_BEFORE_AL;
}
if (typeof callbacks.after === 'function') {
this.after = callbacks.after;
this.flags |= HAS_AFTER_AL;
}
if (typeof callbacks.error === 'function') {
this.error = callbacks.error;
this.flags |= HAS_ERROR_AL;
}

this.uid = ++alUid;
this.data = data === undefined ? null : data;
}
AsyncListenerInst.prototype.create = undefined;
AsyncListenerInst.prototype.before = undefined;
AsyncListenerInst.prototype.after = undefined;
AsyncListenerInst.prototype.error = undefined;
AsyncListenerInst.prototype.data = undefined;
AsyncListenerInst.prototype.uid = 0;
AsyncListenerInst.prototype.flags = 0;

// Create new async listener object. Useful when instantiating a new
// object and want the listener instance, but not add it to the stack.
// If an existing AsyncListenerInst is passed then any new "data" is
// ignored.
function createAsyncListener(callbacks, data) {
if (typeof callbacks !== 'object' || callbacks == null)
throw new TypeError('callbacks argument must be an object');

if (callbacks instanceof AsyncListenerInst)
return callbacks;
else
return new AsyncListenerInst(callbacks, data);
}

// Add a listener to the current queue.
function addAsyncListener(callbacks, data) {
// Fast track if a new AsyncListenerInst has to be created.
if (!(callbacks instanceof AsyncListenerInst)) {
callbacks = createAsyncListener(callbacks, data);
asyncQueue.push(callbacks);
asyncFlags[kHasListener] = 1;
return callbacks;
}

var inQueue = false;
// The asyncQueue will be small. Probably always <= 3 items.
for (var i = 0; i < asyncQueue.length; i++) {
if (callbacks === asyncQueue[i]) {
inQueue = true;
break;
}
}

// Make sure the callback doesn't already exist in the queue.
if (!inQueue) {
asyncQueue.push(callbacks);
asyncFlags[kHasListener] = 1;
}

return callbacks;
}

// Remove listener from the current queue. Though this will not remove
// the listener from the current context. So callback propagation will
// continue.
function removeAsyncListener(obj) {
for (var i = 0; i < asyncQueue.length; i++) {
if (obj === asyncQueue[i]) {
asyncQueue.splice(i, 1);
break;
}
}

if (asyncQueue.length > 0 || currentContext !== undefined)
asyncFlags[kHasListener] = 1;
else
asyncFlags[kHasListener] = 0;
}
};

var assert;
startup.processAssert = function() {
assert = process.assert = function(x, msg) {
@@ -610,11 +284,12 @@
};

startup.processNextTick = function() {
var tracing = NativeModule.require('tracing');
var nextTickQueue = [];
var asyncFlags = process._asyncFlags;
var _runAsyncQueue = process._runAsyncQueue;
var _loadAsyncQueue = process._loadAsyncQueue;
var _unloadAsyncQueue = process._unloadAsyncQueue;
var asyncFlags = tracing._asyncFlags;
var _runAsyncQueue = tracing._runAsyncQueue;
var _loadAsyncQueue = tracing._loadAsyncQueue;
var _unloadAsyncQueue = tracing._unloadAsyncQueue;

// This tickInfo thing is used so that the C++ code in src/node.cc
// can have easy accesss to our nextTick state, and avoid unnecessary
@@ -60,6 +60,21 @@ NODE_EXTERN v8::Local<v8::Object> New(char* data,
// TODO(trevnorris): should be New() for consistency
NODE_EXTERN v8::Local<v8::Object> Use(char* data, uint32_t len);

// This is verbose to be explicit with inline commenting
static inline bool IsWithinBounds(size_t off, size_t len, size_t max) {
// Asking to seek too far into the buffer
// check to avoid wrapping in subsequent subtraction
if (off > max)
return false;

// Asking for more than is left over in the buffer
if (max - off < len)
return false;

// Otherwise we're in bounds
return true;
}

// Internal. Not for public consumption. We can't define these in
// src/node_internals.h due to a circular dependency issue with
// the smalloc.h and node_internals.h headers.
@@ -69,15 +69,23 @@ class ContextifyContext {
: env_(env),
sandbox_(env->isolate(), sandbox),
context_(env->isolate(), CreateV8Context(env)),
proxy_global_(env->isolate(), context()->Global()),
// Wait for sandbox_, proxy_global_, and context_ to die
references_(3) {
references_(0) {
sandbox_.MakeWeak(this, WeakCallback);
sandbox_.MarkIndependent();
references_++;

// Allocation failure or maximum call stack size reached
if (context_.IsEmpty())
return;
context_.MakeWeak(this, WeakCallback);
context_.MarkIndependent();
references_++;

proxy_global_.Reset(env->isolate(), context()->Global());
proxy_global_.MakeWeak(this, WeakCallback);
proxy_global_.MarkIndependent();
references_++;
}


@@ -149,6 +157,7 @@ class ContextifyContext {
if (clone_property_method.IsEmpty()) {
Local<String> code = FIXED_ONE_BYTE_STRING(node_isolate,
"(function cloneProperty(source, key, target) {\n"
" if (key === 'Proxy') return;\n"
" try {\n"
" var desc = Object.getOwnPropertyDescriptor(source, key);\n"
" if (desc.value === source) desc.value = target;\n"
@@ -180,6 +189,9 @@ class ContextifyContext {
HandleScope scope(node_isolate);
Local<Object> wrapper =
env->script_data_constructor_function()->NewInstance();
if (wrapper.IsEmpty())
return scope.Close(Handle<Value>());

Wrap<ContextifyContext>(wrapper, this);
return scope.Close(wrapper);
}
@@ -232,7 +244,17 @@ class ContextifyContext {
// Don't allow contextifying a sandbox multiple times.
assert(sandbox->GetHiddenValue(hidden_name).IsEmpty());

TryCatch try_catch;
ContextifyContext* context = new ContextifyContext(env, sandbox);

if (try_catch.HasCaught()) {
try_catch.ReThrow();
return;
}

if (context->context().IsEmpty())
return;

Local<External> hidden_context = External::New(context);
sandbox->SetHiddenValue(hidden_name, hidden_context);
}
@@ -432,7 +454,7 @@ class ContextifyScript : public BaseObject {

if (v8_script.IsEmpty()) {
if (display_errors) {
DisplayExceptionLine(try_catch.Message());
AppendExceptionLine(env, try_catch.Exception(), try_catch.Message());
}
try_catch.ReThrow();
return;
@@ -490,14 +512,18 @@ class ContextifyScript : public BaseObject {
"sandbox argument must have been converted to a context.");
}

if (contextify_context->context().IsEmpty())
return;

// Do the eval within the context
Context::Scope context_scope(contextify_context->context());
EvalMachine(contextify_context->env(),
timeout,
display_errors,
args,
try_catch);
contextify_context->CopyProperties();
if (EvalMachine(contextify_context->env(),
timeout,
display_errors,
args,
try_catch)) {
contextify_context->CopyProperties();
}
}

static int64_t GetTimeoutArg(const FunctionCallbackInfo<Value>& args,
@@ -565,14 +591,14 @@ class ContextifyScript : public BaseObject {
}


static void EvalMachine(Environment* env,
static bool EvalMachine(Environment* env,
const int64_t timeout,
const bool display_errors,
const FunctionCallbackInfo<Value>& args,
TryCatch& try_catch) {
if (!ContextifyScript::InstanceOf(env, args.This())) {
return ThrowTypeError(
"Script methods can only be called on script instances.");
ThrowTypeError("Script methods can only be called on script instances.");
return false;
}

ContextifyScript* wrapped_script =
@@ -590,19 +616,21 @@ class ContextifyScript : public BaseObject {

if (try_catch.HasCaught() && try_catch.HasTerminated()) {
V8::CancelTerminateExecution(args.GetIsolate());
return ThrowError("Script execution timed out.");
ThrowError("Script execution timed out.");
return false;
}

if (result.IsEmpty()) {
// Error occurred during execution of the script.
if (display_errors) {
DisplayExceptionLine(try_catch.Message());
AppendExceptionLine(env, try_catch.Exception(), try_catch.Message());
}
try_catch.ReThrow();
return;
return false;
}

args.GetReturnValue().Set(result);
return true;
}


@@ -856,6 +856,7 @@ void SSLWrap<Base>::AddMethods(Handle<FunctionTemplate> t) {
NODE_SET_PROTOTYPE_METHOD(t, "endParser", EndParser);
NODE_SET_PROTOTYPE_METHOD(t, "renegotiate", Renegotiate);
NODE_SET_PROTOTYPE_METHOD(t, "shutdown", Shutdown);
NODE_SET_PROTOTYPE_METHOD(t, "getTLSTicket", GetTLSTicket);

#ifdef SSL_set_max_send_fragment
NODE_SET_PROTOTYPE_METHOD(t, "setMaxSendFragment", SetMaxSendFragment);
@@ -1129,7 +1130,7 @@ void SSLWrap<Base>::GetSession(const FunctionCallbackInfo<Value>& args) {
unsigned char* sbuf = new unsigned char[slen];
unsigned char* p = sbuf;
i2d_SSL_SESSION(sess, &p);
args.GetReturnValue().Set(Encode(sbuf, slen, BINARY));
args.GetReturnValue().Set(Encode(sbuf, slen, BUFFER));
delete[] sbuf;
}

@@ -1247,6 +1248,25 @@ void SSLWrap<Base>::Shutdown(const FunctionCallbackInfo<Value>& args) {
}


template <class Base>
void SSLWrap<Base>::GetTLSTicket(const FunctionCallbackInfo<Value>& args) {
HandleScope scope(args.GetIsolate());

Base* w = Unwrap<Base>(args.This());
Environment* env = w->ssl_env();

SSL_SESSION* sess = SSL_get_session(w->ssl_);
if (sess == NULL || sess->tlsext_tick == NULL)
return;

Local<Object> buf = Buffer::New(env,
reinterpret_cast<char*>(sess->tlsext_tick),
sess->tlsext_ticklen);

args.GetReturnValue().Set(buf);
}


#ifdef SSL_set_max_send_fragment
template <class Base>
void SSLWrap<Base>::SetMaxSendFragment(
@@ -1862,9 +1882,9 @@ void Connection::EncIn(const FunctionCallbackInfo<Value>& args) {

size_t off = args[1]->Int32Value();
size_t len = args[2]->Int32Value();
if (off + len > buffer_length) {

if (!Buffer::IsWithinBounds(off, len, buffer_length))
return ThrowError("off + len > buffer.length");
}

int bytes_written;
char* data = buffer_data + off;
@@ -1912,9 +1932,9 @@ void Connection::ClearOut(const FunctionCallbackInfo<Value>& args) {

size_t off = args[1]->Int32Value();
size_t len = args[2]->Int32Value();
if (off + len > buffer_length) {

if (!Buffer::IsWithinBounds(off, len, buffer_length))
return ThrowError("off + len > buffer.length");
}

if (!SSL_is_init_finished(conn->ssl_)) {
int rv;
@@ -1983,9 +2003,9 @@ void Connection::EncOut(const FunctionCallbackInfo<Value>& args) {

size_t off = args[1]->Int32Value();
size_t len = args[2]->Int32Value();
if (off + len > buffer_length) {

if (!Buffer::IsWithinBounds(off, len, buffer_length))
return ThrowError("off + len > buffer.length");
}

int bytes_read = BIO_read(conn->bio_write_, buffer_data + off, len);

@@ -2014,9 +2034,9 @@ void Connection::ClearIn(const FunctionCallbackInfo<Value>& args) {

size_t off = args[1]->Int32Value();
size_t len = args[2]->Int32Value();
if (off + len > buffer_length) {

if (!Buffer::IsWithinBounds(off, len, buffer_length))
return ThrowError("off + len > buffer.length");
}

if (!SSL_is_init_finished(conn->ssl_)) {
int rv;
@@ -3478,7 +3498,7 @@ class PBKDF2Request : public AsyncWrap {
char* salt,
ssize_t iter,
ssize_t keylen)
: AsyncWrap(env, object),
: AsyncWrap(env, object, AsyncWrap::PROVIDER_CRYPTO),
digest_(digest),
error_(0),
passlen_(passlen),
@@ -3740,7 +3760,7 @@ void PBKDF2(const FunctionCallbackInfo<Value>& args) {
class RandomBytesRequest : public AsyncWrap {
public:
RandomBytesRequest(Environment* env, Local<Object> object, size_t size)
: AsyncWrap(env, object),
: AsyncWrap(env, object, AsyncWrap::PROVIDER_CRYPTO),
error_(0),
size_(size),
data_(static_cast<char*>(malloc(size))) {
@@ -187,6 +187,7 @@ class SSLWrap {
static void EndParser(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Renegotiate(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Shutdown(const v8::FunctionCallbackInfo<v8::Value>& args);
static void GetTLSTicket(const v8::FunctionCallbackInfo<v8::Value>& args);

#ifdef SSL_set_max_send_fragment
static void SetMaxSendFragment(
@@ -296,7 +297,7 @@ class Connection : public SSLWrap<Connection>, public AsyncWrap {
SecureContext* sc,
SSLWrap<Connection>::Kind kind)
: SSLWrap<Connection>(env, sc, kind),
AsyncWrap(env, wrap),
AsyncWrap(env, wrap, AsyncWrap::PROVIDER_CRYPTO),
bio_read_(NULL),
bio_write_(NULL),
hello_offset_(0) {
@@ -582,7 +583,7 @@ class Certificate : public AsyncWrap {
static void ExportChallenge(const v8::FunctionCallbackInfo<v8::Value>& args);

Certificate(Environment* env, v8::Local<v8::Object> wrap)
: AsyncWrap(env, wrap) {
: AsyncWrap(env, wrap, AsyncWrap::PROVIDER_CRYPTO) {
MakeWeak<Certificate>(this);
}
};
@@ -42,9 +42,9 @@
#define NODE_NET_SERVER_CONNECTION_ENABLED() (0)
#define NODE_NET_STREAM_END(arg0)
#define NODE_NET_STREAM_END_ENABLED() (0)
#define NODE_NET_SOCKET_READ(arg0, arg1)
#define NODE_NET_SOCKET_READ(arg0, arg1, arg2, arg3, arg4)
#define NODE_NET_SOCKET_READ_ENABLED() (0)
#define NODE_NET_SOCKET_WRITE(arg0, arg1)
#define NODE_NET_SOCKET_WRITE(arg0, arg1, arg2, arg3, arg4)
#define NODE_NET_SOCKET_WRITE_ENABLED() (0)
#define NODE_GC_START(arg0, arg1)
#define NODE_GC_DONE(arg0, arg1)
@@ -748,7 +748,7 @@ static void WriteBuffer(const FunctionCallbackInfo<Value>& args) {
return ThrowRangeError("length out of bounds");
if (off + len < off)
return ThrowRangeError("off + len overflow");
if (off + len > buffer_length)
if (!Buffer::IsWithinBounds(off, len, buffer_length))
return ThrowRangeError("off + len > buffer.length");

buf += off;
@@ -871,9 +871,8 @@ static void Read(const FunctionCallbackInfo<Value>& args) {
}

len = args[3]->Int32Value();
if (off + len > buffer_length) {
return ThrowError("Length extends beyond buffer");
}
if (!Buffer::IsWithinBounds(off, len, buffer_length))
return ThrowRangeError("Length extends beyond buffer");

pos = GET_OFFSET(args[4]);

@@ -49,29 +49,29 @@ inline v8::Local<TypeName> PersistentToLocal(

// Call with valid HandleScope and while inside Context scope.
v8::Handle<v8::Value> MakeCallback(Environment* env,
v8::Handle<v8::Object> object,
v8::Handle<v8::Object> recv,
const char* method,
int argc = 0,
v8::Handle<v8::Value>* argv = NULL);

// Call with valid HandleScope and while inside Context scope.
v8::Handle<v8::Value> MakeCallback(Environment* env,
const v8::Handle<v8::Object> object,
v8::Handle<v8::Object> recv,
uint32_t index,
int argc = 0,
v8::Handle<v8::Value>* argv = NULL);

// Call with valid HandleScope and while inside Context scope.
v8::Handle<v8::Value> MakeCallback(Environment* env,
const v8::Handle<v8::Object> object,
const v8::Handle<v8::String> symbol,
v8::Handle<v8::Object> recv,
v8::Handle<v8::String> symbol,
int argc = 0,
v8::Handle<v8::Value>* argv = NULL);

// Call with valid HandleScope and while inside Context scope.
v8::Handle<v8::Value> MakeCallback(Environment* env,
const v8::Handle<v8::Object> object,
const v8::Handle<v8::Function> callback,
v8::Handle<v8::Value> recv,
v8::Handle<v8::Function> callback,
int argc = 0,
v8::Handle<v8::Value>* argv = NULL);

@@ -155,6 +155,10 @@ inline static void ThrowUVException(int errorno,
v8::ThrowException(UVException(errorno, syscall, message, path));
}

void AppendExceptionLine(Environment* env,
v8::Handle<v8::Value> er,
v8::Handle<v8::Message> message);

NO_RETURN void FatalError(const char* location, const char* message);

v8::Local<v8::Object> BuildStatsObject(Environment* env, const uv_stat_t* s);
@@ -66,7 +66,7 @@ static void Delete(uv_handle_t* handle) {


StatWatcher::StatWatcher(Environment* env, Local<Object> wrap)
: AsyncWrap(env, wrap),
: AsyncWrap(env, wrap, AsyncWrap::PROVIDER_STATWATCHER),
watcher_(new uv_fs_poll_t) {
MakeWeak<StatWatcher>(this);
uv_fs_poll_init(env->event_loop(), watcher_);
@@ -0,0 +1,224 @@
// 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 "env.h"
#include "env-inl.h"
#include "util.h"
#include "util-inl.h"
#include "v8.h"

namespace node {

using v8::Context;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::GCCallbackFlags;
using v8::GCType;
using v8::Handle;
using v8::HandleScope;
using v8::HeapStatistics;
using v8::Isolate;
using v8::Local;
using v8::Null;
using v8::Number;
using v8::Object;
using v8::Uint32;
using v8::Value;
using v8::kGCTypeAll;
using v8::kGCTypeMarkSweepCompact;
using v8::kGCTypeScavenge;


void Environment::IsolateData::BeforeGarbageCollection(Isolate* isolate,
GCType type,
GCCallbackFlags flags) {
Get(isolate)->BeforeGarbageCollection(type, flags);
}


void Environment::IsolateData::AfterGarbageCollection(Isolate* isolate,
GCType type,
GCCallbackFlags flags) {
Get(isolate)->AfterGarbageCollection(type, flags);
}


void Environment::IsolateData::BeforeGarbageCollection(GCType type,
GCCallbackFlags flags) {
gc_info_before_ = GCInfo(isolate(), type, flags, uv_hrtime());
}


void Environment::IsolateData::AfterGarbageCollection(GCType type,
GCCallbackFlags flags) {
gc_info_after_ = GCInfo(isolate(), type, flags, uv_hrtime());

// The copy upfront and the remove-then-insert is to avoid corrupting the
// list when the callback removes itself from it. QUEUE_FOREACH() is unsafe
// when the list is mutated while being walked.
ASSERT(QUEUE_EMPTY(&gc_tracker_queue_) == false);
QUEUE queue;
QUEUE* q = QUEUE_HEAD(&gc_tracker_queue_);
QUEUE_SPLIT(&gc_tracker_queue_, q, &queue);
while (QUEUE_EMPTY(&queue) == false) {
q = QUEUE_HEAD(&queue);
QUEUE_REMOVE(q);
QUEUE_INSERT_TAIL(&gc_tracker_queue_, q);
Environment* env = CONTAINER_OF(q, Environment, gc_tracker_queue_);
env->AfterGarbageCollectionCallback(&gc_info_before_, &gc_info_after_);
}
}


void Environment::IsolateData::StartGarbageCollectionTracking(
Environment* env) {
if (QUEUE_EMPTY(&gc_tracker_queue_)) {
isolate()->AddGCPrologueCallback(BeforeGarbageCollection, v8::kGCTypeAll);
isolate()->AddGCEpilogueCallback(AfterGarbageCollection, v8::kGCTypeAll);
}
ASSERT(QUEUE_EMPTY(&env->gc_tracker_queue_) == true);
QUEUE_INSERT_TAIL(&gc_tracker_queue_, &env->gc_tracker_queue_);
}


void Environment::IsolateData::StopGarbageCollectionTracking(Environment* env) {
ASSERT(QUEUE_EMPTY(&env->gc_tracker_queue_) == false);
QUEUE_REMOVE(&env->gc_tracker_queue_);
QUEUE_INIT(&env->gc_tracker_queue_);
if (QUEUE_EMPTY(&gc_tracker_queue_)) {
isolate()->RemoveGCPrologueCallback(BeforeGarbageCollection);
isolate()->RemoveGCEpilogueCallback(AfterGarbageCollection);
}
}


// Considering a memory constrained environment, creating more objects is less
// than ideal
void Environment::AfterGarbageCollectionCallback(const GCInfo* before,
const GCInfo* after) {
HandleScope handle_scope(isolate());
Context::Scope context_scope(context());
Local<Value> argv[] = { Object::New(), Object::New() };
const GCInfo* infov[] = { before, after };
for (unsigned i = 0; i < ARRAY_SIZE(argv); i += 1) {
Local<Object> obj = argv[i].As<Object>();
const GCInfo* info = infov[i];
switch (info->type()) {
case kGCTypeScavenge:
obj->Set(type_string(), scavenge_string());
break;
case kGCTypeMarkSweepCompact:
obj->Set(type_string(), mark_sweep_compact_string());
break;
default:
UNREACHABLE();
}
obj->Set(flags_string(), Uint32::NewFromUnsigned(info->flags(), isolate()));
obj->Set(timestamp_string(), Number::New(isolate(), info->timestamp()));
// TODO(trevnorris): Setting many object properties in C++ is a significant
// performance hit. Redo this to pass the results to JS and create/set the
// properties there.
#define V(name) \
do { \
obj->Set(name ## _string(), \
Uint32::NewFromUnsigned(info->stats()->name(), isolate())); \
} while (0)
V(total_heap_size);
V(total_heap_size_executable);
V(total_physical_size);
V(used_heap_size);
V(heap_size_limit);
#undef V
}
MakeCallback(this,
Null(isolate()),
gc_info_callback_function(),
ARRAY_SIZE(argv),
argv);
}


void Environment::StartGarbageCollectionTracking(Local<Function> callback) {
ASSERT(gc_info_callback_function().IsEmpty() == true);
set_gc_info_callback_function(callback);
isolate_data()->StartGarbageCollectionTracking(this);
}


void Environment::StopGarbageCollectionTracking() {
ASSERT(gc_info_callback_function().IsEmpty() == false);
isolate_data()->StopGarbageCollectionTracking(this);
set_gc_info_callback_function(Local<Function>());
}


void StartGarbageCollectionTracking(const FunctionCallbackInfo<Value>& args) {
CHECK(args[0]->IsFunction() == true);
HandleScope handle_scope(args.GetIsolate());
Environment* env = Environment::GetCurrent(args.GetIsolate());
env->StartGarbageCollectionTracking(args[0].As<Function>());
}


void GetHeapStatistics(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
HandleScope handle_scope(isolate);
Environment* env = Environment::GetCurrent(isolate);
HeapStatistics s;
isolate->GetHeapStatistics(&s);
Local<Object> info = Object::New();
// TODO(trevnorris): Setting many object properties in C++ is a significant
// performance hit. Redo this to pass the results to JS and create/set the
// properties there.
#define V(name) \
info->Set(env->name ## _string(), Uint32::NewFromUnsigned(s.name(), isolate))
V(total_heap_size);
V(total_heap_size_executable);
V(total_physical_size);
V(used_heap_size);
V(heap_size_limit);
#undef V
args.GetReturnValue().Set(info);
}


void StopGarbageCollectionTracking(const FunctionCallbackInfo<Value>& args) {
HandleScope handle_scope(args.GetIsolate());
Environment::GetCurrent(args.GetIsolate())->StopGarbageCollectionTracking();
}


void InitializeV8Bindings(Handle<Object> target,
Handle<Value> unused,
Handle<Context> context) {
NODE_SET_METHOD(target,
"startGarbageCollectionTracking",
StartGarbageCollectionTracking);
NODE_SET_METHOD(target,
"stopGarbageCollectionTracking",
StopGarbageCollectionTracking);
NODE_SET_METHOD(target, "getHeapStatistics", GetHeapStatistics);
}

} // namespace node

NODE_MODULE_CONTEXT_AWARE_BUILTIN(v8, node::InitializeV8Bindings)
@@ -39,6 +39,7 @@

namespace node {

using v8::Array;
using v8::Context;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
@@ -73,7 +74,7 @@ class ZCtx : public AsyncWrap {
public:

ZCtx(Environment* env, Local<Object> wrap, node_zlib_mode mode)
: AsyncWrap(env, wrap),
: AsyncWrap(env, wrap, AsyncWrap::PROVIDER_ZLIB),
chunk_size_(0),
dictionary_(NULL),
dictionary_len_(0),
@@ -125,6 +126,7 @@ class ZCtx : public AsyncWrap {


// write(flush, in, in_off, in_len, out, out_off, out_len)
template <bool async>
static void Write(const FunctionCallbackInfo<Value>& args) {
HandleScope scope(node_isolate);
assert(args.Length() == 7);
@@ -167,15 +169,15 @@ class ZCtx : public AsyncWrap {
in_off = args[2]->Uint32Value();
in_len = args[3]->Uint32Value();

assert(in_off + in_len <= Buffer::Length(in_buf));
assert(Buffer::IsWithinBounds(in_off, in_len, Buffer::Length(in_buf)));
in = reinterpret_cast<Bytef *>(Buffer::Data(in_buf) + in_off);
}

assert(Buffer::HasInstance(args[4]));
Local<Object> out_buf = args[4]->ToObject();
out_off = args[5]->Uint32Value();
out_len = args[6]->Uint32Value();
assert(out_off + out_len <= Buffer::Length(out_buf));
assert(Buffer::IsWithinBounds(out_off, out_len, Buffer::Length(out_buf)));
out = reinterpret_cast<Bytef *>(Buffer::Data(out_buf) + out_off);

// build up the work request
@@ -190,6 +192,15 @@ class ZCtx : public AsyncWrap {
// set this so that later on, I can easily tell how much was written.
ctx->chunk_size_ = out_len;

if (!async) {
// sync version
Process(work_req);
if (CheckError(ctx))
AfterSync(ctx, args);
return;
}

// async version
uv_queue_work(ctx->env()->event_loop(),
work_req,
ZCtx::Process,
@@ -199,6 +210,21 @@ class ZCtx : public AsyncWrap {
}


static void AfterSync(ZCtx* ctx, const FunctionCallbackInfo<Value>& args) {
Local<Integer> avail_out = Integer::New(ctx->strm_.avail_out, node_isolate);
Local<Integer> avail_in = Integer::New(ctx->strm_.avail_in, node_isolate);

ctx->write_in_progress_ = false;

Local<Array> result = Array::New(2);
result->Set(0, avail_in);
result->Set(1, avail_out);
args.GetReturnValue().Set(result);

ctx->Unref();
}


// thread pool!
// This function may be called multiple times on the uv_work pool
// for a single write() call, until all of the input bytes have
@@ -249,6 +275,31 @@ class ZCtx : public AsyncWrap {
// or shift the queue and call Process.
}


static bool CheckError(ZCtx* ctx) {
// Acceptable error states depend on the type of zlib stream.
switch (ctx->err_) {
case Z_OK:
case Z_STREAM_END:
case Z_BUF_ERROR:
// normal statuses, not fatal
break;
case Z_NEED_DICT:
if (ctx->dictionary_ == NULL)
ZCtx::Error(ctx, "Missing dictionary");
else
ZCtx::Error(ctx, "Bad dictionary");
return false;
default:
// something else.
ZCtx::Error(ctx, "Zlib error");
return false;
}

return true;
}


// v8 land!
static void After(uv_work_t* work_req, int status) {
assert(status == 0);
@@ -259,25 +310,8 @@ class ZCtx : public AsyncWrap {
HandleScope handle_scope(env->isolate());
Context::Scope context_scope(env->context());

// Acceptable error states depend on the type of zlib stream.
switch (ctx->err_) {
case Z_OK:
case Z_STREAM_END:
case Z_BUF_ERROR:
// normal statuses, not fatal
break;
case Z_NEED_DICT:
if (ctx->dictionary_ == NULL) {
ZCtx::Error(ctx, "Missing dictionary");
} else {
ZCtx::Error(ctx, "Bad dictionary");
}
return;
default:
// something else.
ZCtx::Error(ctx, "Zlib error");
return;
}
if (!CheckError(ctx))
return;

Local<Integer> avail_out = Integer::New(ctx->strm_.avail_out, node_isolate);
Local<Integer> avail_in = Integer::New(ctx->strm_.avail_in, node_isolate);
@@ -556,7 +590,8 @@ void InitZlib(Handle<Object> target,

z->InstanceTemplate()->SetInternalFieldCount(1);

NODE_SET_PROTOTYPE_METHOD(z, "write", ZCtx::Write);
NODE_SET_PROTOTYPE_METHOD(z, "write", ZCtx::Write<true>);
NODE_SET_PROTOTYPE_METHOD(z, "writeSync", ZCtx::Write<false>);
NODE_SET_PROTOTYPE_METHOD(z, "init", ZCtx::Init);
NODE_SET_PROTOTYPE_METHOD(z, "close", ZCtx::Close);
NODE_SET_PROTOTYPE_METHOD(z, "params", ZCtx::Params);
@@ -128,7 +128,10 @@ void PipeWrap::New(const FunctionCallbackInfo<Value>& args) {


PipeWrap::PipeWrap(Environment* env, Handle<Object> object, bool ipc)
: StreamWrap(env, object, reinterpret_cast<uv_stream_t*>(&handle_)) {
: StreamWrap(env,
object,
reinterpret_cast<uv_stream_t*>(&handle_),
AsyncWrap::PROVIDER_PIPEWRAP) {
int r = uv_pipe_init(env->event_loop(), &handle_, ipc);
assert(r == 0); // How do we proxy this error up to javascript?
// Suggestion: uv_pipe_init() returns void.
@@ -275,7 +278,9 @@ void PipeWrap::Connect(const FunctionCallbackInfo<Value>& args) {
Local<Object> req_wrap_obj = args[0].As<Object>();
String::AsciiValue name(args[1]);

ConnectWrap* req_wrap = new ConnectWrap(env, req_wrap_obj);
ConnectWrap* req_wrap = new ConnectWrap(env,
req_wrap_obj,
AsyncWrap::PROVIDER_CONNECTWRAP);
uv_pipe_connect(&req_wrap->req_,
&wrap->handle_,
*name,
@@ -78,7 +78,10 @@ class ProcessWrap : public HandleWrap {
}

ProcessWrap(Environment* env, Handle<Object> object)
: HandleWrap(env, object, reinterpret_cast<uv_handle_t*>(&process_)) {
: HandleWrap(env,
object,
reinterpret_cast<uv_handle_t*>(&process_),
AsyncWrap::PROVIDER_PROCESSWRAP) {
}

~ProcessWrap() {
@@ -37,8 +37,10 @@ extern QUEUE req_wrap_queue;
template <typename T>
class ReqWrap : public AsyncWrap {
public:
ReqWrap(Environment* env, v8::Handle<v8::Object> object)
: AsyncWrap(env, object) {
ReqWrap(Environment* env,
v8::Handle<v8::Object> object,
AsyncWrap::ProviderType provider = AsyncWrap::PROVIDER_REQWRAP)
: AsyncWrap(env, object, AsyncWrap::PROVIDER_REQWRAP) {
if (env->in_domain())
object->Set(env->domain_string(), env->domain_array()->Get(0));

@@ -72,7 +72,10 @@ class SignalWrap : public HandleWrap {
}

SignalWrap(Environment* env, Handle<Object> object)
: HandleWrap(env, object, reinterpret_cast<uv_handle_t*>(&handle_)) {
: HandleWrap(env,
object,
reinterpret_cast<uv_handle_t*>(&handle_),
AsyncWrap::PROVIDER_SIGNALWRAP) {
int r = uv_signal_init(env->event_loop(), &handle_);
assert(r == 0);
}
@@ -57,8 +57,9 @@ using v8::Value;

StreamWrap::StreamWrap(Environment* env,
Local<Object> object,
uv_stream_t* stream)
: HandleWrap(env, object, reinterpret_cast<uv_handle_t*>(stream)),
uv_stream_t* stream,
AsyncWrap::ProviderType provider)
: HandleWrap(env, object, reinterpret_cast<uv_handle_t*>(stream), provider),
stream_(stream),
default_callbacks_(this),
callbacks_(&default_callbacks_) {
@@ -528,7 +529,9 @@ void StreamWrap::Shutdown(const FunctionCallbackInfo<Value>& args) {
assert(args[0]->IsObject());
Local<Object> req_wrap_obj = args[0].As<Object>();

ShutdownWrap* req_wrap = new ShutdownWrap(env, req_wrap_obj);
ShutdownWrap* req_wrap = new ShutdownWrap(env,
req_wrap_obj,
AsyncWrap::PROVIDER_SHUTDOWNWRAP);
int err = wrap->callbacks()->DoShutdown(req_wrap, AfterShutdown);
req_wrap->Dispatched();
if (err)
@@ -37,6 +37,8 @@ typedef class ReqWrap<uv_shutdown_t> ShutdownWrap;

class WriteWrap: public ReqWrap<uv_write_t> {
public:
// TODO(trevnorris): WrapWrap inherits from ReqWrap, which I've globbed
// into the same provider. How should these be broken apart?
WriteWrap(Environment* env, v8::Local<v8::Object> obj, StreamWrap* wrap)
: ReqWrap<uv_write_t>(env, obj),
wrap_(wrap) {
@@ -150,7 +152,8 @@ class StreamWrap : public HandleWrap {

StreamWrap(Environment* env,
v8::Local<v8::Object> object,
uv_stream_t* stream);
uv_stream_t* stream,
AsyncWrap::ProviderType provider);

~StreamWrap() {
if (callbacks_ != &default_callbacks_) {
@@ -139,7 +139,10 @@ void TCPWrap::New(const FunctionCallbackInfo<Value>& args) {


TCPWrap::TCPWrap(Environment* env, Handle<Object> object)
: StreamWrap(env, object, reinterpret_cast<uv_stream_t*>(&handle_)) {
: StreamWrap(env,
object,
reinterpret_cast<uv_stream_t*>(&handle_),
AsyncWrap::PROVIDER_TCPWRAP) {
int r = uv_tcp_init(env->event_loop(), &handle_);
assert(r == 0); // How do we proxy this error up to javascript?
// Suggestion: uv_tcp_init() returns void.
@@ -377,7 +380,9 @@ void TCPWrap::Connect(const FunctionCallbackInfo<Value>& args) {
int err = uv_ip4_addr(*ip_address, port, &addr);

if (err == 0) {
ConnectWrap* req_wrap = new ConnectWrap(env, req_wrap_obj);
ConnectWrap* req_wrap = new ConnectWrap(env,
req_wrap_obj,
AsyncWrap::PROVIDER_CONNECTWRAP);
err = uv_tcp_connect(&req_wrap->req_,
&wrap->handle_,
reinterpret_cast<const sockaddr*>(&addr),
@@ -409,7 +414,9 @@ void TCPWrap::Connect6(const FunctionCallbackInfo<Value>& args) {
int err = uv_ip6_addr(*ip_address, port, &addr);

if (err == 0) {
ConnectWrap* req_wrap = new ConnectWrap(env, req_wrap_obj);
ConnectWrap* req_wrap = new ConnectWrap(env,
req_wrap_obj,
AsyncWrap::PROVIDER_CONNECTWRAP);
err = uv_tcp_connect(&req_wrap->req_,
&wrap->handle_,
reinterpret_cast<const sockaddr*>(&addr),
@@ -83,7 +83,10 @@ class TimerWrap : public HandleWrap {
}

TimerWrap(Environment* env, Handle<Object> object)
: HandleWrap(env, object, reinterpret_cast<uv_handle_t*>(&handle_)) {
: HandleWrap(env,
object,
reinterpret_cast<uv_handle_t*>(&handle_),
AsyncWrap::PROVIDER_TIMERWRAP) {
int r = uv_timer_init(env->event_loop(), &handle_);
assert(r == 0);
}
@@ -68,7 +68,9 @@ TLSCallbacks::TLSCallbacks(Environment* env,
StreamWrapCallbacks* old)
: SSLWrap<TLSCallbacks>(env, Unwrap<SecureContext>(sc), kind),
StreamWrapCallbacks(old),
AsyncWrap(env, env->tls_wrap_constructor_function()->NewInstance()),
AsyncWrap(env,
env->tls_wrap_constructor_function()->NewInstance(),
AsyncWrap::PROVIDER_TLSWRAP),
sc_(Unwrap<SecureContext>(sc)),
sc_handle_(env->isolate(), sc),
enc_in_(NULL),
@@ -747,29 +749,37 @@ void TLSCallbacks::SetServername(const FunctionCallbackInfo<Value>& args) {


int TLSCallbacks::SelectSNIContextCallback(SSL* s, int* ad, void* arg) {
HandleScope scope(node_isolate);

TLSCallbacks* p = static_cast<TLSCallbacks*>(arg);
Environment* env = p->env();

const char* servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);

if (servername != NULL) {
// Call the SNI callback and use its return value as context
Local<Object> object = p->object();
Local<Value> ctx = object->Get(env->sni_context_string());
if (servername == NULL)
return SSL_TLSEXT_ERR_OK;

if (!ctx->IsObject())
return SSL_TLSEXT_ERR_NOACK;
HandleScope scope(env->isolate());
// Call the SNI callback and use its return value as context
Local<Object> object = p->object();
Local<Value> ctx = object->Get(env->sni_context_string());

p->sni_context_.Dispose();
p->sni_context_.Reset(node_isolate, ctx);
// Not an object, probably undefined or null
if (!ctx->IsObject())
return SSL_TLSEXT_ERR_NOACK;

SecureContext* sc = Unwrap<SecureContext>(ctx.As<Object>());
InitNPN(sc, p);
SSL_set_SSL_CTX(s, sc->ctx_);
Local<FunctionTemplate> cons = env->secure_context_constructor_template();
if (!cons->HasInstance(ctx)) {
// Failure: incorrect SNI context object
Local<Value> err = Exception::TypeError(env->sni_context_err_string());
p->MakeCallback(env->onerror_string(), 1, &err);
return SSL_TLSEXT_ERR_NOACK;
}

p->sni_context_.Dispose();
p->sni_context_.Reset(node_isolate, ctx);

SecureContext* sc = Unwrap<SecureContext>(ctx.As<Object>());
InitNPN(sc, p);
SSL_set_SSL_CTX(s, sc->ctx_);
return SSL_TLSEXT_ERR_OK;
}
#endif // SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
@@ -174,7 +174,10 @@ void TTYWrap::New(const FunctionCallbackInfo<Value>& args) {


TTYWrap::TTYWrap(Environment* env, Handle<Object> object, int fd, bool readable)
: StreamWrap(env, object, reinterpret_cast<uv_stream_t*>(&handle_)) {
: StreamWrap(env,
object,
reinterpret_cast<uv_stream_t*>(&handle_),
AsyncWrap::PROVIDER_TTYWRAP) {
uv_tty_init(env->event_loop(), &handle_, fd, readable);
}

@@ -73,7 +73,10 @@ inline bool SendWrap::have_callback() const {


UDPWrap::UDPWrap(Environment* env, Handle<Object> object)
: HandleWrap(env, object, reinterpret_cast<uv_handle_t*>(&handle_)) {
: HandleWrap(env,
object,
reinterpret_cast<uv_handle_t*>(&handle_),
AsyncWrap::PROVIDER_UDPWRAP) {
int r = uv_udp_init(env->event_loop(), &handle_);
assert(r == 0); // can't fail anyway
}
@@ -89,75 +89,85 @@ exports.spawnPwd = function(options) {
}
};

var knownGlobals = [setTimeout,
setInterval,
setImmediate,
clearTimeout,
clearInterval,
clearImmediate,
console,
constructor, // Enumerable in V8 3.21.
Buffer,
process,
global];

if (global.gc) {
knownGlobals.push(gc);
}

// Turn this off if the test should not check for global leaks.
exports.globalCheck = true;
if (global.DTRACE_HTTP_SERVER_RESPONSE) {
knownGlobals.push(DTRACE_HTTP_SERVER_RESPONSE);
knownGlobals.push(DTRACE_HTTP_SERVER_REQUEST);
knownGlobals.push(DTRACE_HTTP_CLIENT_RESPONSE);
knownGlobals.push(DTRACE_HTTP_CLIENT_REQUEST);
knownGlobals.push(DTRACE_NET_STREAM_END);
knownGlobals.push(DTRACE_NET_SERVER_CONNECTION);
knownGlobals.push(DTRACE_NET_SOCKET_READ);
knownGlobals.push(DTRACE_NET_SOCKET_WRITE);
}

process.on('exit', function() {
if (!exports.globalCheck) return;
var knownGlobals = [setTimeout,
setInterval,
setImmediate,
clearTimeout,
clearInterval,
clearImmediate,
console,
constructor, // Enumerable in V8 3.21.
Buffer,
process,
global];

if (global.gc) {
knownGlobals.push(gc);
}
if (global.COUNTER_NET_SERVER_CONNECTION) {
knownGlobals.push(COUNTER_NET_SERVER_CONNECTION);
knownGlobals.push(COUNTER_NET_SERVER_CONNECTION_CLOSE);
knownGlobals.push(COUNTER_HTTP_SERVER_REQUEST);
knownGlobals.push(COUNTER_HTTP_SERVER_RESPONSE);
knownGlobals.push(COUNTER_HTTP_CLIENT_REQUEST);
knownGlobals.push(COUNTER_HTTP_CLIENT_RESPONSE);
}

if (global.DTRACE_HTTP_SERVER_RESPONSE) {
knownGlobals.push(DTRACE_HTTP_SERVER_RESPONSE);
knownGlobals.push(DTRACE_HTTP_SERVER_REQUEST);
knownGlobals.push(DTRACE_HTTP_CLIENT_RESPONSE);
knownGlobals.push(DTRACE_HTTP_CLIENT_REQUEST);
knownGlobals.push(DTRACE_NET_STREAM_END);
knownGlobals.push(DTRACE_NET_SERVER_CONNECTION);
knownGlobals.push(DTRACE_NET_SOCKET_READ);
knownGlobals.push(DTRACE_NET_SOCKET_WRITE);
}
if (global.COUNTER_NET_SERVER_CONNECTION) {
knownGlobals.push(COUNTER_NET_SERVER_CONNECTION);
knownGlobals.push(COUNTER_NET_SERVER_CONNECTION_CLOSE);
knownGlobals.push(COUNTER_HTTP_SERVER_REQUEST);
knownGlobals.push(COUNTER_HTTP_SERVER_RESPONSE);
knownGlobals.push(COUNTER_HTTP_CLIENT_REQUEST);
knownGlobals.push(COUNTER_HTTP_CLIENT_RESPONSE);
}
if (global.ArrayBuffer) {
knownGlobals.push(ArrayBuffer);
knownGlobals.push(Int8Array);
knownGlobals.push(Uint8Array);
knownGlobals.push(Uint8ClampedArray);
knownGlobals.push(Int16Array);
knownGlobals.push(Uint16Array);
knownGlobals.push(Int32Array);
knownGlobals.push(Uint32Array);
knownGlobals.push(Float32Array);
knownGlobals.push(Float64Array);
knownGlobals.push(DataView);
}

if (global.ArrayBuffer) {
knownGlobals.push(ArrayBuffer);
knownGlobals.push(Int8Array);
knownGlobals.push(Uint8Array);
knownGlobals.push(Uint8ClampedArray);
knownGlobals.push(Int16Array);
knownGlobals.push(Uint16Array);
knownGlobals.push(Int32Array);
knownGlobals.push(Uint32Array);
knownGlobals.push(Float32Array);
knownGlobals.push(Float64Array);
knownGlobals.push(DataView);
}
// Harmony features.
if (global.Proxy) {
knownGlobals.push(Proxy);
}

if (global.Symbol) {
knownGlobals.push(Symbol);
}

function leakedGlobals() {
var leaked = [];

for (var x in global) {
var found = false;
for (var val in global)
if (-1 === knownGlobals.indexOf(global[val]))
leaked.push(val);

for (var y in knownGlobals) {
if (global[x] === knownGlobals[y]) {
found = true;
break;
}
}
return leaked;
};
exports.leakedGlobals = leakedGlobals;

if (!found) {
console.error('Unknown global: %s', x);
assert.ok(false, 'Unknown global found');
}
// Turn this off if the test should not check for global leaks.
exports.globalCheck = true;

process.on('exit', function() {
if (!exports.globalCheck) return;
var leaked = leakedGlobals();
if (leaked.length > 0) {
console.error('Unknown globals: %s', leaked);
assert.ok(false, 'Unknown global found');
}
});