diff --git a/lib/module.js b/lib/module.js index 564f6c49d6c..02c24c27425 100644 --- a/lib/module.js +++ b/lib/module.js @@ -499,8 +499,6 @@ Module._extensions['.node'] = process.dlopen; Module.runMain = function() { // Load the main module--the command line argument. Module._load(process.argv[1], null, true); - // Handle any nextTicks added in the first tick of the program - process._tickCallback(); }; Module._initPaths = function() { diff --git a/src/node.cc b/src/node.cc index 76d6503366d..f342238ccd9 100644 --- a/src/node.cc +++ b/src/node.cc @@ -990,6 +990,41 @@ void SetupNextTick(const FunctionCallbackInfo& args) { } +Handle ExecuteNextTickCallback(Environment* env) { + Environment::TickInfo* tick_info = env->tick_info(); + + TryCatch try_catch; + try_catch.SetVerbose(true); + + if (tick_info->last_threw() == 1) { + tick_info->set_last_threw(0); + return True(env->isolate()); + } + + if (tick_info->in_tick()) { + return True(env->isolate()); + } + + if (tick_info->length() == 0) { + tick_info->set_index(0); + return True(env->isolate()); + } + + tick_info->set_in_tick(true); + + env->tick_callback_function()->Call(env->process_object(), 0, NULL); + + tick_info->set_in_tick(false); + + if (try_catch.HasCaught()) { + tick_info->set_last_threw(true); + return False(env->isolate()); + } + + return True(env->isolate()); +} + + Handle MakeDomainCallback(Environment* env, Handle recv, const Handle callback, @@ -1063,30 +1098,7 @@ Handle MakeDomainCallback(Environment* env, return Undefined(env->isolate()); } - Environment::TickInfo* tick_info = env->tick_info(); - - if (tick_info->last_threw() == 1) { - tick_info->set_last_threw(0); - return ret; - } - - if (tick_info->in_tick()) { - return ret; - } - - if (tick_info->length() == 0) { - tick_info->set_index(0); - return ret; - } - - tick_info->set_in_tick(true); - - env->tick_callback_function()->Call(process, 0, NULL); - - tick_info->set_in_tick(false); - - if (try_catch.HasCaught()) { - tick_info->set_last_threw(true); + if (ExecuteNextTickCallback(env)->IsFalse()) { return Undefined(env->isolate()); } @@ -1132,26 +1144,7 @@ Handle MakeCallback(Environment* env, return Undefined(env->isolate()); } - Environment::TickInfo* tick_info = env->tick_info(); - - if (tick_info->in_tick()) { - return ret; - } - - if (tick_info->length() == 0) { - tick_info->set_index(0); - return ret; - } - - tick_info->set_in_tick(true); - - // process nextTicks after call - env->tick_callback_function()->Call(process, 0, NULL); - - tick_info->set_in_tick(false); - - if (try_catch.HasCaught()) { - tick_info->set_last_threw(true); + if (ExecuteNextTickCallback(env)->IsFalse()) { return Undefined(env->isolate()); } @@ -2893,6 +2886,8 @@ void Load(Environment* env) { Local arg = env->process_object(); f->Call(global, 1, &arg); + // Handle any nextTicks added in the first tick of the program + ExecuteNextTickCallback(env); } static void PrintHelp(); diff --git a/test/message/nexttick_throw.out b/test/message/nexttick_throw.out index baced2ce879..c15d37fad20 100644 --- a/test/message/nexttick_throw.out +++ b/test/message/nexttick_throw.out @@ -5,6 +5,3 @@ ReferenceError: undefined_reference_error_maker is not defined at *test*message*nexttick_throw.js:*:* at process._tickCallback (node.js:*:*) - at Function.Module.runMain (module.js:*:*) - at startup (node.js:*:*) - at node.js:*:* diff --git a/test/simple/test-next-tick-ordering3.js b/test/simple/test-next-tick-ordering3.js new file mode 100644 index 00000000000..5131f7fdc2b --- /dev/null +++ b/test/simple/test-next-tick-ordering3.js @@ -0,0 +1,109 @@ +// 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 util = require('util'); + +if (!util.isFunction(Object.observe) && !util.isFunction(Promise)) { + console.error('Skipping because node does not support Object.observe and Promise'); + process.exit(0); +} + +var results = {}; + + +function Test1() { + var order = results['Test1'] = []; + var obj = {}; + Object.observe(obj, function(changeRecord) { + order.push('Object.observe'); + }); + process.nextTick(function() { + order.push('nextTick'); + }); + obj.a = 1; +} + + +function Test2() { + var order = results['Test2'] = []; + var obj = {}; + Object.observe(obj, function(changeRecord) { + order.push('Object.observe'); + }); + setTimeout(function() { + process.nextTick(function() { + order.push('nextTick'); + }); + obj.a = 1; + }, 0); +} + + +function Test3() { + var order = results['Test3'] = []; + var obj = {}; + + var promise = new Promise(function(onFulfilled, onRejected) { + onFulfilled(); + }); + + process.nextTick(function() { + order.push('nextTick'); + }); + + promise.then(function() { + order.push('Promise.onFullfilled'); + }, null); +} + + +function Test4() { + var order = results['Test4'] = []; + var obj = {}; + + setTimeout(function() { + var promise = new Promise(function(onFulfilled, onRejected) { + onFulfilled(); + }); + + process.nextTick(function() { + order.push('nextTick'); + }); + + promise.then(function() { + order.push('Promise.onFullfilled'); + }, null); + }, 0); +} + +Test1(); +Test2(); +Test3(); +Test4(); + +process.on('exit', function() { + assert.deepEqual(results.Test1, ['Object.observe', 'nextTick']); + assert.deepEqual(results.Test2, ['Object.observe', 'nextTick']); + assert.deepEqual(results.Test3, ['Promise.onFullfilled', 'nextTick']); + assert.deepEqual(results.Test4, ['Promise.onFullfilled', 'nextTick']); +});