From 07e8065d4279d46bf5f92080e53e6e0d16bc3265 Mon Sep 17 00:00:00 2001
From: Ben Noordhuis <info@bnoordhuis.nl>
Date: Fri, 24 Sep 2010 17:02:08 +0200
Subject: [PATCH] New() methods should be invoked as constructors, not regular functions. Corner cases like Script::New() may cause a SIGSEGV when the GC is run.

More details: http://groups.google.com/group/nodejs/browse_thread/thread/a7e5db68d4cd6356
---
 src/node.cc                |   14 ++++++++++++++
 src/node.h                 |    9 +++++++++
 src/node_buffer.cc         |   12 +++---------
 src/node_cares.cc          |    4 ++++
 src/node_idle_watcher.cc   |    4 ++++
 src/node_io_watcher.cc     |    4 ++++
 src/node_script.cc         |    4 ++++
 src/node_signal_watcher.cc |    4 ++++
 src/node_stat_watcher.cc   |    4 ++++
 src/node_timer.cc          |    4 ++++
 10 files changed, 54 insertions(+), 9 deletions(-)

diff --git a/src/node.cc b/src/node.cc
index f4b0820..650b2ec 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -787,6 +787,20 @@ Local<Value> ErrnoException(int errorno,
 }
 
 
+Handle<Value> FromConstructorTemplate(Persistent<FunctionTemplate>& constructorTemplate, const Arguments& args) {
+  HandleScope scope;
+
+  const int argc = args.Length();
+  Local<Value> argv[argc];
+
+  for (int i = 0; i < argc; ++i) {
+    argv[i] = args[i];
+  }
+
+  Local<Object> instance = constructorTemplate->GetFunction()->NewInstance(argc, argv);
+  return scope.Close(instance);
+}
+
 enum encoding ParseEncoding(Handle<Value> encoding_v, enum encoding _default) {
   HandleScope scope;
 
diff --git a/src/node.h b/src/node.h
index c73df2a..07b7f5e 100644
--- a/src/node.h
+++ b/src/node.h
@@ -65,6 +65,15 @@ ssize_t DecodeWrite(char *buf,
 v8::Local<v8::Object> BuildStatsObject(struct stat * s);
 
 
+/**
+ * Call this when your constructor is invoked as a regular function, e.g. Buffer(10) instead of new Buffer(10).
+ * @param constructorTemplate Constructor template to instantiate from.
+ * @param args The arguments object passed to your constructor.
+ * @see v8::Arguments::IsConstructCall
+ */
+v8::Handle<v8::Value> FromConstructorTemplate(v8::Persistent<v8::FunctionTemplate>& constructorTemplate, const v8::Arguments& args);
+
+
 static inline v8::Persistent<v8::Function>* cb_persist(
     const v8::Local<v8::Value> &v) {
   v8::Persistent<v8::Function> *fn = new v8::Persistent<v8::Function>();
diff --git a/src/node_buffer.cc b/src/node_buffer.cc
index 10ddbaf..434bc54 100644
--- a/src/node_buffer.cc
+++ b/src/node_buffer.cc
@@ -127,18 +127,12 @@ size_t Buffer::Length(Handle<Object> obj) {
 
 
 Handle<Value> Buffer::New(const Arguments &args) {
-  HandleScope scope;
-
   if (!args.IsConstructCall()) {
-    Local<Value> argv[10];
-    for (int i = 0; i < MIN(args.Length(), 10); i++) {
-      argv[i] = args[i];
-    }
-    Local<Object> instance =
-      constructor_template->GetFunction()->NewInstance(args.Length(), argv);
-    return scope.Close(instance);
+    return FromConstructorTemplate(constructor_template, args);
   }
 
+  HandleScope scope;
+
   Buffer *buffer;
   if (args[0]->IsInt32()) {
     // var buffer = new Buffer(1024);
diff --git a/src/node_cares.cc b/src/node_cares.cc
index 4bd07f8..ea227a8 100644
--- a/src/node_cares.cc
+++ b/src/node_cares.cc
@@ -459,6 +459,10 @@ void Channel::Initialize(Handle<Object> target) {
 
 
 Handle<Value> Channel::New(const Arguments& args) {
+  if (!args.IsConstructCall()) {
+    return FromConstructorTemplate(constructor_template, args);
+  }
+
   HandleScope scope;
 
   struct ares_options options;
diff --git a/src/node_idle_watcher.cc b/src/node_idle_watcher.cc
index 851a6f8..9ae1c4b 100644
--- a/src/node_idle_watcher.cc
+++ b/src/node_idle_watcher.cc
@@ -77,6 +77,10 @@ void IdleWatcher::Callback(EV_P_ ev_idle *w, int revents) {
 //  idle.start();
 //
 Handle<Value> IdleWatcher::New(const Arguments& args) {
+  if (!args.IsConstructCall()) {
+    return FromConstructorTemplate(constructor_template, args);
+  }
+
   HandleScope scope;
 
   IdleWatcher *s = new IdleWatcher();
diff --git a/src/node_io_watcher.cc b/src/node_io_watcher.cc
index 3d519d1..d0d83a0 100644
--- a/src/node_io_watcher.cc
+++ b/src/node_io_watcher.cc
@@ -68,6 +68,10 @@ void IOWatcher::Callback(EV_P_ ev_io *w, int revents) {
 //  io.start();
 //
 Handle<Value> IOWatcher::New(const Arguments& args) {
+  if (!args.IsConstructCall()) {
+    return FromConstructorTemplate(constructor_template, args);
+  }
+
   HandleScope scope;
   IOWatcher *s = new IOWatcher();
   s->Wrap(args.This());
diff --git a/src/node_script.cc b/src/node_script.cc
index d9631ec..cfcb205 100644
--- a/src/node_script.cc
+++ b/src/node_script.cc
@@ -78,6 +78,10 @@ void node::Script::Initialize (Handle<Object> target) {
 
 
 Handle<Value> node::Script::New (const Arguments& args) {
+  if (!args.IsConstructCall()) {
+    return FromConstructorTemplate(constructor_template, args);
+  }
+
   HandleScope scope;
 
   node::Script *t = new node::Script();
diff --git a/src/node_signal_watcher.cc b/src/node_signal_watcher.cc
index 305c06a..d593c94 100644
--- a/src/node_signal_watcher.cc
+++ b/src/node_signal_watcher.cc
@@ -51,6 +51,10 @@ void SignalWatcher::Callback(EV_P_ ev_signal *watcher, int revents) {
 }
 
 Handle<Value> SignalWatcher::New(const Arguments& args) {
+  if (!args.IsConstructCall()) {
+    return FromConstructorTemplate(constructor_template, args);
+  }
+
   HandleScope scope;
 
   if (args.Length() != 1 || !args[0]->IsInt32()) {
diff --git a/src/node_stat_watcher.cc b/src/node_stat_watcher.cc
index 88ae9c6..bbc4fdf 100644
--- a/src/node_stat_watcher.cc
+++ b/src/node_stat_watcher.cc
@@ -46,6 +46,10 @@ void StatWatcher::Callback(EV_P_ ev_stat *watcher, int revents) {
 
 
 Handle<Value> StatWatcher::New(const Arguments& args) {
+  if (!args.IsConstructCall()) {
+    return FromConstructorTemplate(constructor_template, args);
+  }
+
   HandleScope scope;
   StatWatcher *s = new StatWatcher();
   s->Wrap(args.Holder());
diff --git a/src/node_timer.cc b/src/node_timer.cc
index bee1aac..5918feb 100644
--- a/src/node_timer.cc
+++ b/src/node_timer.cc
@@ -97,6 +97,10 @@ Timer::~Timer ()
 Handle<Value>
 Timer::New (const Arguments& args)
 {
+  if (!args.IsConstructCall()) {
+    return FromConstructorTemplate(constructor_template, args);
+  }
+
   HandleScope scope;
 
   Timer *t = new Timer();
-- 
1.7.0.4

