From 2cad7a69ce3228b1e40f3bf8117ca739a5d6929d Mon Sep 17 00:00:00 2001 From: Trevor Norris Date: Mon, 20 May 2013 14:37:55 -0700 Subject: [PATCH 01/19] buffer: throw when writing beyond buffer Previously one could write anywhere in a buffer pool if they accidently got their offset wrong. Mainly because the cc level checks only test against the parent slow buffer and not against the js object properties. So now we check to make sure values won't go beyond bounds without letting the dev know. --- lib/buffer.js | 3 +++ test/simple/test-buffer.js | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/lib/buffer.js b/lib/buffer.js index c75dbc93a8..000c54b3a8 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -339,6 +339,9 @@ Buffer.prototype.write = function(string, offset, length, encoding) { } encoding = String(encoding || 'utf8').toLowerCase(); + if (string.length > 0 && (length < 0 || offset < 0)) + throw new RangeError('attempt to write beyond buffer bounds'); + var ret; switch (encoding) { case 'hex': diff --git a/test/simple/test-buffer.js b/test/simple/test-buffer.js index 3808442ad1..0d071127c9 100644 --- a/test/simple/test-buffer.js +++ b/test/simple/test-buffer.js @@ -221,6 +221,16 @@ new Buffer(0); b.write('', 1024); b.write('', 2048); +// throw when writing past bounds from the pool +assert.throws(function() { + b.write('a', 2048); +}, RangeError); + +// throw when writing to negative offset +assert.throws(function() { + b.write('a', -1); +}, RangeError); + // try to copy 0 bytes worth of data into an empty buffer b.copy(new Buffer(0), 0, 0, 0); From f46ad012bc5a40194242ea1e9669c9cc25bd7047 Mon Sep 17 00:00:00 2001 From: Timothy J Fontaine Date: Fri, 17 May 2013 15:04:24 -0700 Subject: [PATCH 02/19] timers: internal unref'd timer for api timeouts When an internal api needs a timeout, they should use timers._unrefActive since that won't hold the loop open. This solves the problem where you might have unref'd the socket handle but the timeout for the socket was still active. --- lib/timers.js | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/lib/timers.js b/lib/timers.js index 708f0af16e..076503e601 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -373,3 +373,111 @@ exports.clearImmediate = function(immediate) { process._needImmediateCallback = false; } }; + + +// Internal APIs that need timeouts should use timers._unrefActive isntead of +// timers.active as internal timeouts shouldn't hold the loop open + +var unrefList, unrefTimer; + + +function unrefTimeout() { + var now = Date.now(); + + debug('unrefTimer fired'); + + var first; + while (first = L.peek(unrefList)) { + var diff = now - first._idleStart; + + if (diff < first._idleTimeout) { + diff = first._idleTimeout - diff; + unrefTimer.start(diff, 0); + unrefTimer.when = now + diff; + debug('unrefTimer rescheudling for later'); + return; + } + + L.remove(first); + + var domain = first.domain; + + if (!first._onTimeout) continue; + if (domain && domain._disposed) continue; + + try { + if (domain) domain.enter(); + var threw = true; + debug('unreftimer firing timeout'); + first._onTimeout(); + threw = false; + if (domain) domain.exit(); + } finally { + if (threw) process.nextTick(unrefTimeout); + } + } + + debug('unrefList is empty'); + unrefTimer.when = -1; +} + + +exports._unrefActive = function(item) { + var msecs = item._idleTimeout; + if (!msecs || msecs < 0) return; + assert(msecs >= 0); + + L.remove(item); + + if (!unrefList) { + debug('unrefList initialized'); + unrefList = {}; + L.init(unrefList); + + debug('unrefTimer initialized'); + unrefTimer = new Timer(); + unrefTimer.unref(); + unrefTimer.when = -1; + unrefTimer.ontimeout = unrefTimeout; + } + + var now = Date.now(); + item._idleStart = now; + + if (L.isEmpty(unrefList)) { + debug('unrefList empty'); + L.append(unrefList, item); + + unrefTimer.start(msecs, 0); + unrefTimer.when = now + msecs; + debug('unrefTimer scheduled'); + return; + } + + var when = now + msecs; + + debug('unrefList find where we can insert'); + + var cur, them; + + for (cur = unrefList._idlePrev; cur != unrefList; cur = cur._idlePrev) { + them = cur._idleStart + cur._idleTimeout; + + if (when < them) { + debug('unrefList inserting into middle of list'); + + L.append(cur, item); + + if (unrefTimer.when > when) { + debug('unrefTimer is scheduled to fire too late, reschedule'); + unrefTimer.start(msecs, 0); + unrefTimer.when = when; + } + + return; + } + } + + debug('unrefList append to end'); + L.append(unrefList, item); +}; From a846d9388cbdf298f17b341d0ed669c1852185a8 Mon Sep 17 00:00:00 2001 From: Timothy J Fontaine Date: Fri, 17 May 2013 15:07:28 -0700 Subject: [PATCH 03/19] net: use timers._unrefActive for internal timeouts --- lib/net.js | 14 +++--- test/simple/test-net-socket-timeout-unref.js | 47 ++++++++++++++++++++ 2 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 test/simple/test-net-socket-timeout-unref.js diff --git a/lib/net.js b/lib/net.js index 646983f6ca..7f6f306b84 100644 --- a/lib/net.js +++ b/lib/net.js @@ -307,7 +307,7 @@ Socket.prototype.listen = function() { Socket.prototype.setTimeout = function(msecs, callback) { if (msecs > 0 && !isNaN(msecs) && isFinite(msecs)) { timers.enroll(this, msecs); - timers.active(this); + timers._unrefActive(this); if (callback) { this.once('timeout', callback); } @@ -481,7 +481,7 @@ function onread(buffer, offset, length) { var self = handle.owner; assert(handle === self._handle, 'handle != self._handle'); - timers.active(self); + timers._unrefActive(self); var end = offset + length; debug('onread', process._errno, offset, length, end); @@ -612,7 +612,7 @@ Socket.prototype._write = function(data, encoding, cb) { this._pendingData = null; this._pendingEncoding = ''; - timers.active(this); + timers._unrefActive(this); if (!this._handle) { this._destroy(new Error('This socket is closed.'), cb); @@ -702,7 +702,7 @@ function afterWrite(status, handle, req) { return; } - timers.active(self); + timers._unrefActive(self); if (self !== process.stderr && self !== process.stdout) debug('afterWrite call cb'); @@ -783,7 +783,7 @@ Socket.prototype.connect = function(options, cb) { self.once('connect', cb); } - timers.active(this); + timers._unrefActive(this); self._connecting = true; self.writable = true; @@ -814,7 +814,7 @@ Socket.prototype.connect = function(options, cb) { self._destroy(); }); } else { - timers.active(self); + timers._unrefActive(self); addressType = addressType || 4; @@ -861,7 +861,7 @@ function afterConnect(status, handle, req, readable, writable) { if (status == 0) { self.readable = readable; self.writable = writable; - timers.active(self); + timers._unrefActive(self); self.emit('connect'); diff --git a/test/simple/test-net-socket-timeout-unref.js b/test/simple/test-net-socket-timeout-unref.js new file mode 100644 index 0000000000..32aef444a9 --- /dev/null +++ b/test/simple/test-net-socket-timeout-unref.js @@ -0,0 +1,47 @@ +// 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 common = require('../common'); +var assert = require('assert'); +var net = require('net'); + +var server = net.createServer(function (c) { + c.write('hello'); + c.unref(); +}); +server.listen(common.PORT); +server.unref(); + +var timedout = false; + +[8, 5, 3, 6, 2, 4].forEach(function (T) { + var socket = net.createConnection(common.PORT, 'localhost'); + socket.setTimeout(T * 1000, function () { + console.log(process._getActiveHandles()); + timedout = true; + socket.destroy(); + }); + socket.unref(); +}); + +process.on('exit', function () { + assert.strictEqual(timedout, false, 'Socket timeout should not hold loop open'); +}); From 99b737bd60a5c3c4e0569d7c24fae0326fda548d Mon Sep 17 00:00:00 2001 From: Nathan Rajlich Date: Tue, 21 May 2013 09:26:42 -0700 Subject: [PATCH 04/19] configure: respect the --dest-os flag consistently Consider a user on his Mac, who wants to cross-compile for his Linux ARM device: ./configure --dest-cpu=arm --dest-os=linux Before this patch, for example, DTrace probes would incorrectly attempt to be enabled because the configure script is running on a MacOS machine, even though we're trying to compile a binary for `linux`. With this patch, the `--dest-os` flag is respected throughout the configure script, thus leaving DTrace probes disabled in this cross-compiling scenario. --- configure | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/configure b/configure index 09f3c40978..4747dd2a16 100755 --- a/configure +++ b/configure @@ -10,7 +10,8 @@ import sys CC = os.environ.get('CC', 'cc') root_dir = os.path.dirname(__file__) -sys.path.insert(0, os.path.join(root_dir, 'deps', 'v8', 'tools')) +sys.path.insert(0, os.path.join(root_dir, 'tools', 'gyp', 'pylib')) +from gyp.common import GetFlavor # parse our options parser = optparse.OptionParser() @@ -236,7 +237,7 @@ parser.add_option("--dest-os", action="store", dest="dest_os", help="Operating system to build for. Valid values are: " - "win, mac, solaris, freebsd, linux") + "win, mac, solaris, freebsd, openbsd, linux") parser.add_option("--no-ifaddrs", action="store_true", @@ -468,16 +469,16 @@ def configure_node(o): # By default, enable DTrace on SunOS systems. Don't allow it on other # systems, since it won't work. (The MacOS build process is different than # SunOS, and we haven't implemented it.) - if sys.platform.startswith('sunos') or sys.platform.startswith('darwin'): + if flavor in ('solaris', 'mac'): o['variables']['node_use_dtrace'] = b(not options.without_dtrace) - elif sys.platform.startswith('linux'): + elif flavor == 'linux': o['variables']['node_use_dtrace'] = 'false' o['variables']['node_use_systemtap'] = b(options.with_dtrace) if options.systemtap_includes: o['include_dirs'] += [options.systemtap_includes] elif options.with_dtrace: raise Exception( - 'DTrace is currently only supported on SunOS or Linux systems.') + 'DTrace is currently only supported on SunOS, MacOS or Linux systems.') else: o['variables']['node_use_dtrace'] = 'false' o['variables']['node_use_systemtap'] = 'false' @@ -486,7 +487,7 @@ def configure_node(o): o['defines'] += ['SUNOS_NO_IFADDRS'] # By default, enable ETW on Windows. - if sys.platform.startswith('win32'): + if flavor == 'win': o['variables']['node_use_etw'] = b(not options.without_etw); elif options.with_etw: raise Exception('ETW is only supported on Windows.') @@ -494,7 +495,7 @@ def configure_node(o): o['variables']['node_use_etw'] = 'false' # By default, enable Performance counters on Windows. - if sys.platform.startswith('win32'): + if flavor == 'win': o['variables']['node_use_perfctr'] = b(not options.without_perfctr); elif options.with_perfctr: raise Exception('Performance counter is only supported on Windows.') @@ -607,7 +608,7 @@ def configure_openssl(o): def configure_winsdk(o): - if not sys.platform.startswith('win32'): + if flavor != 'win': return winsdk_dir = os.environ.get("WindowsSdkDir") @@ -620,6 +621,13 @@ def configure_winsdk(o): print "ctrpp not found in WinSDK path--using pre-gen files from tools/msvs/genfiles." +# determine the "flavor" (operating system) we're building for, +# leveraging gyp's GetFlavor function +flavor_params = {}; +if (options.dest_os): + flavor_params['flavor'] = options.dest_os; +flavor = GetFlavor(flavor_params); + output = { 'variables': { 'python': sys.executable }, 'include_dirs': [], @@ -668,14 +676,12 @@ write('config.mk', '# Do not edit. Generated by the configure script.\n' + config) if options.use_ninja: - gyp_args = ['-f', 'ninja'] + gyp_args = ['-f', 'ninja-' + flavor] elif options.use_xcode: gyp_args = ['-f', 'xcode'] -elif os.name == 'nt': +elif flavor == 'win': gyp_args = ['-f', 'msvs', '-G', 'msvs_version=auto'] -elif options.dest_os: - gyp_args = ['-f', 'make-' + options.dest_os] else: - gyp_args = ['-f', 'make'] + gyp_args = ['-f', 'make-' + flavor] subprocess.call([sys.executable, 'tools/gyp_node'] + gyp_args) From 89dcf22526044918ae3f79f6728ad4e603a1d710 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 22 May 2013 13:47:29 +0200 Subject: [PATCH 05/19] doc: the build requires gcc 4.2 or newer --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index aa505e555b..9f6e07871a 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ Evented I/O for V8 javascript. [![Build Status](https://secure.travis-ci.org/joy Prerequisites (Unix only): + * GCC 4.2 or newer * Python 2.6 or 2.7 * GNU Make 3.81 or newer * libexecinfo (FreeBSD and OpenBSD only) From fda2b319dc8a5dae528f8b12096911b71a0282db Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 22 May 2013 21:43:35 +0200 Subject: [PATCH 06/19] http: save roundtrips, convert buffers to strings This commit adds an optimization to the HTTP client that makes it possible to: * Pack the headers and the first chunk of the request body into a single write(). * Pack the chunk header and the chunk itself into a single write(). Because only one write() system call is issued instead of several, the chances of data ending up in a single TCP packet are phenomenally higher: the benchmark with `type=buf size=32` jumps from 50 req/s to 7,500 req/s, a 150-fold increase. This commit removes the check from e4b716ef that pushes binary encoded strings into the slow path. The commit log mentions that: We were assuming that any string can be concatenated safely to CRLF. However, for hex, base64, or binary encoded writes, this is not the case, and results in sending the incorrect response. For hex and base64 strings that's certainly true but binary strings are 'das Ding an sich': string.length is the same before and after decoding. Fixes #5528. --- benchmark/http/client-request-body.js | 69 +++++++++++++++++++++++++++ lib/http.js | 20 +++++++- 2 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 benchmark/http/client-request-body.js diff --git a/benchmark/http/client-request-body.js b/benchmark/http/client-request-body.js new file mode 100644 index 0000000000..7a3468a670 --- /dev/null +++ b/benchmark/http/client-request-body.js @@ -0,0 +1,69 @@ +// Measure the time it takes for the HTTP client to send a request body. + +var common = require('../common.js'); +var http = require('http'); + +var bench = common.createBenchmark(main, { + dur: [5], + type: ['asc', 'utf', 'buf'], + bytes: [32, 256, 1024], + method: ['write', 'end '] // two spaces added to line up each row +}); + +function main(conf) { + var dur = +conf.dur; + var len = +conf.bytes; + + var encoding; + var chunk; + switch (conf.type) { + case 'buf': + chunk = new Buffer(len); + chunk.fill('x'); + break; + case 'utf': + encoding = 'utf8'; + chunk = new Array(len / 2 + 1).join('ü'); + break; + case 'asc': + chunk = new Array(len + 1).join('a'); + break; + } + + var nreqs = 0; + var options = { + headers: { 'Connection': 'keep-alive', 'Transfer-Encoding': 'chunked' }, + agent: new http.Agent({ maxSockets: 1 }), + host: '127.0.0.1', + port: common.PORT, + path: '/', + method: 'POST' + }; + + var server = http.createServer(function(req, res) { + res.end(); + }); + server.listen(options.port, options.host, function() { + setTimeout(done, dur * 1000); + bench.start(); + pummel(); + }); + + function pummel() { + var req = http.request(options, function(res) { + nreqs++; + pummel(); // Line up next request. + res.resume(); + }); + if (conf.method === 'write') { + req.write(chunk, encoding); + req.end(); + } else { + req.end(chunk, encoding); + } + } + + function done() { + bench.end(nreqs); + } +} diff --git a/lib/http.js b/lib/http.js index 4a4fe94f3e..bcde516dae 100644 --- a/lib/http.js +++ b/lib/http.js @@ -782,12 +782,28 @@ OutgoingMessage.prototype.write = function(chunk, encoding) { if (chunk.length === 0) return false; + // TODO(bnoordhuis) Temporary optimization hack, remove in v0.11. We only + // want to convert the buffer when we're sending: + // + // a) Transfer-Encoding chunks, because it lets us pack the chunk header + // and the chunk into a single write(), or + // + // b) the first chunk of a fixed-length request, because it lets us pack + // the request headers and the chunk into a single write(). + // + // Converting to strings is expensive, CPU-wise, but reducing the number + // of write() calls more than makes up for that because we're dramatically + // reducing the number of TCP roundtrips. + if (chunk instanceof Buffer && (this.chunkedEncoding || !this._headerSent)) { + chunk = chunk.toString('binary'); + encoding = 'binary'; + } + var len, ret; if (this.chunkedEncoding) { if (typeof(chunk) === 'string' && encoding !== 'hex' && - encoding !== 'base64' && - encoding !== 'binary') { + encoding !== 'base64') { len = Buffer.byteLength(chunk, encoding); chunk = len.toString(16) + CRLF + chunk + CRLF; ret = this._send(chunk, encoding); From 279361b27729118481ca2e2fecc2c3f930c56f41 Mon Sep 17 00:00:00 2001 From: Timothy J Fontaine Date: Thu, 23 May 2013 13:39:12 -0700 Subject: [PATCH 07/19] v8: update to 3.14.5.9 --- deps/v8/build/common.gypi | 11 ++++++++++- deps/v8/src/flag-definitions.h | 2 ++ deps/v8/src/isolate.cc | 15 +++++++++++++++ deps/v8/src/json-parser.h | 7 ++----- deps/v8/src/objects-inl.h | 3 +-- deps/v8/src/objects.h | 7 ++----- deps/v8/src/parser.cc | 4 ++-- deps/v8/src/parser.h | 5 +++++ deps/v8/src/platform-posix.cc | 17 +---------------- deps/v8/src/stub-cache.cc | 8 ++++---- deps/v8/src/v8utils.h | 2 -- deps/v8/src/version.cc | 2 +- deps/v8/tools/gen-postmortem-metadata.py | 15 ++++----------- 13 files changed, 49 insertions(+), 49 deletions(-) diff --git a/deps/v8/build/common.gypi b/deps/v8/build/common.gypi index 6e0ef0c997..78888b8d7c 100644 --- a/deps/v8/build/common.gypi +++ b/deps/v8/build/common.gypi @@ -157,7 +157,7 @@ [ 'v8_use_arm_eabi_hardfloat=="true"', { 'defines': [ 'USE_EABI_HARDFLOAT=1', - 'CAN_USE_VFP2_INSTRUCTIONS', + 'CAN_USE_VFP3_INSTRUCTIONS', ], 'target_conditions': [ ['_toolset=="target"', { @@ -378,6 +378,15 @@ 'conditions': [ ['OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="netbsd" \ or OS=="android"', { + 'cflags!': [ + '-O2', + '-Os', + ], + 'cflags': [ + '-fdata-sections', + '-ffunction-sections', + '-O3', + ], 'conditions': [ [ 'gcc_version==44 and clang==0', { 'cflags': [ diff --git a/deps/v8/src/flag-definitions.h b/deps/v8/src/flag-definitions.h index 4c7c090f40..0427e7d852 100644 --- a/deps/v8/src/flag-definitions.h +++ b/deps/v8/src/flag-definitions.h @@ -449,6 +449,8 @@ DEFINE_int(sim_stack_alignment, 8, "Stack alingment in bytes in simulator (4 or 8, 8 is default)") // isolate.cc +DEFINE_bool(abort_on_uncaught_exception, false, + "abort program (dump core) when an uncaught exception is thrown") DEFINE_bool(trace_exception, false, "print stack trace when throwing exceptions") DEFINE_bool(preallocate_message_memory, false, diff --git a/deps/v8/src/isolate.cc b/deps/v8/src/isolate.cc index 75e15a4541..04a438bfa5 100644 --- a/deps/v8/src/isolate.cc +++ b/deps/v8/src/isolate.cc @@ -1080,6 +1080,7 @@ bool Isolate::IsErrorObject(Handle obj) { return false; } +static int fatal_exception_depth = 0; void Isolate::DoThrow(Object* exception, MessageLocation* location) { ASSERT(!has_pending_exception()); @@ -1150,6 +1151,20 @@ void Isolate::DoThrow(Object* exception, MessageLocation* location) { thread_local_top()->pending_message_start_pos_ = location->start_pos(); thread_local_top()->pending_message_end_pos_ = location->end_pos(); } + + // If the abort-on-uncaught-exception flag is specified, abort on any + // exception not caught by JavaScript, even when an external handler is + // present. This flag is intended for use by JavaScript developers, so + // print a user-friendly stack trace (not an internal one). + if (fatal_exception_depth == 0 && + FLAG_abort_on_uncaught_exception && + (report_exception || can_be_caught_externally)) { + fatal_exception_depth++; + fprintf(stderr, "%s\n\nFROM\n", + *MessageHandler::GetLocalizedMessage(this, message_obj)); + PrintCurrentStackTrace(stderr); + OS::Abort(); + } } else if (location != NULL && !location->script().is_null()) { // We are bootstrapping and caught an error where the location is set // and we have a script for the location. diff --git a/deps/v8/src/json-parser.h b/deps/v8/src/json-parser.h index ebe3db1382..03ed22d70e 100644 --- a/deps/v8/src/json-parser.h +++ b/deps/v8/src/json-parser.h @@ -287,7 +287,6 @@ Handle JsonParser::ParseJsonValue() { // Parse a JSON object. Position must be right at '{'. template Handle JsonParser::ParseJsonObject() { - HandleScope scope; Handle prototype; Handle json_object = factory()->NewJSObject(object_constructor()); @@ -361,13 +360,12 @@ Handle JsonParser::ParseJsonObject() { if (!prototype.is_null()) SetPrototype(json_object, prototype); } AdvanceSkipWhitespace(); - return scope.CloseAndEscape(json_object); + return json_object; } // Parse a JSON array. Position must be right at '['. template Handle JsonParser::ParseJsonArray() { - HandleScope scope; ZoneScope zone_scope(zone(), DELETE_ON_EXIT); ZoneList > elements(4, zone()); ASSERT_EQ(c0_, '['); @@ -390,8 +388,7 @@ Handle JsonParser::ParseJsonArray() { for (int i = 0, n = elements.length(); i < n; i++) { fast_elements->set(i, *elements[i]); } - Handle json_array = factory()->NewJSArrayWithElements(fast_elements); - return scope.CloseAndEscape(json_array); + return factory()->NewJSArrayWithElements(fast_elements); } diff --git a/deps/v8/src/objects-inl.h b/deps/v8/src/objects-inl.h index 4834fa63a4..ea5a93f16b 100644 --- a/deps/v8/src/objects-inl.h +++ b/deps/v8/src/objects-inl.h @@ -3500,9 +3500,8 @@ Code::Flags Code::ComputeFlags(Kind kind, kind == CALL_IC || kind == STORE_IC || kind == KEYED_STORE_IC); - ASSERT(argc <= Code::kMaxArguments); // Compute the bit mask. - unsigned int bits = KindField::encode(kind) + int bits = KindField::encode(kind) | ICStateField::encode(ic_state) | TypeField::encode(type) | ExtraICStateField::encode(extra_ic_state) diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h index 47d775781b..755dd42d9e 100644 --- a/deps/v8/src/objects.h +++ b/deps/v8/src/objects.h @@ -4180,8 +4180,8 @@ class Code: public HeapObject { // FLAGS_MIN_VALUE and FLAGS_MAX_VALUE are specified to ensure that // enumeration type has correct value range (see Issue 830 for more details). enum Flags { - FLAGS_MIN_VALUE = 0, - FLAGS_MAX_VALUE = kMaxUInt32 + FLAGS_MIN_VALUE = kMinInt, + FLAGS_MAX_VALUE = kMaxInt }; #define CODE_KIND_LIST(V) \ @@ -4644,9 +4644,6 @@ class Code: public HeapObject { // Signed field cannot be encoded using the BitField class. static const int kArgumentsCountShift = 14; static const int kArgumentsCountMask = ~((1 << kArgumentsCountShift) - 1); - static const int kArgumentsBits = - PlatformSmiTagging::kSmiValueSize - Code::kArgumentsCountShift + 1; - static const int kMaxArguments = (1 << kArgumentsBits) - 1; // This constant should be encodable in an ARM instruction. static const int kFlagsNotUsedInLookup = diff --git a/deps/v8/src/parser.cc b/deps/v8/src/parser.cc index 6da414af93..03e4b039cc 100644 --- a/deps/v8/src/parser.cc +++ b/deps/v8/src/parser.cc @@ -4243,7 +4243,7 @@ ZoneList* Parser::ParseArguments(bool* ok) { while (!done) { Expression* argument = ParseAssignmentExpression(true, CHECK_OK); result->Add(argument, zone()); - if (result->length() > Code::kMaxArguments) { + if (result->length() > kMaxNumFunctionParameters) { ReportMessageAt(scanner().location(), "too_many_arguments", Vector::empty()); *ok = false; @@ -4420,7 +4420,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle function_name, top_scope_->DeclareParameter(param_name, VAR); num_parameters++; - if (num_parameters > Code::kMaxArguments) { + if (num_parameters > kMaxNumFunctionParameters) { ReportMessageAt(scanner().location(), "too_many_parameters", Vector::empty()); *ok = false; diff --git a/deps/v8/src/parser.h b/deps/v8/src/parser.h index e36a9b3dca..93fd1b8aa9 100644 --- a/deps/v8/src/parser.h +++ b/deps/v8/src/parser.h @@ -449,6 +449,11 @@ class Parser { Vector > args); private: + // Limit on number of function parameters is chosen arbitrarily. + // Code::Flags uses only the low 17 bits of num-parameters to + // construct a hashable id, so if more than 2^17 are allowed, this + // should be checked. + static const int kMaxNumFunctionParameters = 32766; static const int kMaxNumFunctionLocals = 131071; // 2^17-1 enum Mode { diff --git a/deps/v8/src/platform-posix.cc b/deps/v8/src/platform-posix.cc index ad74eba8d9..3bc83733ca 100644 --- a/deps/v8/src/platform-posix.cc +++ b/deps/v8/src/platform-posix.cc @@ -109,26 +109,11 @@ void* OS::GetRandomMmapAddr() { raw_addr &= V8_UINT64_C(0x3ffffffff000); #else uint32_t raw_addr = V8::RandomPrivate(isolate); - - raw_addr &= 0x3ffff000; - -# ifdef __sun - // For our Solaris/illumos mmap hint, we pick a random address in the bottom - // half of the top half of the address space (that is, the third quarter). - // Because we do not MAP_FIXED, this will be treated only as a hint -- the - // system will not fail to mmap() because something else happens to already - // be mapped at our random address. We deliberately set the hint high enough - // to get well above the system's break (that is, the heap); Solaris and - // illumos will try the hint and if that fails allocate as if there were - // no hint at all. The high hint prevents the break from getting hemmed in - // at low values, ceding half of the address space to the system heap. - raw_addr += 0x80000000; -# else // The range 0x20000000 - 0x60000000 is relatively unpopulated across a // variety of ASLR modes (PAE kernel, NX compat mode, etc) and on macos // 10.6 and 10.7. + raw_addr &= 0x3ffff000; raw_addr += 0x20000000; -# endif #endif return reinterpret_cast(raw_addr); } diff --git a/deps/v8/src/stub-cache.cc b/deps/v8/src/stub-cache.cc index 8490c7e748..411914719c 100644 --- a/deps/v8/src/stub-cache.cc +++ b/deps/v8/src/stub-cache.cc @@ -617,7 +617,7 @@ Handle StubCache::ComputeCallConstant(int argc, Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); - ASSERT(flags == code->flags()); + ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); @@ -655,7 +655,7 @@ Handle StubCache::ComputeCallField(int argc, Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); - ASSERT(flags == code->flags()); + ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); @@ -692,7 +692,7 @@ Handle StubCache::ComputeCallInterceptor(int argc, Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); - ASSERT(flags == code->flags()); + ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); @@ -721,7 +721,7 @@ Handle StubCache::ComputeCallGlobal(int argc, CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); - ASSERT(flags == code->flags()); + ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); diff --git a/deps/v8/src/v8utils.h b/deps/v8/src/v8utils.h index 111abdf8b8..9072b4e285 100644 --- a/deps/v8/src/v8utils.h +++ b/deps/v8/src/v8utils.h @@ -209,8 +209,6 @@ INLINE(void CopyChars(sinkchar* dest, const sourcechar* src, int chars)); template void CopyChars(sinkchar* dest, const sourcechar* src, int chars) { - ASSERT(chars >= 0); - if (chars == 0) return; sinkchar* limit = dest + chars; #ifdef V8_HOST_CAN_READ_UNALIGNED if (sizeof(*dest) == sizeof(*src)) { diff --git a/deps/v8/src/version.cc b/deps/v8/src/version.cc index 715c2e5393..f7dcbd3039 100644 --- a/deps/v8/src/version.cc +++ b/deps/v8/src/version.cc @@ -35,7 +35,7 @@ #define MAJOR_VERSION 3 #define MINOR_VERSION 14 #define BUILD_NUMBER 5 -#define PATCH_LEVEL 8 +#define PATCH_LEVEL 9 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) #define IS_CANDIDATE_VERSION 0 diff --git a/deps/v8/tools/gen-postmortem-metadata.py b/deps/v8/tools/gen-postmortem-metadata.py index 7bee763bc9..f59cfd3033 100644 --- a/deps/v8/tools/gen-postmortem-metadata.py +++ b/deps/v8/tools/gen-postmortem-metadata.py @@ -76,23 +76,16 @@ consts_misc = [ { 'name': 'SmiTag', 'value': 'kSmiTag' }, { 'name': 'SmiTagMask', 'value': 'kSmiTagMask' }, { 'name': 'SmiValueShift', 'value': 'kSmiTagSize' }, - { 'name': 'SmiShiftSize', 'value': 'kSmiShiftSize' }, { 'name': 'PointerSizeLog2', 'value': 'kPointerSizeLog2' }, - { 'name': 'prop_desc_key', - 'value': 'DescriptorArray::kDescriptorKey' }, - { 'name': 'prop_desc_details', - 'value': 'DescriptorArray::kDescriptorDetails' }, - { 'name': 'prop_desc_value', - 'value': 'DescriptorArray::kDescriptorValue' }, - { 'name': 'prop_desc_size', - 'value': 'DescriptorArray::kDescriptorSize' }, + { 'name': 'prop_idx_transitions', + 'value': 'DescriptorArray::kTransitionsIndex' }, { 'name': 'prop_idx_first', 'value': 'DescriptorArray::kFirstIndex' }, { 'name': 'prop_type_field', 'value': 'FIELD' }, { 'name': 'prop_type_first_phantom', - 'value': 'Code::MAP_TRANSITION' }, + 'value': 'MAP_TRANSITION' }, { 'name': 'prop_type_mask', 'value': 'PropertyDetails::TypeField::kMask' }, @@ -114,7 +107,7 @@ extras_accessors = [ 'JSObject, elements, Object, kElementsOffset', 'FixedArray, data, uintptr_t, kHeaderSize', 'Map, instance_attributes, int, kInstanceAttributesOffset', - 'Map, transitions, uintptr_t, kTransitionsOrBackPointerOffset', + 'Map, instance_descriptors, int, kInstanceDescriptorsOrBitField3Offset', 'Map, inobject_properties, int, kInObjectPropertiesOffset', 'Map, instance_size, int, kInstanceSizeOffset', 'HeapNumber, value, double, kValueOffset', From dbe142c4eda0f15fad9fa85743dd11b81292fa8f Mon Sep 17 00:00:00 2001 From: Timothy J Fontaine Date: Thu, 23 May 2013 13:57:59 -0700 Subject: [PATCH 08/19] v8: fix GetLocalizedMessage usage As is the backport of the abort on uncaught exception wouldn't compile because we it was passing in `this` when it was unnecessary. --- deps/v8/src/isolate.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/v8/src/isolate.cc b/deps/v8/src/isolate.cc index 04a438bfa5..5a5293eb71 100644 --- a/deps/v8/src/isolate.cc +++ b/deps/v8/src/isolate.cc @@ -1161,7 +1161,7 @@ void Isolate::DoThrow(Object* exception, MessageLocation* location) { (report_exception || can_be_caught_externally)) { fatal_exception_depth++; fprintf(stderr, "%s\n\nFROM\n", - *MessageHandler::GetLocalizedMessage(this, message_obj)); + *MessageHandler::GetLocalizedMessage(message_obj)); PrintCurrentStackTrace(stderr); OS::Abort(); } From e2385839d7bde937334e34da082293cae877fe2f Mon Sep 17 00:00:00 2001 From: Timothy J Fontaine Date: Thu, 23 May 2013 13:40:40 -0700 Subject: [PATCH 09/19] v8: re-apply floating patches --- deps/v8/build/common.gypi | 11 +---------- deps/v8/src/json-parser.h | 7 +++++-- deps/v8/src/objects-inl.h | 3 ++- deps/v8/src/objects.h | 7 +++++-- deps/v8/src/parser.cc | 4 ++-- deps/v8/src/parser.h | 5 ----- deps/v8/src/platform-posix.cc | 17 ++++++++++++++++- deps/v8/src/stub-cache.cc | 8 ++++---- deps/v8/src/v8utils.h | 2 ++ deps/v8/tools/gen-postmortem-metadata.py | 15 +++++++++++---- 10 files changed, 48 insertions(+), 31 deletions(-) diff --git a/deps/v8/build/common.gypi b/deps/v8/build/common.gypi index 78888b8d7c..6e0ef0c997 100644 --- a/deps/v8/build/common.gypi +++ b/deps/v8/build/common.gypi @@ -157,7 +157,7 @@ [ 'v8_use_arm_eabi_hardfloat=="true"', { 'defines': [ 'USE_EABI_HARDFLOAT=1', - 'CAN_USE_VFP3_INSTRUCTIONS', + 'CAN_USE_VFP2_INSTRUCTIONS', ], 'target_conditions': [ ['_toolset=="target"', { @@ -378,15 +378,6 @@ 'conditions': [ ['OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="netbsd" \ or OS=="android"', { - 'cflags!': [ - '-O2', - '-Os', - ], - 'cflags': [ - '-fdata-sections', - '-ffunction-sections', - '-O3', - ], 'conditions': [ [ 'gcc_version==44 and clang==0', { 'cflags': [ diff --git a/deps/v8/src/json-parser.h b/deps/v8/src/json-parser.h index 03ed22d70e..ebe3db1382 100644 --- a/deps/v8/src/json-parser.h +++ b/deps/v8/src/json-parser.h @@ -287,6 +287,7 @@ Handle JsonParser::ParseJsonValue() { // Parse a JSON object. Position must be right at '{'. template Handle JsonParser::ParseJsonObject() { + HandleScope scope; Handle prototype; Handle json_object = factory()->NewJSObject(object_constructor()); @@ -360,12 +361,13 @@ Handle JsonParser::ParseJsonObject() { if (!prototype.is_null()) SetPrototype(json_object, prototype); } AdvanceSkipWhitespace(); - return json_object; + return scope.CloseAndEscape(json_object); } // Parse a JSON array. Position must be right at '['. template Handle JsonParser::ParseJsonArray() { + HandleScope scope; ZoneScope zone_scope(zone(), DELETE_ON_EXIT); ZoneList > elements(4, zone()); ASSERT_EQ(c0_, '['); @@ -388,7 +390,8 @@ Handle JsonParser::ParseJsonArray() { for (int i = 0, n = elements.length(); i < n; i++) { fast_elements->set(i, *elements[i]); } - return factory()->NewJSArrayWithElements(fast_elements); + Handle json_array = factory()->NewJSArrayWithElements(fast_elements); + return scope.CloseAndEscape(json_array); } diff --git a/deps/v8/src/objects-inl.h b/deps/v8/src/objects-inl.h index ea5a93f16b..4834fa63a4 100644 --- a/deps/v8/src/objects-inl.h +++ b/deps/v8/src/objects-inl.h @@ -3500,8 +3500,9 @@ Code::Flags Code::ComputeFlags(Kind kind, kind == CALL_IC || kind == STORE_IC || kind == KEYED_STORE_IC); + ASSERT(argc <= Code::kMaxArguments); // Compute the bit mask. - int bits = KindField::encode(kind) + unsigned int bits = KindField::encode(kind) | ICStateField::encode(ic_state) | TypeField::encode(type) | ExtraICStateField::encode(extra_ic_state) diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h index 755dd42d9e..47d775781b 100644 --- a/deps/v8/src/objects.h +++ b/deps/v8/src/objects.h @@ -4180,8 +4180,8 @@ class Code: public HeapObject { // FLAGS_MIN_VALUE and FLAGS_MAX_VALUE are specified to ensure that // enumeration type has correct value range (see Issue 830 for more details). enum Flags { - FLAGS_MIN_VALUE = kMinInt, - FLAGS_MAX_VALUE = kMaxInt + FLAGS_MIN_VALUE = 0, + FLAGS_MAX_VALUE = kMaxUInt32 }; #define CODE_KIND_LIST(V) \ @@ -4644,6 +4644,9 @@ class Code: public HeapObject { // Signed field cannot be encoded using the BitField class. static const int kArgumentsCountShift = 14; static const int kArgumentsCountMask = ~((1 << kArgumentsCountShift) - 1); + static const int kArgumentsBits = + PlatformSmiTagging::kSmiValueSize - Code::kArgumentsCountShift + 1; + static const int kMaxArguments = (1 << kArgumentsBits) - 1; // This constant should be encodable in an ARM instruction. static const int kFlagsNotUsedInLookup = diff --git a/deps/v8/src/parser.cc b/deps/v8/src/parser.cc index 03e4b039cc..6da414af93 100644 --- a/deps/v8/src/parser.cc +++ b/deps/v8/src/parser.cc @@ -4243,7 +4243,7 @@ ZoneList* Parser::ParseArguments(bool* ok) { while (!done) { Expression* argument = ParseAssignmentExpression(true, CHECK_OK); result->Add(argument, zone()); - if (result->length() > kMaxNumFunctionParameters) { + if (result->length() > Code::kMaxArguments) { ReportMessageAt(scanner().location(), "too_many_arguments", Vector::empty()); *ok = false; @@ -4420,7 +4420,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle function_name, top_scope_->DeclareParameter(param_name, VAR); num_parameters++; - if (num_parameters > kMaxNumFunctionParameters) { + if (num_parameters > Code::kMaxArguments) { ReportMessageAt(scanner().location(), "too_many_parameters", Vector::empty()); *ok = false; diff --git a/deps/v8/src/parser.h b/deps/v8/src/parser.h index 93fd1b8aa9..e36a9b3dca 100644 --- a/deps/v8/src/parser.h +++ b/deps/v8/src/parser.h @@ -449,11 +449,6 @@ class Parser { Vector > args); private: - // Limit on number of function parameters is chosen arbitrarily. - // Code::Flags uses only the low 17 bits of num-parameters to - // construct a hashable id, so if more than 2^17 are allowed, this - // should be checked. - static const int kMaxNumFunctionParameters = 32766; static const int kMaxNumFunctionLocals = 131071; // 2^17-1 enum Mode { diff --git a/deps/v8/src/platform-posix.cc b/deps/v8/src/platform-posix.cc index 3bc83733ca..ad74eba8d9 100644 --- a/deps/v8/src/platform-posix.cc +++ b/deps/v8/src/platform-posix.cc @@ -109,11 +109,26 @@ void* OS::GetRandomMmapAddr() { raw_addr &= V8_UINT64_C(0x3ffffffff000); #else uint32_t raw_addr = V8::RandomPrivate(isolate); + + raw_addr &= 0x3ffff000; + +# ifdef __sun + // For our Solaris/illumos mmap hint, we pick a random address in the bottom + // half of the top half of the address space (that is, the third quarter). + // Because we do not MAP_FIXED, this will be treated only as a hint -- the + // system will not fail to mmap() because something else happens to already + // be mapped at our random address. We deliberately set the hint high enough + // to get well above the system's break (that is, the heap); Solaris and + // illumos will try the hint and if that fails allocate as if there were + // no hint at all. The high hint prevents the break from getting hemmed in + // at low values, ceding half of the address space to the system heap. + raw_addr += 0x80000000; +# else // The range 0x20000000 - 0x60000000 is relatively unpopulated across a // variety of ASLR modes (PAE kernel, NX compat mode, etc) and on macos // 10.6 and 10.7. - raw_addr &= 0x3ffff000; raw_addr += 0x20000000; +# endif #endif return reinterpret_cast(raw_addr); } diff --git a/deps/v8/src/stub-cache.cc b/deps/v8/src/stub-cache.cc index 411914719c..8490c7e748 100644 --- a/deps/v8/src/stub-cache.cc +++ b/deps/v8/src/stub-cache.cc @@ -617,7 +617,7 @@ Handle StubCache::ComputeCallConstant(int argc, Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); - ASSERT_EQ(flags, code->flags()); + ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); @@ -655,7 +655,7 @@ Handle StubCache::ComputeCallField(int argc, Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); - ASSERT_EQ(flags, code->flags()); + ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); @@ -692,7 +692,7 @@ Handle StubCache::ComputeCallInterceptor(int argc, Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); - ASSERT_EQ(flags, code->flags()); + ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); @@ -721,7 +721,7 @@ Handle StubCache::ComputeCallGlobal(int argc, CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); - ASSERT_EQ(flags, code->flags()); + ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); diff --git a/deps/v8/src/v8utils.h b/deps/v8/src/v8utils.h index 9072b4e285..111abdf8b8 100644 --- a/deps/v8/src/v8utils.h +++ b/deps/v8/src/v8utils.h @@ -209,6 +209,8 @@ INLINE(void CopyChars(sinkchar* dest, const sourcechar* src, int chars)); template void CopyChars(sinkchar* dest, const sourcechar* src, int chars) { + ASSERT(chars >= 0); + if (chars == 0) return; sinkchar* limit = dest + chars; #ifdef V8_HOST_CAN_READ_UNALIGNED if (sizeof(*dest) == sizeof(*src)) { diff --git a/deps/v8/tools/gen-postmortem-metadata.py b/deps/v8/tools/gen-postmortem-metadata.py index f59cfd3033..7bee763bc9 100644 --- a/deps/v8/tools/gen-postmortem-metadata.py +++ b/deps/v8/tools/gen-postmortem-metadata.py @@ -76,16 +76,23 @@ consts_misc = [ { 'name': 'SmiTag', 'value': 'kSmiTag' }, { 'name': 'SmiTagMask', 'value': 'kSmiTagMask' }, { 'name': 'SmiValueShift', 'value': 'kSmiTagSize' }, + { 'name': 'SmiShiftSize', 'value': 'kSmiShiftSize' }, { 'name': 'PointerSizeLog2', 'value': 'kPointerSizeLog2' }, - { 'name': 'prop_idx_transitions', - 'value': 'DescriptorArray::kTransitionsIndex' }, + { 'name': 'prop_desc_key', + 'value': 'DescriptorArray::kDescriptorKey' }, + { 'name': 'prop_desc_details', + 'value': 'DescriptorArray::kDescriptorDetails' }, + { 'name': 'prop_desc_value', + 'value': 'DescriptorArray::kDescriptorValue' }, + { 'name': 'prop_desc_size', + 'value': 'DescriptorArray::kDescriptorSize' }, { 'name': 'prop_idx_first', 'value': 'DescriptorArray::kFirstIndex' }, { 'name': 'prop_type_field', 'value': 'FIELD' }, { 'name': 'prop_type_first_phantom', - 'value': 'MAP_TRANSITION' }, + 'value': 'Code::MAP_TRANSITION' }, { 'name': 'prop_type_mask', 'value': 'PropertyDetails::TypeField::kMask' }, @@ -107,7 +114,7 @@ extras_accessors = [ 'JSObject, elements, Object, kElementsOffset', 'FixedArray, data, uintptr_t, kHeaderSize', 'Map, instance_attributes, int, kInstanceAttributesOffset', - 'Map, instance_descriptors, int, kInstanceDescriptorsOrBitField3Offset', + 'Map, transitions, uintptr_t, kTransitionsOrBackPointerOffset', 'Map, inobject_properties, int, kInObjectPropertiesOffset', 'Map, instance_size, int, kInstanceSizeOffset', 'HeapNumber, value, double, kValueOffset', From a2f93cf77a8121db713bb003e26031bcca446f1f Mon Sep 17 00:00:00 2001 From: isaacs Date: Thu, 23 May 2013 15:12:13 -0700 Subject: [PATCH 10/19] http: Return true on empty writes, not false Otherwise, writing an empty string causes the whole program to grind to a halt when piping data into http messages. This wasn't as much of a problem (though it WAS a bug) in 0.8 and before, because our hyperactive 'drain' behavior would mean that some *previous* write() would probably have a pending drain event, and cause things to start moving again. --- lib/http.js | 4 +- test/simple/test-http-zero-length-write.js | 93 ++++++++++++++++++++++ 2 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 test/simple/test-http-zero-length-write.js diff --git a/lib/http.js b/lib/http.js index bcde516dae..ab36cd7491 100644 --- a/lib/http.js +++ b/lib/http.js @@ -780,7 +780,9 @@ OutgoingMessage.prototype.write = function(chunk, encoding) { throw new TypeError('first argument must be a string or Buffer'); } - if (chunk.length === 0) return false; + // If we get an empty string or buffer, then just do nothing, and + // signal the user to keep writing. + if (chunk.length === 0) return true; // TODO(bnoordhuis) Temporary optimization hack, remove in v0.11. We only // want to convert the buffer when we're sending: diff --git a/test/simple/test-http-zero-length-write.js b/test/simple/test-http-zero-length-write.js new file mode 100644 index 0000000000..e68f947c32 --- /dev/null +++ b/test/simple/test-http-zero-length-write.js @@ -0,0 +1,93 @@ +// 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 common = require('../common'); +var assert = require('assert'); + +var http = require('http'); + +var Stream = require('stream'); + +function getSrc() { + // An old-style readable stream. + // The Readable class prevents this behavior. + var src = new Stream(); + + // start out paused, just so we don't miss anything yet. + var paused = false; + src.pause = function() { + paused = true; + }; + src.resume = function() { + paused = false; + }; + + var chunks = [ '', 'asdf', '', 'foo', '', 'bar', '' ]; + var interval = setInterval(function() { + if (paused) + return + + var chunk = chunks.shift(); + if (chunk !== undefined) { + src.emit('data', chunk); + } else { + src.emit('end'); + clearInterval(interval); + } + }, 1); + + return src; +} + + +var expect = 'asdffoobar'; + +var server = http.createServer(function(req, res) { + var actual = ''; + req.setEncoding('utf8'); + req.on('data', function(c) { + actual += c; + }); + req.on('end', function() { + assert.equal(actual, expect); + getSrc().pipe(res); + }); + server.close(); +}); + +server.listen(common.PORT, function() { + var req = http.request({ port: common.PORT, method: 'POST' }); + var actual = ''; + req.on('response', function(res) { + res.setEncoding('utf8'); + res.on('data', function(c) { + actual += c; + }); + res.on('end', function() { + assert.equal(actual, expect); + }); + }); + getSrc().pipe(req); +}); + +process.on('exit', function(c) { + if (!c) console.log('ok'); +}); From 007e63bb13b5d3af54081972ab88217571828906 Mon Sep 17 00:00:00 2001 From: Timothy J Fontaine Date: Thu, 23 May 2013 16:23:07 -0700 Subject: [PATCH 11/19] buffer: special case empty string writes Prior to 119354f we specifically handled passing a zero length string to write on a buffer, restore that functionality. --- src/node_buffer.cc | 10 +++++++++- test/simple/test-buffer.js | 7 +++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 2dfddc7644..8153c8319a 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -345,7 +345,15 @@ Handle Buffer::StringWrite(const Arguments& args) { Local str = args[0].As(); - if (encoding == HEX && str->Length() % 2 != 0) + int length = str->Length(); + + if (length == 0) { + constructor_template->GetFunction()->Set(chars_written_sym, + Integer::New(0)); + return scope.Close(Integer::New(0)); + } + + if (encoding == HEX && length % 2 != 0) return ThrowTypeError("Invalid hex string"); size_t offset = args[1]->Int32Value(); diff --git a/test/simple/test-buffer.js b/test/simple/test-buffer.js index 0d071127c9..3026824f0a 100644 --- a/test/simple/test-buffer.js +++ b/test/simple/test-buffer.js @@ -998,3 +998,10 @@ assert.equal(Buffer.byteLength('aaaa==', 'base64'), 3); assert.throws(function() { Buffer('', 'buffer'); }, TypeError); + +assert.doesNotThrow(function () { + var slow = new SlowBuffer(1); + assert(slow.write('', Buffer.poolSize * 10) === 0); + var fast = new Buffer(1); + assert(fast.write('', Buffer.poolSize * 10) === 0); +}); From a40133d10cdb911b27fe8d46d67a835b0103bbf1 Mon Sep 17 00:00:00 2001 From: Nathan Zadoks Date: Fri, 24 May 2013 19:34:38 +0200 Subject: [PATCH 12/19] http: remove bodyHead from 'upgrade' events Streams2 makes this unnecessary. An empty buffer is provided for compatibility. --- doc/api/http.markdown | 20 ++++++++------------ lib/http.js | 10 ++++++++-- test/simple/test-http-upgrade-agent.js | 4 +++- test/simple/test-http-upgrade-client.js | 4 +++- test/simple/test-http-upgrade-client2.js | 5 +++++ test/simple/test-http-upgrade-server.js | 2 +- 6 files changed, 28 insertions(+), 17 deletions(-) diff --git a/doc/api/http.markdown b/doc/api/http.markdown index e93aa2d2dd..7204690e34 100644 --- a/doc/api/http.markdown +++ b/doc/api/http.markdown @@ -93,7 +93,7 @@ not be emitted. ### Event: 'connect' -`function (request, socket, head) { }` +`function (request, socket) { }` Emitted each time a client requests a http CONNECT method. If this event isn't listened for, then clients requesting a CONNECT method will have their @@ -102,8 +102,6 @@ connections closed. * `request` is the arguments for the http request, as it is in the request event. * `socket` is the network socket between the server and client. -* `head` is an instance of Buffer, the first packet of the tunneling stream, - this may be empty. After this event is emitted, the request's socket will not have a `data` event listener, meaning you will need to bind to it in order to handle data @@ -111,7 +109,7 @@ sent to the server on that socket. ### Event: 'upgrade' -`function (request, socket, head) { }` +`function (request, socket) { }` Emitted each time a client requests a http upgrade. If this event isn't listened for, then clients requesting an upgrade will have their connections @@ -120,7 +118,6 @@ closed. * `request` is the arguments for the http request, as it is in the request event. * `socket` is the network socket between the server and client. -* `head` is an instance of Buffer, the first packet of the upgraded stream, this may be empty. After this event is emitted, the request's socket will not have a `data` @@ -595,7 +592,7 @@ Emitted after a socket is assigned to this request. ### Event: 'connect' -`function (response, socket, head) { }` +`function (response, socket) { }` Emitted each time a server responds to a request with a CONNECT method. If this event isn't being listened for, clients receiving a CONNECT method will have @@ -612,14 +609,13 @@ A client server pair that show you how to listen for the `connect` event. res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('okay'); }); - proxy.on('connect', function(req, cltSocket, head) { + proxy.on('connect', function(req, cltSocket) { // connect to an origin server var srvUrl = url.parse('http://' + req.url); var srvSocket = net.connect(srvUrl.port, srvUrl.hostname, function() { cltSocket.write('HTTP/1.1 200 Connection Established\r\n' + 'Proxy-agent: Node-Proxy\r\n' + '\r\n'); - srvSocket.write(head); srvSocket.pipe(cltSocket); cltSocket.pipe(srvSocket); }); @@ -639,7 +635,7 @@ A client server pair that show you how to listen for the `connect` event. var req = http.request(options); req.end(); - req.on('connect', function(res, socket, head) { + req.on('connect', function(res, socket) { console.log('got connected!'); // make a request over an HTTP tunnel @@ -658,7 +654,7 @@ A client server pair that show you how to listen for the `connect` event. ### Event: 'upgrade' -`function (response, socket, head) { }` +`function (response, socket) { }` Emitted each time a server responds to a request with an upgrade. If this event isn't being listened for, clients receiving an upgrade header will have @@ -673,7 +669,7 @@ A client server pair that show you how to listen for the `upgrade` event. res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('okay'); }); - srv.on('upgrade', function(req, socket, head) { + srv.on('upgrade', function(req, socket) { socket.write('HTTP/1.1 101 Web Socket Protocol Handshake\r\n' + 'Upgrade: WebSocket\r\n' + 'Connection: Upgrade\r\n' + @@ -698,7 +694,7 @@ A client server pair that show you how to listen for the `upgrade` event. var req = http.request(options); req.end(); - req.on('upgrade', function(res, socket, upgradeHead) { + req.on('upgrade', function(res, socket) { console.log('got upgraded!'); socket.end(); process.exit(0); diff --git a/lib/http.js b/lib/http.js index ab36cd7491..3c3a56c50b 100644 --- a/lib/http.js +++ b/lib/http.js @@ -28,6 +28,9 @@ var FreeList = require('freelist').FreeList; var HTTPParser = process.binding('http_parser').HTTPParser; var assert = require('assert').ok; +// an empty buffer for UPGRADE/CONNECT bodyHead compatibility +var emptyBuffer = new Buffer(0); + var debug; if (process.env.NODE_DEBUG && /http/.test(process.env.NODE_DEBUG)) { debug = function(x) { console.error('HTTP: %s', x); }; @@ -1579,7 +1582,9 @@ function socketOnData(d, start, end) { socket.removeListener('close', socketCloseListener); socket.removeListener('error', socketErrorListener); - req.emit(eventName, res, socket, bodyHead); + socket.unshift(bodyHead); + + req.emit(eventName, res, socket, emptyBuffer); req.emit('close'); } else { // Got Upgrade header or CONNECT method, but have no handler. @@ -1952,7 +1957,8 @@ function connectionListener(socket) { var eventName = req.method === 'CONNECT' ? 'connect' : 'upgrade'; if (EventEmitter.listenerCount(self, eventName) > 0) { - self.emit(eventName, req, req.socket, bodyHead); + socket.unshift(bodyHead); + self.emit(eventName, req, req.socket, emptyBuffer); } else { // Got upgrade header or CONNECT method, but have no handler. socket.destroy(); diff --git a/test/simple/test-http-upgrade-agent.js b/test/simple/test-http-upgrade-agent.js index 1077a983dc..ff8e540556 100644 --- a/test/simple/test-http-upgrade-agent.js +++ b/test/simple/test-http-upgrade-agent.js @@ -67,7 +67,7 @@ srv.listen(common.PORT, '127.0.0.1', function() { req.on('upgrade', function(res, socket, upgradeHead) { // XXX: This test isn't fantastic, as it assumes that the entire response // from the server will arrive in a single data callback - assert.equal(upgradeHead, 'nurtzo'); + assert.equal(upgradeHead, ''); console.log(res.headers); var expectedHeaders = { 'hello': 'world', @@ -78,6 +78,8 @@ srv.listen(common.PORT, '127.0.0.1', function() { // Make sure this request got removed from the pool. assert(!http.globalAgent.sockets.hasOwnProperty(name)); + assert.equal(socket.read(), 'nurtzo'); + req.on('close', function() { socket.end(); srv.close(); diff --git a/test/simple/test-http-upgrade-client.js b/test/simple/test-http-upgrade-client.js index 3bf5beccf5..9361e0b503 100644 --- a/test/simple/test-http-upgrade-client.js +++ b/test/simple/test-http-upgrade-client.js @@ -56,7 +56,7 @@ srv.listen(common.PORT, '127.0.0.1', function() { req.on('upgrade', function(res, socket, upgradeHead) { // XXX: This test isn't fantastic, as it assumes that the entire response // from the server will arrive in a single data callback - assert.equal(upgradeHead, 'nurtzo'); + assert.equal(upgradeHead, ''); console.log(res.headers); var expectedHeaders = {'hello': 'world', @@ -64,6 +64,8 @@ srv.listen(common.PORT, '127.0.0.1', function() { 'upgrade': 'websocket' }; assert.deepEqual(expectedHeaders, res.headers); + assert.equal(socket.read(), 'nurtzo'); + socket.end(); srv.close(); diff --git a/test/simple/test-http-upgrade-client2.js b/test/simple/test-http-upgrade-client2.js index fa39f2a572..53f15d2939 100644 --- a/test/simple/test-http-upgrade-client2.js +++ b/test/simple/test-http-upgrade-client2.js @@ -30,6 +30,9 @@ server.on('upgrade', function(req, socket, head) { socket.write('HTTP/1.1 101 Ok' + CRLF + 'Connection: Upgrade' + CRLF + 'Upgrade: Test' + CRLF + CRLF + 'head'); + socket.on('readable', function() { + socket.read(); + }); socket.on('end', function() { socket.end(); }); @@ -50,6 +53,7 @@ server.listen(common.PORT, function() { wasUpgrade = true; request.removeListener('upgrade', onUpgrade); + socket.unref(); socket.end(); } request.on('upgrade', onUpgrade); @@ -75,6 +79,7 @@ server.listen(common.PORT, function() { successCount++; // Test pass console.log('Pass!'); + server.unref(); server.close(); }); }); diff --git a/test/simple/test-http-upgrade-server.js b/test/simple/test-http-upgrade-server.js index 6a14d39aa5..88b83ca85e 100644 --- a/test/simple/test-http-upgrade-server.js +++ b/test/simple/test-http-upgrade-server.js @@ -55,7 +55,7 @@ function testServer() { 'Connection: Upgrade\r\n' + '\r\n\r\n'); - request_upgradeHead = upgradeHead; + request_upgradeHead = socket.read(); socket.ondata = function(d, start, end) { var data = d.toString('utf8', start, end); From 1314c4aeeb96f59b010ca8fef2e8e86bbc1f398c Mon Sep 17 00:00:00 2001 From: isaacs Date: Fri, 24 May 2013 14:41:00 -0700 Subject: [PATCH 13/19] uv: upgrade to 0.10.8 --- deps/uv/ChangeLog | 25 ++++++++++++++++++++- deps/uv/src/unix/darwin.c | 46 ++++++++++++++++++++++----------------- deps/uv/src/unix/error.c | 1 + deps/uv/src/unix/signal.c | 2 +- deps/uv/src/unix/stream.c | 43 ++++++++++++++++++++++++++++++++---- deps/uv/src/version.c | 2 +- deps/uv/src/win/process.c | 45 +++++++++++++++++++++++++++++++++++--- deps/uv/src/win/stream.c | 4 ++-- 8 files changed, 136 insertions(+), 32 deletions(-) diff --git a/deps/uv/ChangeLog b/deps/uv/ChangeLog index 3eed9f1652..356a5b10b1 100644 --- a/deps/uv/ChangeLog +++ b/deps/uv/ChangeLog @@ -1,4 +1,27 @@ -2013.05.15, Version 0.10.7 (Stable) +2013.05.25, Version 0.10.8 (Stable) + +Changes since version 0.10.7: + +* windows: make uv_spawn not fail under job control (Bert Belder) + +* darwin: assume CFRunLoopStop() isn't thread-safe (Fedor Indutny) + +* win: fix UV_EALREADY incorrectly set (Bert Belder) + +* darwin: make two uv__cf_*() functions static (Ben Noordhuis) + +* darwin: task_info() cannot fail (Ben Noordhuis) + +* unix: add mapping for ENETDOWN (Ben Noordhuis) + +* unix: implicitly signal write errors to libuv user (Ben Noordhuis) + +* unix: fix assert on signal pipe overflow (Bert Belder) + +* unix: turn off POLLOUT after stream connect (Ben Noordhuis) + + +2013.05.15, Version 0.10.7 (Stable), 028baaf0846b686a81e992cb2f2f5a9b8e841fcf Changes since version 0.10.6: diff --git a/deps/uv/src/unix/darwin.c b/deps/uv/src/unix/darwin.c index cfbfed23ff..77e662f4e1 100644 --- a/deps/uv/src/unix/darwin.c +++ b/deps/uv/src/unix/darwin.c @@ -38,8 +38,8 @@ #include /* sysconf */ /* Forward declarations */ -void uv__cf_loop_runner(void* arg); -void uv__cf_loop_cb(void* arg); +static void uv__cf_loop_runner(void* arg); +static void uv__cf_loop_cb(void* arg); typedef struct uv__cf_loop_signal_s uv__cf_loop_signal_t; struct uv__cf_loop_signal_s { @@ -84,9 +84,8 @@ void uv__platform_loop_delete(uv_loop_t* loop) { uv__cf_loop_signal_t* s; assert(loop->cf_loop != NULL); - CFRunLoopStop(loop->cf_loop); + uv__cf_loop_signal(loop, NULL, NULL); uv_thread_join(&loop->cf_thread); - loop->cf_loop = NULL; uv_sem_destroy(&loop->cf_sem); uv_mutex_destroy(&loop->cf_mutex); @@ -103,7 +102,7 @@ void uv__platform_loop_delete(uv_loop_t* loop) { } -void uv__cf_loop_runner(void* arg) { +static void uv__cf_loop_runner(void* arg) { uv_loop_t* loop; loop = arg; @@ -125,7 +124,7 @@ void uv__cf_loop_runner(void* arg) { } -void uv__cf_loop_cb(void* arg) { +static void uv__cf_loop_cb(void* arg) { uv_loop_t* loop; ngx_queue_t* item; ngx_queue_t split_head; @@ -145,7 +144,12 @@ void uv__cf_loop_cb(void* arg) { item = ngx_queue_head(&split_head); s = ngx_queue_data(item, uv__cf_loop_signal_t, member); - s->cb(s->arg); + + /* This was a termination signal */ + if (s->cb == NULL) + CFRunLoopStop(loop->cf_loop); + else + s->cb(s->arg); ngx_queue_remove(item); free(s); @@ -253,19 +257,21 @@ void uv_loadavg(double avg[3]) { uv_err_t uv_resident_set_memory(size_t* rss) { - struct task_basic_info t_info; - mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; - - int r = task_info(mach_task_self(), - TASK_BASIC_INFO, - (task_info_t)&t_info, - &t_info_count); - - if (r != KERN_SUCCESS) { - return uv__new_sys_error(errno); - } - - *rss = t_info.resident_size; + mach_msg_type_number_t count; + task_basic_info_data_t info; + kern_return_t err; + + count = TASK_BASIC_INFO_COUNT; + err = task_info(mach_task_self(), + TASK_BASIC_INFO, + (task_info_t) &info, + &count); + (void) &err; + /* task_info(TASK_BASIC_INFO) cannot really fail. Anything other than + * KERN_SUCCESS implies a libuv bug. + */ + assert(err == KERN_SUCCESS); + *rss = info.resident_size; return uv_ok_; } diff --git a/deps/uv/src/unix/error.c b/deps/uv/src/unix/error.c index 9e3e84ad9a..05ab482025 100644 --- a/deps/uv/src/unix/error.c +++ b/deps/uv/src/unix/error.c @@ -79,6 +79,7 @@ uv_err_code uv_translate_sys_error(int sys_errno) { case EMSGSIZE: return UV_EMSGSIZE; case ENAMETOOLONG: return UV_ENAMETOOLONG; case EINVAL: return UV_EINVAL; + case ENETDOWN: return UV_ENETDOWN; case ENETUNREACH: return UV_ENETUNREACH; case ECONNABORTED: return UV_ECONNABORTED; case ELOOP: return UV_ELOOP; diff --git a/deps/uv/src/unix/signal.c b/deps/uv/src/unix/signal.c index f7fd2e5e4f..22c7783b9f 100644 --- a/deps/uv/src/unix/signal.c +++ b/deps/uv/src/unix/signal.c @@ -160,7 +160,7 @@ static void uv__signal_handler(int signum) { } while (r == -1 && errno == EINTR); assert(r == sizeof msg || - (r == -1 && errno != EAGAIN && errno != EWOULDBLOCK)); + (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))); if (r != -1) handle->caught_signals++; diff --git a/deps/uv/src/unix/stream.c b/deps/uv/src/unix/stream.c index fda2f02f57..aeefa2c419 100644 --- a/deps/uv/src/unix/stream.c +++ b/deps/uv/src/unix/stream.c @@ -60,6 +60,7 @@ static void uv__stream_connect(uv_stream_t*); static void uv__write(uv_stream_t* stream); static void uv__read(uv_stream_t* stream); static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events); +static size_t uv__write_req_size(uv_write_t* req); /* Used by the accept() EMFILE party trick. */ @@ -399,6 +400,7 @@ void uv__stream_destroy(uv_stream_t* stream) { if (req->bufs != req->bufsml) free(req->bufs); + req->bufs = NULL; if (req->cb) { uv__set_artificial_error(req->handle->loop, UV_ECANCELED); @@ -413,6 +415,13 @@ void uv__stream_destroy(uv_stream_t* stream) { req = ngx_queue_data(q, uv_write_t, queue); uv__req_unregister(stream->loop, req); + if (req->bufs != NULL) { + stream->write_queue_size -= uv__write_req_size(req); + if (req->bufs != req->bufsml) + free(req->bufs); + req->bufs = NULL; + } + if (req->cb) { uv__set_sys_error(stream->loop, req->error); req->cb(req, req->error ? -1 : 0); @@ -652,6 +661,7 @@ static void uv__drain(uv_stream_t* stream) { static size_t uv__write_req_size(uv_write_t* req) { size_t size; + assert(req->bufs != NULL); size = uv__buf_count(req->bufs + req->write_index, req->bufcnt - req->write_index); assert(req->handle->write_queue_size >= size); @@ -665,10 +675,18 @@ static void uv__write_req_finish(uv_write_t* req) { /* Pop the req off tcp->write_queue. */ ngx_queue_remove(&req->queue); - if (req->bufs != req->bufsml) { - free(req->bufs); + + /* Only free when there was no error. On error, we touch up write_queue_size + * right before making the callback. The reason we don't do that right away + * is that a write_queue_size > 0 is our only way to signal to the user that + * he should stop writing - which he should if we got an error. Something to + * revisit in future revisions of the libuv API. + */ + if (req->error == 0) { + if (req->bufs != req->bufsml) + free(req->bufs); + req->bufs = NULL; } - req->bufs = NULL; /* Add it to the write_completed_queue where it will have its * callback called in the near future. @@ -778,7 +796,6 @@ start: if (errno != EAGAIN && errno != EWOULDBLOCK) { /* Error */ req->error = errno; - stream->write_queue_size -= uv__write_req_size(req); uv__write_req_finish(req); return; } else if (stream->flags & UV_STREAM_BLOCKING) { @@ -855,6 +872,13 @@ static void uv__write_callbacks(uv_stream_t* stream) { ngx_queue_remove(q); uv__req_unregister(stream->loop, req); + if (req->bufs != NULL) { + stream->write_queue_size -= uv__write_req_size(req); + if (req->bufs != req->bufsml) + free(req->bufs); + req->bufs = NULL; + } + /* NOTE: call callback AFTER freeing the request data. */ if (req->cb) { uv__set_sys_error(stream->loop, req->error); @@ -1136,6 +1160,7 @@ static void uv__stream_connect(uv_stream_t* stream) { stream->connect_req = NULL; uv__req_unregister(stream->loop, req); + uv__io_stop(stream->loop, &stream->io_watcher, UV__POLLOUT); if (req->cb) { uv__set_sys_error(stream->loop, error); @@ -1283,6 +1308,16 @@ int uv_read2_start(uv_stream_t* stream, uv_alloc_cb alloc_cb, int uv_read_stop(uv_stream_t* stream) { + /* Sanity check. We're going to stop the handle unless it's primed for + * writing but that means there should be some kind of write action in + * progress. + */ + assert(!uv__io_active(&stream->io_watcher, UV__POLLOUT) || + !ngx_queue_empty(&stream->write_completed_queue) || + !ngx_queue_empty(&stream->write_queue) || + stream->shutdown_req != NULL || + stream->connect_req != NULL); + uv__io_stop(stream->loop, &stream->io_watcher, UV__POLLIN); uv__handle_stop(stream); stream->flags &= ~UV_STREAM_READING; diff --git a/deps/uv/src/version.c b/deps/uv/src/version.c index cb77c176fb..98765533db 100644 --- a/deps/uv/src/version.c +++ b/deps/uv/src/version.c @@ -34,7 +34,7 @@ #define UV_VERSION_MAJOR 0 #define UV_VERSION_MINOR 10 -#define UV_VERSION_PATCH 7 +#define UV_VERSION_PATCH 8 #define UV_VERSION_IS_RELEASE 1 diff --git a/deps/uv/src/win/process.c b/deps/uv/src/win/process.c index 8ef420e67b..f98767a428 100644 --- a/deps/uv/src/win/process.c +++ b/deps/uv/src/win/process.c @@ -49,7 +49,22 @@ static HANDLE uv_global_job_handle_; static uv_once_t uv_global_job_handle_init_guard_ = UV_ONCE_INIT; -static void uv__init_global_job_handle() { +static void uv__init_global_job_handle(void) { + /* Create a job object and set it up to kill all contained processes when + * it's closed. Since this handle is made non-inheritable and we're not + * giving it to anyone, we're the only process holding a reference to it. + * That means that if this process exits it is closed and all the processes + * it contains are killed. All processes created with uv_spawn that are not + * spawned with the UV_PROCESS_DETACHED flag are assigned to this job. + * + * We're setting the JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK flag so only the + * processes that we explicitly add are affected, and *their* subprocesses + * are not. This ensures that our child processes are not limited in their + * ability to use job control on Windows versions that don't deal with + * nested jobs (prior to Windows 8 / Server 2012). It also lets our child + * processes created detached processes without explicitly breaking away + * from job control (which uv_spawn doesn't, either). + */ SECURITY_ATTRIBUTES attr; JOBOBJECT_EXTENDED_LIMIT_INFORMATION info; @@ -920,7 +935,18 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process, } process_flags = CREATE_UNICODE_ENVIRONMENT; + if (options.flags & UV_PROCESS_DETACHED) { + /* Note that we're not setting the CREATE_BREAKAWAY_FROM_JOB flag. That + * means that libuv might not let you create a fully deamonized process + * when run under job control. However the type of job control that libuv + * itself creates doesn't trickle down to subprocesses so they can still + * daemonize. + * + * A reason to not do this is that CREATE_BREAKAWAY_FROM_JOB makes the + * CreateProcess call fail if we're under job control that doesn't allow + * breakaway. + */ process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP; } @@ -943,8 +969,21 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process, if (!(options.flags & UV_PROCESS_DETACHED)) { uv_once(&uv_global_job_handle_init_guard_, uv__init_global_job_handle); - if (!AssignProcessToJobObject(uv_global_job_handle_, info.hProcess)) - uv_fatal_error(GetLastError(), "AssignProcessToJobObject"); + if (!AssignProcessToJobObject(uv_global_job_handle_, info.hProcess)) { + /* AssignProcessToJobObject might fail if this process is under job + * control and the job doesn't have the + * JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK flag set, on a Windows version + * that doesn't support nested jobs. + * + * When that happens we just swallow the error and continue without + * establishing a kill-child-on-parent-exit relationship, otherwise + * there would be no way for libuv applications run under job control + * to spawn processes at all. + */ + DWORD err = GetLastError(); + if (err != ERROR_ACCESS_DENIED) + uv_fatal_error(err, "AssignProcessToJobObject"); + } } /* Set IPC pid to all IPC pipes. */ diff --git a/deps/uv/src/win/stream.c b/deps/uv/src/win/stream.c index 097f349794..edc5407cf5 100644 --- a/deps/uv/src/win/stream.c +++ b/deps/uv/src/win/stream.c @@ -56,7 +56,7 @@ int uv_accept(uv_stream_t* server, uv_stream_t* client) { int uv_read_start(uv_stream_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb) { if (handle->flags & UV_HANDLE_READING) { - uv__set_sys_error(handle->loop, UV_EALREADY); + uv__set_artificial_error(handle->loop, UV_EALREADY); return -1; } @@ -82,7 +82,7 @@ int uv_read_start(uv_stream_t* handle, uv_alloc_cb alloc_cb, int uv_read2_start(uv_stream_t* handle, uv_alloc_cb alloc_cb, uv_read2_cb read_cb) { if (handle->flags & UV_HANDLE_READING) { - uv__set_sys_error(handle->loop, UV_EALREADY); + uv__set_artificial_error(handle->loop, UV_EALREADY); return -1; } From 074e823a81f2b1a5c954b0d6dfc5e10a4bed96ab Mon Sep 17 00:00:00 2001 From: isaacs Date: Fri, 24 May 2013 14:41:43 -0700 Subject: [PATCH 14/19] npm: Upgrade to 1.2.23 --- deps/npm/html/api/bin.html | 2 +- deps/npm/html/api/bugs.html | 2 +- deps/npm/html/api/commands.html | 2 +- deps/npm/html/api/config.html | 2 +- deps/npm/html/api/deprecate.html | 2 +- deps/npm/html/api/docs.html | 2 +- deps/npm/html/api/edit.html | 2 +- deps/npm/html/api/explore.html | 2 +- deps/npm/html/api/help-search.html | 2 +- deps/npm/html/api/init.html | 2 +- deps/npm/html/api/install.html | 2 +- deps/npm/html/api/link.html | 2 +- deps/npm/html/api/load.html | 2 +- deps/npm/html/api/ls.html | 2 +- deps/npm/html/api/npm.html | 4 ++-- deps/npm/html/api/outdated.html | 2 +- deps/npm/html/api/owner.html | 2 +- deps/npm/html/api/pack.html | 2 +- deps/npm/html/api/prefix.html | 2 +- deps/npm/html/api/prune.html | 2 +- deps/npm/html/api/publish.html | 2 +- deps/npm/html/api/rebuild.html | 2 +- deps/npm/html/api/restart.html | 2 +- deps/npm/html/api/root.html | 2 +- deps/npm/html/api/run-script.html | 2 +- deps/npm/html/api/search.html | 2 +- deps/npm/html/api/shrinkwrap.html | 2 +- deps/npm/html/api/start.html | 2 +- deps/npm/html/api/stop.html | 2 +- deps/npm/html/api/submodule.html | 2 +- deps/npm/html/api/tag.html | 2 +- deps/npm/html/api/test.html | 2 +- deps/npm/html/api/uninstall.html | 2 +- deps/npm/html/api/unpublish.html | 2 +- deps/npm/html/api/update.html | 2 +- deps/npm/html/api/version.html | 2 +- deps/npm/html/api/view.html | 2 +- deps/npm/html/api/whoami.html | 2 +- deps/npm/html/doc/README.html | 2 +- deps/npm/html/doc/adduser.html | 2 +- deps/npm/html/doc/bin.html | 2 +- deps/npm/html/doc/bugs.html | 2 +- deps/npm/html/doc/build.html | 2 +- deps/npm/html/doc/bundle.html | 2 +- deps/npm/html/doc/cache.html | 2 +- deps/npm/html/doc/changelog.html | 2 +- deps/npm/html/doc/coding-style.html | 2 +- deps/npm/html/doc/completion.html | 2 +- deps/npm/html/doc/config.html | 2 +- deps/npm/html/doc/dedupe.html | 2 +- deps/npm/html/doc/deprecate.html | 2 +- deps/npm/html/doc/developers.html | 2 +- deps/npm/html/doc/disputes.html | 2 +- deps/npm/html/doc/docs.html | 2 +- deps/npm/html/doc/edit.html | 2 +- deps/npm/html/doc/explore.html | 2 +- deps/npm/html/doc/faq.html | 2 +- deps/npm/html/doc/folders.html | 2 +- deps/npm/html/doc/global.html | 2 +- deps/npm/html/doc/help-search.html | 2 +- deps/npm/html/doc/help.html | 2 +- deps/npm/html/doc/index.html | 2 +- deps/npm/html/doc/init.html | 2 +- deps/npm/html/doc/install.html | 2 +- deps/npm/html/doc/json.html | 2 +- deps/npm/html/doc/link.html | 2 +- deps/npm/html/doc/ls.html | 4 ++-- deps/npm/html/doc/npm.html | 4 ++-- deps/npm/html/doc/outdated.html | 2 +- deps/npm/html/doc/owner.html | 2 +- deps/npm/html/doc/pack.html | 2 +- deps/npm/html/doc/prefix.html | 2 +- deps/npm/html/doc/prune.html | 2 +- deps/npm/html/doc/publish.html | 2 +- deps/npm/html/doc/rebuild.html | 2 +- deps/npm/html/doc/registry.html | 2 +- deps/npm/html/doc/removing-npm.html | 2 +- deps/npm/html/doc/restart.html | 2 +- deps/npm/html/doc/rm.html | 2 +- deps/npm/html/doc/root.html | 2 +- deps/npm/html/doc/run-script.html | 2 +- deps/npm/html/doc/scripts.html | 2 +- deps/npm/html/doc/search.html | 2 +- deps/npm/html/doc/semver.html | 2 +- deps/npm/html/doc/shrinkwrap.html | 2 +- deps/npm/html/doc/star.html | 2 +- deps/npm/html/doc/stars.html | 2 +- deps/npm/html/doc/start.html | 2 +- deps/npm/html/doc/stop.html | 2 +- deps/npm/html/doc/submodule.html | 2 +- deps/npm/html/doc/tag.html | 2 +- deps/npm/html/doc/test.html | 2 +- deps/npm/html/doc/uninstall.html | 2 +- deps/npm/html/doc/unpublish.html | 2 +- deps/npm/html/doc/update.html | 2 +- deps/npm/html/doc/version.html | 2 +- deps/npm/html/doc/view.html | 2 +- deps/npm/html/doc/whoami.html | 2 +- deps/npm/lib/build.js | 2 ++ deps/npm/lib/install.js | 2 +- deps/npm/man/man1/ls.1 | 2 +- deps/npm/man/man1/npm.1 | 2 +- deps/npm/man/man3/npm.3 | 2 +- .../node_modules/npm-registry-client/lib/request.js | 5 ++++- deps/npm/node_modules/npm-registry-client/package.json | 10 +++------- deps/npm/package.json | 2 +- 106 files changed, 115 insertions(+), 114 deletions(-) diff --git a/deps/npm/html/api/bin.html b/deps/npm/html/api/bin.html index 88cbedc9ee..224f570a5d 100644 --- a/deps/npm/html/api/bin.html +++ b/deps/npm/html/api/bin.html @@ -19,7 +19,7 @@

This function should not be used programmatically. Instead, just refer to the npm.bin member.

- +