summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xconfigure137
-rwxr-xr-xjs2c.py317
-rw-r--r--jsmin.py218
-rw-r--r--src/file.cc153
-rw-r--r--src/file.h8
-rw-r--r--src/main.js85
-rw-r--r--src/net.cc425
-rw-r--r--src/net.h8
-rw-r--r--src/node.cc296
-rw-r--r--src/node.h7
-rw-r--r--src/node_tcp.cc341
-rw-r--r--src/node_tcp.h8
-rw-r--r--src/process.cc39
-rw-r--r--src/process.h8
-rw-r--r--test/mjsunit.js165
-rw-r--r--test/test-test.js12
-rw-r--r--wscript28
17 files changed, 1759 insertions, 496 deletions
diff --git a/configure b/configure
new file mode 100755
index 0000000000..01876912fc
--- /dev/null
+++ b/configure
@@ -0,0 +1,137 @@
+#! /bin/sh
+
+# waf configure wrapper
+
+# Fancy colors used to beautify the output a bit.
+#
+if [ "$NOCOLOR" ] ; then
+ NORMAL=""
+ BOLD=""
+ RED=""
+ YELLOW=""
+ GREEN=""
+else
+ NORMAL='\\033[0m'
+ BOLD='\\033[01;1m'
+ RED='\\033[01;91m'
+ YELLOW='\\033[00;33m'
+ GREEN='\\033[01;92m'
+fi
+
+EXIT_SUCCESS=0
+EXIT_FAILURE=1
+EXIT_ERROR=2
+EXIT_BUG=10
+
+CUR_DIR=$PWD
+
+#possible relative path
+WORKINGDIR=`dirname $0`
+cd $WORKINGDIR
+#abs path
+WORKINGDIR=`pwd`
+cd $CUR_DIR
+
+# Checks for WAF. Honours $WAF if set. Stores path to 'waf' in $WAF.
+# Requires that $PYTHON is set.
+#
+checkWAF()
+{
+ printf "Checking for WAF\t\t\t: "
+ #installed miniwaf in sourcedir
+ if [ -z "$WAF" ] ; then
+ if [ -f "${WORKINGDIR}/waf" ] ; then
+ WAF="${WORKINGDIR}/waf"
+ if [ ! -x "$WAF" ] ; then
+ chmod +x $WAF
+ fi
+ fi
+ fi
+ if [ -z "$WAF" ] ; then
+ if [ -f "${WORKINGDIR}/waf-light" ] ; then
+ ${WORKINGDIR}/waf-light --make-waf
+ WAF="${WORKINGDIR}/waf"
+ fi
+ fi
+ #global installed waf with waf->waf.py link
+ if [ -z "$WAF" ] ; then
+ WAF=`which waf 2>/dev/null`
+ fi
+ # neither waf nor miniwaf could be found
+ if [ ! -x "$WAF" ] ; then
+ printf $RED"not found"$NORMAL"\n"
+ echo "Go to http://code.google.com/p/waf/"
+ echo "and download a waf version"
+ exit $EXIT_FAILURE
+ else
+ printf $GREEN"$WAF"$NORMAL"\n"
+ fi
+}
+
+# Generates a Makefile. Requires that $WAF is set.
+#
+generateMakefile()
+{
+ cat > Makefile << EOF
+#!/usr/bin/make -f
+# Waf Makefile wrapper
+WAF_HOME=$CUR_DIR
+
+all:
+ @$WAF build
+
+all-debug:
+ @$WAF -v build
+
+all-progress:
+ @$WAF -p build
+
+install:
+ if test -n "\$(DESTDIR)"; then \\
+ $WAF install --yes --destdir="\$(DESTDIR)" --prefix="$PREFIX"; \\
+ else \\
+ $WAF install --yes --prefix="$PREFIX"; \\
+ fi;
+
+uninstall:
+ @if test -n "\$(DESTDIR)"; then \\
+ $WAF uninstall --destdir="\$(DESTDIR)" --prefix="$PREFIX"; \\
+ else \\
+ $WAF uninstall --prefix="$PREFIX"; \\
+ fi;
+
+clean:
+ @$WAF clean
+
+distclean:
+ @$WAF distclean
+ @-rm -rf _build_
+ @-rm -f Makefile
+
+check:
+ @$WAF check
+
+dist:
+ @$WAF dist
+
+.PHONY: clean dist distclean check uninstall install all
+
+EOF
+}
+
+checkWAF
+
+PREFIX=/usr/local
+case $1 in
+ --prefix)
+ PREFIX=$2
+ ;;
+esac
+
+export PREFIX
+generateMakefile
+
+
+"${WAF}" configure --prefix "${PREFIX}"
+
+exit $?
diff --git a/js2c.py b/js2c.py
new file mode 100755
index 0000000000..312626101b
--- /dev/null
+++ b/js2c.py
@@ -0,0 +1,317 @@
+#!/usr/bin/env python
+#
+# Copyright 2006-2008 the V8 project authors. All rights reserved.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# This is a utility for converting JavaScript source code into C-style
+# char arrays. It is used for embedded JavaScript code in the V8
+# library.
+
+import os, re, sys, string
+import jsmin
+
+
+def ToCArray(lines):
+ result = []
+ for chr in lines:
+ value = ord(chr)
+ assert value < 128
+ result.append(str(value))
+ result.append("0")
+ return ", ".join(result)
+
+
+def CompressScript(lines, do_jsmin):
+ # If we're not expecting this code to be user visible, we can run it through
+ # a more aggressive minifier.
+ if do_jsmin:
+ return jsmin.jsmin(lines)
+
+ # Remove stuff from the source that we don't want to appear when
+ # people print the source code using Function.prototype.toString().
+ # Note that we could easily compress the scripts mode but don't
+ # since we want it to remain readable.
+ #lines = re.sub('//.*\n', '\n', lines) # end-of-line comments
+ #lines = re.sub(re.compile(r'/\*.*?\*/', re.DOTALL), '', lines) # comments.
+ #lines = re.sub('\s+\n+', '\n', lines) # trailing whitespace
+ return lines
+
+
+def ReadFile(filename):
+ file = open(filename, "rt")
+ try:
+ lines = file.read()
+ finally:
+ file.close()
+ return lines
+
+
+def ReadLines(filename):
+ result = []
+ for line in open(filename, "rt"):
+ if '#' in line:
+ line = line[:line.index('#')]
+ line = line.strip()
+ if len(line) > 0:
+ result.append(line)
+ return result
+
+
+def LoadConfigFrom(name):
+ import ConfigParser
+ config = ConfigParser.ConfigParser()
+ config.read(name)
+ return config
+
+
+def ParseValue(string):
+ string = string.strip()
+ if string.startswith('[') and string.endswith(']'):
+ return string.lstrip('[').rstrip(']').split()
+ else:
+ return string
+
+
+def ExpandConstants(lines, constants):
+ for key, value in constants.items():
+ lines = lines.replace(key, str(value))
+ return lines
+
+
+def ExpandMacros(lines, macros):
+ for name, macro in macros.items():
+ start = lines.find(name + '(', 0)
+ while start != -1:
+ # Scan over the arguments
+ assert lines[start + len(name)] == '('
+ height = 1
+ end = start + len(name) + 1
+ last_match = end
+ arg_index = 0
+ mapping = { }
+ def add_arg(str):
+ # Remember to expand recursively in the arguments
+ replacement = ExpandMacros(str.strip(), macros)
+ mapping[macro.args[arg_index]] = replacement
+ while end < len(lines) and height > 0:
+ # We don't count commas at higher nesting levels.
+ if lines[end] == ',' and height == 1:
+ add_arg(lines[last_match:end])
+ last_match = end + 1
+ elif lines[end] in ['(', '{', '[']:
+ height = height + 1
+ elif lines[end] in [')', '}', ']']:
+ height = height - 1
+ end = end + 1
+ # Remember to add the last match.
+ add_arg(lines[last_match:end-1])
+ result = macro.expand(mapping)
+ # Replace the occurrence of the macro with the expansion
+ lines = lines[:start] + result + lines[end:]
+ start = lines.find(name + '(', end)
+ return lines
+
+class TextMacro:
+ def __init__(self, args, body):
+ self.args = args
+ self.body = body
+ def expand(self, mapping):
+ result = self.body
+ for key, value in mapping.items():
+ result = result.replace(key, value)
+ return result
+
+class PythonMacro:
+ def __init__(self, args, fun):
+ self.args = args
+ self.fun = fun
+ def expand(self, mapping):
+ args = []
+ for arg in self.args:
+ args.append(mapping[arg])
+ return str(self.fun(*args))
+
+CONST_PATTERN = re.compile('^const\s+([a-zA-Z0-9_]+)\s*=\s*([^;]*);$')
+MACRO_PATTERN = re.compile('^macro\s+([a-zA-Z0-9_]+)\s*\(([^)]*)\)\s*=\s*([^;]*);$')
+PYTHON_MACRO_PATTERN = re.compile('^python\s+macro\s+([a-zA-Z0-9_]+)\s*\(([^)]*)\)\s*=\s*([^;]*);$')
+
+def ReadMacros(lines):
+ constants = { }
+ macros = { }
+ for line in lines:
+ hash = line.find('#')
+ if hash != -1: line = line[:hash]
+ line = line.strip()
+ if len(line) is 0: continue
+ const_match = CONST_PATTERN.match(line)
+ if const_match:
+ name = const_match.group(1)
+ value = const_match.group(2).strip()
+ constants[name] = value
+ else:
+ macro_match = MACRO_PATTERN.match(line)
+ if macro_match:
+ name = macro_match.group(1)
+ args = map(string.strip, macro_match.group(2).split(','))
+ body = macro_match.group(3).strip()
+ macros[name] = TextMacro(args, body)
+ else:
+ python_match = PYTHON_MACRO_PATTERN.match(line)
+ if python_match:
+ name = python_match.group(1)
+ args = map(string.strip, python_match.group(2).split(','))
+ body = python_match.group(3).strip()
+ fun = eval("lambda " + ",".join(args) + ': ' + body)
+ macros[name] = PythonMacro(args, fun)
+ else:
+ raise ("Illegal line: " + line)
+ return (constants, macros)
+
+
+HEADER_TEMPLATE = """\
+#ifndef node_natives_h
+#define node_natives_h
+namespace node {
+
+%(source_lines)s\
+
+}
+#endif
+"""
+
+
+SOURCE_DECLARATION = """\
+ static const char native_%(id)s[] = { %(data)s };
+"""
+
+
+GET_DELAY_INDEX_CASE = """\
+ if (strcmp(name, "%(id)s") == 0) return %(i)i;
+"""
+
+
+GET_DELAY_SCRIPT_SOURCE_CASE = """\
+ if (index == %(i)i) return Vector<const char>(%(id)s, %(length)i);
+"""
+
+
+GET_DELAY_SCRIPT_NAME_CASE = """\
+ if (index == %(i)i) return Vector<const char>("%(name)s", %(length)i);
+"""
+
+def JS2C(source, target):
+ ids = []
+ delay_ids = []
+ modules = []
+ # Locate the macros file name.
+ consts = {}
+ macros = {}
+ for s in source:
+ if 'macros.py' == (os.path.split(str(s))[1]):
+ (consts, macros) = ReadMacros(ReadLines(str(s)))
+ else:
+ modules.append(s)
+
+ # Build source code lines
+ source_lines = [ ]
+ source_lines_empty = []
+ for s in modules:
+ delay = str(s).endswith('-delay.js')
+ lines = ReadFile(str(s))
+ do_jsmin = lines.find('// jsminify this file, js2c: jsmin') != -1
+ lines = ExpandConstants(lines, consts)
+ lines = ExpandMacros(lines, macros)
+ lines = CompressScript(lines, do_jsmin)
+ data = ToCArray(lines)
+ id = (os.path.split(str(s))[1])[:-3]
+ if delay: id = id[:-6]
+ if delay:
+ delay_ids.append((id, len(lines)))
+ else:
+ ids.append((id, len(lines)))
+ source_lines.append(SOURCE_DECLARATION % { 'id': id, 'data': data })
+ source_lines_empty.append(SOURCE_DECLARATION % { 'id': id, 'data': 0 })
+
+ # Build delay support functions
+ get_index_cases = [ ]
+ get_script_source_cases = [ ]
+ get_script_name_cases = [ ]
+
+ i = 0
+ for (id, length) in delay_ids:
+ native_name = "native %s.js" % id
+ get_index_cases.append(GET_DELAY_INDEX_CASE % { 'id': id, 'i': i })
+ get_script_source_cases.append(GET_DELAY_SCRIPT_SOURCE_CASE % {
+ 'id': id,
+ 'length': length,
+ 'i': i
+ })
+ get_script_name_cases.append(GET_DELAY_SCRIPT_NAME_CASE % {
+ 'name': native_name,
+ 'length': len(native_name),
+ 'i': i
+ });
+ i = i + 1
+
+ for (id, length) in ids:
+ native_name = "native %s.js" % id
+ get_index_cases.append(GET_DELAY_INDEX_CASE % { 'id': id, 'i': i })
+ get_script_source_cases.append(GET_DELAY_SCRIPT_SOURCE_CASE % {
+ 'id': id,
+ 'length': length,
+ 'i': i
+ })
+ get_script_name_cases.append(GET_DELAY_SCRIPT_NAME_CASE % {
+ 'name': native_name,
+ 'length': len(native_name),
+ 'i': i
+ });
+ i = i + 1
+
+ # Emit result
+ output = open(str(target[0]), "w")
+ output.write(HEADER_TEMPLATE % {
+ 'builtin_count': len(ids) + len(delay_ids),
+ 'delay_count': len(delay_ids),
+ 'source_lines': "\n".join(source_lines),
+ 'get_index_cases': "".join(get_index_cases),
+ 'get_script_source_cases': "".join(get_script_source_cases),
+ 'get_script_name_cases': "".join(get_script_name_cases)
+ })
+ output.close()
+
+ if len(target) > 1:
+ output = open(str(target[1]), "w")
+ output.write(HEADER_TEMPLATE % {
+ 'builtin_count': len(ids) + len(delay_ids),
+ 'delay_count': len(delay_ids),
+ 'source_lines': "\n".join(source_lines_empty),
+ 'get_index_cases': "".join(get_index_cases),
+ 'get_script_source_cases': "".join(get_script_source_cases),
+ 'get_script_name_cases': "".join(get_script_name_cases)
+ })
+ output.close()
diff --git a/jsmin.py b/jsmin.py
new file mode 100644
index 0000000000..ae7581413a
--- /dev/null
+++ b/jsmin.py
@@ -0,0 +1,218 @@
+#!/usr/bin/python
+
+# This code is original from jsmin by Douglas Crockford, it was translated to
+# Python by Baruch Even. The original code had the following copyright and
+# license.
+#
+# /* jsmin.c
+# 2007-05-22
+#
+# Copyright (c) 2002 Douglas Crockford (www.crockford.com)
+#
+# 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 shall be used for Good, not Evil.
+#
+# 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.
+# */
+
+from StringIO import StringIO
+
+def jsmin(js):
+ ins = StringIO(js)
+ outs = StringIO()
+ JavascriptMinify().minify(ins, outs)
+ str = outs.getvalue()
+ if len(str) > 0 and str[0] == '\n':
+ str = str[1:]
+ return str
+
+def isAlphanum(c):
+ """return true if the character is a letter, digit, underscore,
+ dollar sign, or non-ASCII character.
+ """
+ return ((c >= 'a' and c <= 'z') or (c >= '0' and c <= '9') or
+ (c >= 'A' and c <= 'Z') or c == '_' or c == '$' or c == '\\' or (c is not None and ord(c) > 126));
+
+class UnterminatedComment(Exception):
+ pass
+
+class UnterminatedStringLiteral(Exception):
+ pass
+
+class UnterminatedRegularExpression(Exception):
+ pass
+
+class JavascriptMinify(object):
+
+ def _outA(self):
+ self.outstream.write(self.theA)
+ def _outB(self):
+ self.outstream.write(self.theB)
+
+ def _get(self):
+ """return the next character from stdin. Watch out for lookahead. If
+ the character is a control character, translate it to a space or
+ linefeed.
+ """
+ c = self.theLookahead
+ self.theLookahead = None
+ if c == None:
+ c = self.instream.read(1)
+ if c >= ' ' or c == '\n':
+ return c
+ if c == '': # EOF
+ return '\000'
+ if c == '\r':
+ return '\n'
+ return ' '
+
+ def _peek(self):
+ self.theLookahead = self._get()
+ return self.theLookahead
+
+ def _next(self):
+ """get the next character, excluding comments. peek() is used to see
+ if an unescaped '/' is followed by a '/' or '*'.
+ """
+ c = self._get()
+ if c == '/' and self.theA != '\\':
+ p = self._peek()
+ if p == '/':
+ c = self._get()
+ while c > '\n':
+ c = self._get()
+ return c
+ if p == '*':
+ c = self._get()
+ while 1:
+ c = self._get()
+ if c == '*':
+ if self._peek() == '/':
+ self._get()
+ return ' '
+ if c == '\000':
+ raise UnterminatedComment()
+
+ return c
+
+ def _action(self, action):
+ """do something! What you do is determined by the argument:
+ 1 Output A. Copy B to A. Get the next B.
+ 2 Copy B to A. Get the next B. (Delete A).
+ 3 Get the next B. (Delete B).
+ action treats a string as a single character. Wow!
+ action recognizes a regular expression if it is preceded by ( or , or =.
+ """
+ if action <= 1:
+ self._outA()
+
+ if action <= 2:
+ self.theA = self.theB
+ if self.theA == "'" or self.theA == '"':
+ while 1:
+ self._outA()
+ self.theA = self._get()
+ if self.theA == self.theB:
+ break
+ if self.theA <= '\n':
+ raise UnterminatedStringLiteral()
+ if self.theA == '\\':
+ self._outA()
+ self.theA = self._get()
+
+
+ if action <= 3:
+ self.theB = self._next()
+ if self.theB == '/' and (self.theA == '(' or self.theA == ',' or
+ self.theA == '=' or self.theA == ':' or
+ self.theA == '[' or self.theA == '?' or
+ self.theA == '!' or self.theA == '&' or
+ self.theA == '|' or self.theA == ';' or
+ self.theA == '{' or self.theA == '}' or
+ self.theA == '\n'):
+ self._outA()
+ self._outB()
+ while 1:
+ self.theA = self._get()
+ if self.theA == '/':
+ break
+ elif self.theA == '\\':
+ self._outA()
+ self.theA = self._get()
+ elif self.theA <= '\n':
+ raise UnterminatedRegularExpression()
+ self._outA()
+ self.theB = self._next()
+
+
+ def _jsmin(self):
+ """Copy the input to the output, deleting the characters which are
+ insignificant to JavaScript. Comments will be removed. Tabs will be
+ replaced with spaces. Carriage returns will be replaced with linefeeds.
+ Most spaces and linefeeds will be removed.
+ """
+ self.theA = '\n'
+ self._action(3)
+
+ while self.theA != '\000':
+ if self.theA == ' ':
+ if isAlphanum(self.theB):
+ self._action(1)
+ else:
+ self._action(2)
+ elif self.theA == '\n':
+ if self.theB in ['{', '[', '(', '+', '-']:
+ self._action(1)
+ elif self.theB == ' ':
+ self._action(3)
+ else:
+ if isAlphanum(self.theB):
+ self._action(1)
+ else:
+ self._action(2)
+ else:
+ if self.theB == ' ':
+ if isAlphanum(self.theA):
+ self._action(1)
+ else:
+ self._action(3)
+ elif self.theB == '\n':
+ if self.theA in ['}', ']', ')', '+', '-', '"', '\'']:
+ self._action(1)
+ else:
+ if isAlphanum(self.theA):
+ self._action(1)
+ else:
+ self._action(3)
+ else:
+ self._action(1)
+
+ def minify(self, instream, outstream):
+ self.instream = instream
+ self.outstream = outstream
+ self.theA = '\n'
+ self.theB = None
+ self.theLookahead = None
+
+ self._jsmin()
+ self.instream.close()
+
+if __name__ == '__main__':
+ import sys
+ jsm = JavascriptMinify()
+ jsm.minify(sys.stdin, sys.stdout)
diff --git a/src/file.cc b/src/file.cc
new file mode 100644
index 0000000000..6638f07536
--- /dev/null
+++ b/src/file.cc
@@ -0,0 +1,153 @@
+#include "node.h"
+#include <string.h>
+
+using namespace v8;
+
+class Callback {
+ public:
+ Callback(Handle<Value> v);
+ ~Callback();
+ Local<Value> Call(Handle<Object> recv, int argc, Handle<Value> argv[]);
+ private:
+ Persistent<Function> handle;
+};
+
+
+Callback::Callback (Handle<Value> v)
+{
+ HandleScope scope;
+ Handle<Function> f = Handle<Function>::Cast(v);
+ handle = Persistent<Function>::New(f);
+}
+
+Callback::~Callback ()
+{
+ handle.Dispose();
+ handle.Clear(); // necessary?
+}
+
+Local<Value>
+Callback::Call (Handle<Object> recv, int argc, Handle<Value> argv[])
+{
+ HandleScope scope;
+ Local<Value> r = handle->Call(recv, argc, argv);
+ return scope.Close(r);
+}
+
+static int
+after_rename (eio_req *req)
+{
+ Callback *callback = static_cast<Callback*>(req->data);
+ if (callback != NULL) {
+ HandleScope scope;
+ const int argc = 2;
+ Local<Value> argv[argc];
+
+ argv[0] = Integer::New(req->errorno);
+ argv[1] = String::New(strerror(req->errorno));
+
+ callback->Call(Context::GetCurrent()->Global(), argc, argv);
+ delete callback;
+ }
+ return 0;
+}
+
+JS_METHOD(rename)
+{
+ if (args.Length() < 2)
+ return Undefined();
+
+ HandleScope scope;
+
+ String::Utf8Value path(args[0]->ToString());
+ String::Utf8Value new_path(args[1]->ToString());
+
+ Callback *callback = NULL;
+ if (!args[2]->IsUndefined()) callback = new Callback(args[2]);
+
+ eio_req *req = eio_rename(*path, *new_path, EIO_PRI_DEFAULT, after_rename, callback);
+ node_eio_submit(req);
+
+ return Undefined();
+}
+
+static int
+after_stat (eio_req *req)
+{
+ Callback *callback = static_cast<Callback*>(req->data);
+ if (callback != NULL) {
+ HandleScope scope;
+ const int argc = 3;
+ Local<Value> argv[argc];
+
+ Local<Object> stats = Object::New();
+ argv[0] = stats;
+ argv[1] = Integer::New(req->errorno);
+ argv[2] = String::New(strerror(req->errorno));
+
+ if (req->result == 0) {
+ struct stat *s = static_cast<struct stat*>(req->ptr2);
+
+ /* ID of device containing file */
+ stats->Set(JS_SYMBOL("dev"), Integer::New(s->st_dev));
+ /* inode number */
+ stats->Set(JS_SYMBOL("ino"), Integer::New(s->st_ino));
+ /* protection */
+ stats->Set(JS_SYMBOL("mode"), Integer::New(s->st_mode));
+ /* number of hard links */
+ stats->Set(JS_SYMBOL("nlink"), Integer::New(s->st_nlink));
+ /* user ID of owner */
+ stats->Set(JS_SYMBOL("uid"), Integer::New(s->st_uid));
+ /* group ID of owner */
+ stats->Set(JS_SYMBOL("gid"), Integer::New(s->st_gid));
+ /* device ID (if special file) */
+ stats->Set(JS_SYMBOL("rdev"), Integer::New(s->st_rdev));
+ /* total size, in bytes */
+ stats->Set(JS_SYMBOL("size"), Integer::New(s->st_size));
+ /* blocksize for filesystem I/O */
+ stats->Set(JS_SYMBOL("blksize"), Integer::New(s->st_blksize));
+ /* number of blocks allocated */
+ stats->Set(JS_SYMBOL("blocks"), Integer::New(s->st_blocks));
+ /* time of last access */
+ stats->Set(JS_SYMBOL("atime"), Date::New(1000*static_cast<double>(s->st_atime)));
+ /* time of last modification */
+ stats->Set(JS_SYMBOL("mtime"), Date::New(1000*static_cast<double>(s->st_mtime)));
+ /* time of last status change */
+ stats->Set(JS_SYMBOL("ctime"), Date::New(1000*static_cast<double>(s->st_ctime)));
+ }
+
+ callback->Call(Context::GetCurrent()->Global(), argc, argv);
+ delete callback;
+ }
+ return 0;
+}
+
+JS_METHOD(stat)
+{
+ if (args.Length() < 1)
+ return v8::Undefined();
+
+ HandleScope scope;
+
+ String::Utf8Value path(args[0]->ToString());
+
+ Callback *callback = NULL;
+ if (!args[1]->IsUndefined()) callback = new Callback(args[1]);
+
+ eio_req *req = eio_stat(*path, EIO_PRI_DEFAULT, after_stat, callback);
+ node_eio_submit(req);
+
+ return Undefined();
+}
+
+void
+NodeInit_file (Handle<Object> target)
+{
+ HandleScope scope;
+
+ Local<Object> fs = Object::New();
+ target->Set(String::NewSymbol("fs"), fs);
+
+ JS_SET_METHOD(fs, "rename", rename);
+ JS_SET_METHOD(fs, "stat", stat);
+}
diff --git a/src/file.h b/src/file.h
new file mode 100644
index 0000000000..75fddb7871
--- /dev/null
+++ b/src/file.h
@@ -0,0 +1,8 @@
+#ifndef node_file_h
+#define node_file_h
+
+#include <v8.h>
+
+void NodeInit_file (v8::Handle<v8::Object> target);
+
+#endif
diff --git a/src/main.js b/src/main.js
new file mode 100644
index 0000000000..ae847e798f
--- /dev/null
+++ b/src/main.js
@@ -0,0 +1,85 @@
+// module search paths
+node.includes = ["."];
+
+node.path = new function () {
+ this.join = function () {
+ var joined = "";
+ for (var i = 0; i < arguments.length; i++) {
+ var part = arguments[i].toString();
+ if (i === 0) {
+ part = part.replace(/\/*$/, "/");
+ } else if (i === arguments.length - 1) {
+ part = part.replace(/^\/*/, "");
+ } else {
+ part = part.replace(/^\/*/, "")
+ .replace(/\/*$/, "/");
+ }
+ joined += part;
+ }
+ return joined;
+ };
+
+ this.dirname = function (path) {
+ var parts = path.split("/");
+ return parts.slice(0, parts.length-1);
+ };
+};
+
+function __include (module, path) {
+ var export = module.require(path);
+ for (var i in export) {
+ if (export.hasOwnProperty(i))
+ module[i] = export[i];
+ }
+}
+
+
+
+function __require (path, loading_file) {
+
+ var filename = path;
+ // relative path
+ // absolute path
+ if (path.slice(0,1) === "/") {
+ } else {
+ filename = node.path.join(node.path.dirname(loading_file), path);
+ }
+ node.blocking.print("require: " + filename);
+
+ /*
+ for (var i = 0; i < suffixes.length; i++) {
+ var f = filename + "." + suffixes[i];
+
+ var stats = node.blocking.stat(f);
+ for (var j in stats) {
+ node.blocking.print("stats." + j + " = " + stats[j].toString());
+ }
+ }
+ */
+
+ var source = node.blocking.cat(filename);
+
+ // wrap the source in a function
+ source = "function (__file__, __dir__) { "
+ + " var exports = {};"
+ + " function require (m) { return __require(m, __file__); }"
+ + " function include (m) { return __include(this, m); }"
+ + source
+ + " return exports;"
+ + "};"
+ ;
+ var create_module = node.blocking.exec(source, filename);
+
+ // execute the function wrap
+ return create_module(filename, node.path.dirname(filename));
+}
+
+// main script execution.
+//__require(ARGV[1], ARGV[1]);
+//
+fs.stat("/tmp/world", function (stat, status, msg) {
+ for ( var i in stat ) {
+ node.blocking.print(i + ": " + stat[i]);
+ }
+ node.blocking.print("done: " + status.toString() + " " + msg.toString());
+});
diff --git a/src/net.cc b/src/net.cc
new file mode 100644
index 0000000000..b381ed99c7
--- /dev/null
+++ b/src/net.cc
@@ -0,0 +1,425 @@
+#include "net.h"
+#include "node.h"
+
+#include <oi_socket.h>
+#include <oi_buf.h>
+
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <strings.h>
+
+using namespace v8;
+
+static Persistent<String> readyState_str;
+
+static Persistent<Integer> readyStateCONNECTING;
+static Persistent<Integer> readyStateOPEN;
+static Persistent<Integer> readyStateCLOSED;
+
+enum encoding {UTF8, RAW};
+
+class Socket {
+public:
+ Socket (Handle<Object> obj, double timeout);
+ ~Socket ();
+
+ int ConnectTCP (char *port, char *host);
+ void Write (Handle<Value> arg);
+ void Disconnect ();
+
+ void SetEncoding (enum encoding);
+ void SetTimeout (double);
+
+ void OnConnect ();
+ void OnRead (const void *buf, size_t count);
+ void OnDrain ();
+ void OnError (oi_error e);
+ void OnClose ();
+
+private:
+ oi_socket socket_;
+ struct addrinfo *address_;
+ Persistent<Object> js_object_;
+};
+
+static void
+on_connect (oi_socket *socket)
+{
+ Socket *s = static_cast<Socket*> (socket->data);
+ s->OnConnect();
+}
+
+static void
+on_read (oi_socket *socket, const void *buf, size_t count)
+{
+ Socket *s = static_cast<Socket*> (socket->data);
+ s->OnRead(buf, count);
+}
+
+static void
+on_drain (oi_socket *socket)
+{
+ Socket *s = static_cast<Socket*> (socket->data);
+ s->OnDrain();
+}
+
+static void
+on_error (oi_socket *socket, oi_error e)
+{
+ Socket *s = static_cast<Socket*> (socket->data);
+ s->OnError(e);
+}
+
+static void
+on_timeout (oi_socket *socket)
+{
+ Socket *s = static_cast<Socket*> (socket->data);
+ s->OnTimeout(e);
+}
+
+static void
+on_close (oi_socket *socket)
+{
+ Socket *s = static_cast<Socket*> (socket->data);
+ s->OnClose();
+}
+
+
+static Handle<Value>
+NewSocket (const Arguments& args)
+{
+ if (args.Length() > 1)
+ return Undefined();
+
+ HandleScope scope;
+
+ // Default options
+ double timeout = 60.0; // in seconds
+ enum {RAW, UTF8} encoding = RAW;
+
+ // Set options from argument.
+ if (args.Length() == 1 && args[0]->IsObject()) {
+ Local<Object> options = args[0]->ToObject();
+ Local<Value> timeout_value = options->Get(String::NewSymbol("timeout"));
+ Local<Value> encoding_value = options->Get(String::NewSymbol("encoding"));
+
+ if (timeout_value->IsNumber()) {
+ // timeout is specified in milliseconds like other time
+ // values in javascript
+ timeout = timeout_value->NumberValue() / 1000;
+ }
+
+ if (encoding_value->IsString()) {
+ Local<String> encoding_string = encoding_value->ToString();
+ char buf[5]; // need enough room for "utf8" or "raw"
+ encoding_string->WriteAscii(buf, 0, 4);
+ buf[4] = '\0';
+ if(strcasecmp(buf, "utf8") == 0) encoding = UTF8;
+ }
+ }
+
+ Socket *s = new Socket(args.This(), timeout);
+ if(s == NULL)
+ return Undefined(); // XXX raise error?
+
+ return args.This();
+}
+
+static Socket*
+Unwrapsocket (Handle<Object> obj)
+{
+ HandleScope scope;
+ Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
+ Socket* socket = static_cast<Socket*>(field->Value());
+ return socket;
+}
+
+static Handle<Value>
+SocketConnectTCPCallback (const Arguments& args)
+{
+ if (args.Length() < 1)
+ return Undefined();
+
+ HandleScope scope;
+ Socket *socket = Unwrapsocket(args.Holder());
+
+ String::AsciiValue port(args[0]);
+
+ char *host = NULL;
+ String::AsciiValue host_v(args[1]->ToString());
+ if(args[1]->IsString()) {
+ host = *host_v;
+ }
+
+ int r = socket->ConnectTCP(*port, host);
+ // TODO raise error if r != 0
+
+ return Undefined();
+}
+
+static Handle<Value>
+SocketWriteCallback (const Arguments& args)
+{
+ HandleScope scope;
+ Socket *socket = Unwrapsocket(args.Holder());
+ socket->Write(args[0]);
+ return Undefined();
+}
+
+static Handle<Value>
+SocketCloseCallback (const Arguments& args)
+{
+ HandleScope scope;
+ Socket *socket = Unwrapsocket(args.Holder());
+ socket->Disconnect();
+ return Undefined();
+}
+
+static void
+DestroySocket (Persistent<Value> _, void *data)
+{
+ Socket *s = static_cast<Socket*> (data);
+ delete s;
+}
+
+Socket::Socket(Handle<Object> js_object, double timeout)
+{
+ oi_socket_init(&socket_, timeout);
+ socket_.on_connect = on_connect;
+ socket_.on_read = on_read;
+ socket_.on_drain = on_drain;
+ socket_.on_error = on_error;
+ socket_.on_close = on_close;
+ socket_.on_timeout = on_timeout;
+ socket_.data = this;
+
+ HandleScope scope;
+ js_object_ = Persistent<Object>::New(js_object);
+ js_object_->SetInternalField (0, External::New(this));
+ js_object_.MakeWeak (this, DestroySocket);
+}
+
+Socket::~Socket ()
+{
+ Disconnect();
+ oi_socket_detach(&socket_);
+ js_object_.Dispose();
+ js_object_.Clear(); // necessary?
+}
+
+static struct addrinfo tcp_hints =
+/* ai_flags */ { AI_PASSIVE
+/* ai_family */ , AF_UNSPEC
+/* ai_socktype */ , SOCK_STREAM
+/* ai_protocol */ , 0
+/* ai_addrlen */ , 0
+/* ai_addr */ , 0
+/* ai_canonname */ , 0
+/* ai_next */ , 0
+ };
+
+int
+Socket::ConnectTCP(char *port, char *host)
+{
+ int r;
+
+ HandleScope scope;
+
+ js_object_->Set(readyState_str, readyStateCONNECTING);
+
+ /* FIXME Blocking DNS resolution. */
+ printf("resolving host: %s, port: %s\n", host, port);
+ r = getaddrinfo (host, port, &tcp_hints, &address);
+ if(r != 0) {
+ perror("getaddrinfo");
+ return r;
+ }
+
+ r = oi_socket_connect (&socket, address);
+ if(r != 0) {
+ perror("oi_socket_connect");
+ return r;
+ }
+ oi_socket_attach (&socket, node_loop());
+
+ freeaddrinfo(address);
+ address = NULL;
+}
+
+void Socket::Write (Handle<Value> arg)
+{
+ HandleScope scope;
+
+ if (arg == Null()) {
+
+ oi_socket_write_eof(&socket);
+
+ } else if (arg->IsString()) {
+ Local<String> s = arg->ToString();
+
+ size_t l1 = s->Utf8Length(), l2;
+ oi_buf *buf = oi_buf_new2(l1);
+ l2 = s->WriteUtf8(buf->base, l1);
+ assert(l1 == l2);
+
+ oi_socket_write(&socket, buf);
+
+ } else if (arg->IsArray()) {
+ size_t length = array->Length();
+ Handle<Array> array = Handle<Array>::Cast(arg);
+ oi_buf *buf = oi_buf_new2(length);
+ for (int i = 0; i < length; i++) {
+ Local<Value> int_value = array->Get(Integer::New(i));
+ buf[i] = int_value->Int32Value();
+ }
+
+ oi_socket_write(&socket, buf);
+
+ } else {
+ // raise error bad argument.
+ assert(0);
+ }
+}
+
+void
+Socket::Disconnect()
+{
+ oi_socket_close(&socket);
+}
+
+void
+Socket::OnConnect()
+{
+ HandleScope scope;
+
+ assert(READY_STATE_CONNECTING == ReadyState());
+ js_object_->Set(readyState_str, readyStateOPEN);
+
+ Handle<Value> on_connect_value = js_object_->Get( String::NewSymbol("on_connect") );
+ if (!on_connect_value->IsFunction())
+ return;
+ Handle<Function> on_connect = Handle<Function>::Cast(on_connect_value);
+
+ TryCatch try_catch;
+
+ Handle<Value> r = on_connect->Call(js_object_, 0, NULL);
+
+ if(try_catch.HasCaught())
+ node_fatal_exception(try_catch);
+}
+
+void
+Socket::OnRead (const void *buf, size_t count)
+{
+ HandleScope scope;
+
+ assert(READY_STATE_OPEN == ReadyState());
+
+ Handle<Value> onread_value = js_object_->Get( String::NewSymbol("on_read") );
+ if (!onread_value->IsFunction()) return;
+ Handle<Function> onread = Handle<Function>::Cast(onread_value);
+
+ const int argc = 1;
+ Handle<Value> argv[argc];
+
+ if(count) {
+ Handle<String> chunk = String::New((const char*)buf, count); // TODO binary data?
+ argv[0] = chunk;
+ } else {
+ // TODO eof? delete write method?
+ argv[0] = Null();
+ }
+
+ TryCatch try_catch;
+
+ Handle<Value> r = onread->Call(js_object_, argc, argv);
+
+ if(try_catch.HasCaught())
+ node_fatal_exception(try_catch);
+}
+
+void
+Socket::OnClose ()
+{
+ HandleScope scope;
+
+ printf("onclose readyState %d\n", ReadyState());
+
+ assert(READY_STATE_OPEN == ReadyState());
+ js_object_->Set(readyState_str, readyStateCLOSED);
+
+ Handle<Value> onclose_value = js_object_->Get( String::NewSymbol("on_close") );
+ if (!onclose_value->IsFunction()) return;
+ Handle<Function> onclose = Handle<Function>::Cast(onclose_value);
+
+ TryCatch try_catch;
+
+ Handle<Value> r = onclose->Call(js_object_, 0, NULL);
+
+ if(try_catch.HasCaught())
+ node_fatal_exception(try_catch);
+}
+
+void
+NodeInit_net (Handle<Object> target)
+{
+ HandleScope scope;
+
+ //
+ // Socket
+ //
+ Local<FunctionTemplate> socket_template = FunctionTemplate::New(NewSocket);
+ target->Set(String::NewSymbol("Socket"), socket_template->GetFunction());
+ socket_template->InstanceTemplate()->SetInternalFieldCount(1);
+
+ // socket.connectTCP()
+ Local<FunctionTemplate> socket_connect_tcp =
+ FunctionTemplate::New(SocketConnectTCPCallback);
+ socket_template->InstanceTemplate()->Set(String::NewSymbol("connectTCP"),
+ socket_connect_tcp->GetFunction());
+
+ // socket.connectUNIX()
+ Local<FunctionTemplate> socket_connect_unix =
+ FunctionTemplate::New(SocketConnectUNIXCallback);
+ socket_template->InstanceTemplate()->Set(String::NewSymbol("connectUNIX"),
+ socket_connect_unix->GetFunction());
+
+ // socket.write()
+ Local<FunctionTemplate> socket_write =
+ FunctionTemplate::New(SocketWriteCallback);
+ socket_template->InstanceTemplate()->Set(String::NewSymbol("write"),
+ socket_write->GetFunction());
+
+ // socket.close()
+ Local<FunctionTemplate> socket_close =
+ FunctionTemplate::New(SocketCloseCallback);
+ socket_template->InstanceTemplate()->Set(String::NewSymbol("close"),
+ socket_close->GetFunction());
+
+ //
+ // Server
+ //
+ Local<FunctionTemplate> server_template = FunctionTemplate::New(NewServer);
+ target->Set(String::NewSymbol("Server"), server_template->GetFunction());
+ server_template->InstanceTemplate()->SetInternalFieldCount(1);
+
+ // server.listenTCP()
+ Local<FunctionTemplate> server_listenTCP =
+ FunctionTemplate::New(ServerListenTCPCallback);
+ server_template->InstanceTemplate()->Set(String::NewSymbol("listenTCP"),
+ server_listenTCP->GetFunction());
+
+ // server.listenUNIX()
+ Local<FunctionTemplate> server_listenUNIX =
+ FunctionTemplate::New(ServerListenUNIXCallback);
+ server_template->InstanceTemplate()->Set(String::NewSymbol("listenUNIX"),
+ server_listenTCP->GetFunction());
+
+ // server.close()
+ Local<FunctionTemplate> server_close = FunctionTemplate::New(ServerCloseCallback);
+ server_template->InstanceTemplate()->Set(String::NewSymbol("close"),
+ server_close->GetFunction());
+}
+
diff --git a/src/net.h b/src/net.h
new file mode 100644
index 0000000000..347a3ebceb
--- /dev/null
+++ b/src/net.h
@@ -0,0 +1,8 @@
+#ifndef node_net_h
+#define node_net_h
+
+#include <v8.h>
+
+void NodeInit_net (v8::Handle<v8::Object> target);
+
+#endif
diff --git a/src/node.cc b/src/node.cc
index e89b88c042..07d31b29d4 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -1,9 +1,13 @@
#include "node.h"
-#include "node_tcp.h"
+//#include "net.h"
+#include "file.h"
+#include "process.h"
#include "node_http.h"
#include "node_timer.h"
+#include "natives.h"
+
#include <stdio.h>
#include <assert.h>
@@ -12,20 +16,114 @@
#include <map>
using namespace v8;
+using namespace node;
using namespace std;
static int exit_code = 0;
-// Reads a file into a v8 string.
-static Handle<String>
-ReadFile (const string& name)
+// Extracts a C string from a V8 Utf8Value.
+const char*
+ToCString(const v8::String::Utf8Value& value)
{
+ return *value ? *value : "<string conversion failed>";
+}
- FILE* file = fopen(name.c_str(), "rb");
- if (file == NULL) return Handle<String>();
+void
+ReportException(v8::TryCatch* try_catch)
+{
+ v8::HandleScope handle_scope;
+ v8::String::Utf8Value exception(try_catch->Exception());
+ const char* exception_string = ToCString(exception);
+ v8::Handle<v8::Message> message = try_catch->Message();
+ if (message.IsEmpty()) {
+ // V8 didn't provide any extra information about this error; just
+ // print the exception.
+ printf("%s\n", exception_string);
+ } else {
+ message->PrintCurrentStackTrace(stdout);
+
+ // Print (filename):(line number): (message).
+ v8::String::Utf8Value filename(message->GetScriptResourceName());
+ const char* filename_string = ToCString(filename);
+ int linenum = message->GetLineNumber();
+ printf("%s:%i: %s\n", filename_string, linenum, exception_string);
+ // Print line of source code.
+ v8::String::Utf8Value sourceline(message->GetSourceLine());
+ const char* sourceline_string = ToCString(sourceline);
+ printf("%s\n", sourceline_string);
+ // Print wavy underline (GetUnderline is deprecated).
+ int start = message->GetStartColumn();
+ for (int i = 0; i < start; i++) {
+ printf(" ");
+ }
+ int end = message->GetEndColumn();
+ for (int i = start; i < end; i++) {
+ printf("^");
+ }
+ printf("\n");
+ }
+}
+
+// Executes a string within the current v8 context.
+Handle<Value>
+ExecuteString(v8::Handle<v8::String> source,
+ v8::Handle<v8::Value> filename)
+{
+ HandleScope scope;
+ Handle<Script> script = Script::Compile(source, filename);
+ if (script.IsEmpty()) {
+ return ThrowException(String::New("Error compiling string"));
+ }
+
+ Handle<Value> result = script->Run();
+ if (result.IsEmpty()) {
+ return ThrowException(String::New("Error running string"));
+ }
+
+ return scope.Close(result);
+}
+
+JS_METHOD(print)
+{
+ if (args.Length() < 1) return v8::Undefined();
+ HandleScope scope;
+ Handle<Value> arg = args[0];
+ String::Utf8Value value(arg);
+
+ printf("%s\n", *value);
+ fflush(stdout);
+
+ return Undefined();
+}
+
+
+JS_METHOD(cat)
+{
+ if (args.Length() < 1) return v8::Undefined();
+ HandleScope scope;
+
+ String::Utf8Value filename(args[0]);
+
+ Local<String> error_msg = String::New("File I/O error");
+
+ FILE* file = fopen(*filename, "rb");
+ if (file == NULL) {
+ // Raise error
+ perror("fopen()");
+ return ThrowException(error_msg);
+ }
- fseek(file, 0, SEEK_END);
+ int r = fseek(file, 0, SEEK_END);
+ if (r < 0) {
+ perror("fseek()");
+ return ThrowException(error_msg);
+ }
+
int size = ftell(file);
+ if (size < 0) {
+ perror("ftell()");
+ return ThrowException(error_msg);
+ }
rewind(file);
char chars[size+1];
@@ -34,6 +132,7 @@ ReadFile (const string& name)
int read = fread(&chars[i], 1, size - i, file);
if(read <= 0) {
perror("read()");
+ return ThrowException(error_msg);
}
i += read;
}
@@ -45,38 +144,27 @@ ReadFile (const string& name)
fclose(file);
- HandleScope scope;
- Local<String> result = String::New(expanded_base, size);
+ Local<String> contents = String::New(expanded_base, size);
- return scope.Close(result);
+ return scope.Close(contents);
}
-static Handle<Value>
-Log (const Arguments& args)
+JS_METHOD(exec)
{
- if (args.Length() < 1) return v8::Undefined();
+ if (args.Length() < 2)
+ return Undefined();
+
HandleScope scope;
- Handle<Value> arg = args[0];
- String::Utf8Value value(arg);
- printf("%s\n", *value);
- fflush(stdout);
+ Local<String> source = args[0]->ToString();
+ Local<String> filename = args[1]->ToString();
- return Undefined();
+ Handle<Value> result = ExecuteString(source, filename);
+
+ return scope.Close(result);
}
-static Handle<Value>
-BlockingFileRead (const Arguments& args)
-{
- if (args.Length() < 1) return v8::Undefined();
- HandleScope scope;
-
- String::Utf8Value filename(args[0]);
- Handle<String> output = ReadFile (*filename);
- return scope.Close(output);
-}
-
static void
OnFatalError (const char* location, const char* message)
{
@@ -85,45 +173,6 @@ OnFatalError (const char* location, const char* message)
}
-// Extracts a C string from a V8 Utf8Value.
-const char* ToCString(const v8::String::Utf8Value& value) {
- return *value ? *value : "<string conversion failed>";
-}
-
-void ReportException(v8::TryCatch* try_catch) {
- v8::HandleScope handle_scope;
- v8::String::Utf8Value exception(try_catch->Exception());
- const char* exception_string = ToCString(exception);
- v8::Handle<v8::Message> message = try_catch->Message();
- if (message.IsEmpty()) {
- // V8 didn't provide any extra information about this error; just
- // print the exception.
- printf("%s\n", exception_string);
- } else {
- message->PrintCurrentStackTrace(stdout);
-
- // Print (filename):(line number): (message).
- v8::String::Utf8Value filename(message->GetScriptResourceName());
- const char* filename_string = ToCString(filename);
- int linenum = message->GetLineNumber();
- printf("%s:%i: %s\n", filename_string, linenum, exception_string);
- // Print line of source code.
- v8::String::Utf8Value sourceline(message->GetSourceLine());
- const char* sourceline_string = ToCString(sourceline);
- printf("%s\n", sourceline_string);
- // Print wavy underline (GetUnderline is deprecated).
- int start = message->GetStartColumn();
- for (int i = 0; i < start; i++) {
- printf(" ");
- }
- int end = message->GetEndColumn();
- for (int i = start; i < end; i++) {
- printf("^");
- }
- printf("\n");
- }
-}
-
void
node_fatal_exception (TryCatch &try_catch)
{
@@ -132,60 +181,12 @@ node_fatal_exception (TryCatch &try_catch)
exit_code = 1;
}
-
-// Executes a string within the current v8 context.
-bool ExecuteString(v8::Handle<v8::String> source,
- v8::Handle<v8::Value> name,
- bool print_result,
- bool report_exceptions) {
- v8::HandleScope handle_scope;
- v8::TryCatch try_catch;
- v8::Handle<v8::Script> script = v8::Script::Compile(source, name);
- if (script.IsEmpty()) {
- // Print errors that happened during compilation.
- if (report_exceptions)
- ReportException(&try_catch);
- return false;
- } else {
- v8::Handle<v8::Value> result = script->Run();
- if (result.IsEmpty()) {
- // Print errors that happened during execution.
- if (report_exceptions)
- ReportException(&try_catch);
- return false;
- } else {
- if (print_result && !result->IsUndefined()) {
- // If all went well and the result wasn't undefined then print
- // the returned value.
- v8::String::Utf8Value str(result);
- const char* cstr = ToCString(str);
- printf("%s\n", cstr);
- }
- return true;
- }
- }
+void node_exit (int code)
+{
+ exit_code = code;
+ ev_unloop(node_loop(), EVUNLOOP_ALL);
}
-// The callback that is invoked by v8 whenever the JavaScript 'load'
-// function is called. Loads, compiles and executes its argument
-// JavaScript file.
-v8::Handle<v8::Value> Load(const v8::Arguments& args) {
- for (int i = 0; i < args.Length(); i++) {
- v8::HandleScope handle_scope;
- v8::String::Utf8Value file(args[i]);
- if (*file == NULL) {
- return v8::ThrowException(v8::String::New("Error loading file"));
- }
- v8::Handle<v8::String> source = ReadFile(*file);
- if (source.IsEmpty()) {
- return v8::ThrowException(v8::String::New("Error loading file"));
- }
- if (!ExecuteString(source, v8::String::New(*file), false, true)) {
- return v8::ThrowException(v8::String::New("Error executing file"));
- }
- }
- return v8::Undefined();
-}
static ev_async thread_pool_watcher;
@@ -212,49 +213,59 @@ node_eio_submit(eio_req *req)
int
main (int argc, char *argv[])
{
+ // start eio thread pool
+ ev_async_init(&thread_pool_watcher, thread_pool_cb);
+ eio_init(thread_pool_want_poll, NULL);
+
V8::SetFlagsFromCommandLine(&argc, argv, true);
- if(argc != 2) {
+ if(argc < 2) {
fprintf(stderr, "No script was specified.\n");
return 1;
}
-
string filename(argv[1]);
HandleScope handle_scope;
Persistent<Context> context = Context::New(NULL, ObjectTemplate::New());
Context::Scope context_scope(context);
+ V8::SetFatalErrorHandler(OnFatalError);
Local<Object> g = Context::GetCurrent()->Global();
- g->Set ( String::New("log")
- , FunctionTemplate::New(Log)->GetFunction()
- );
+ Local<Object> node = Object::New();
+ g->Set(String::New("node"), node);
- g->Set ( String::New("load")
- , FunctionTemplate::New(Load)->GetFunction()
- );
+ Local<Object> blocking = Object::New();
+ node->Set(String::New("blocking"), blocking);
- g->Set ( String::New("blockingFileRead")
- , FunctionTemplate::New(BlockingFileRead)->GetFunction()
- );
+ JS_SET_METHOD(blocking, "exec", exec);
+ JS_SET_METHOD(blocking, "cat", cat);
+ JS_SET_METHOD(blocking, "print", print);
+ Local<Array> arguments = Array::New(argc);
+ for (int i = 0; i < argc; i++) {
+ Local<String> arg = String::New(argv[i]);
+ arguments->Set(Integer::New(i), arg);
+ }
+ g->Set(String::New("ARGV"), arguments);
+
+ // BUILT-IN MODULES
Init_timer(g);
- Init_tcp(g);
+ //NodeInit_net(g);
+ NodeInit_process(g);
+ NodeInit_file(g);
Init_http(g);
- V8::SetFatalErrorHandler(OnFatalError);
-
- v8::Handle<v8::String> source = ReadFile(filename);
-
- // start eio thread pool
- ev_async_init(&thread_pool_watcher, thread_pool_cb);
- ev_async_start(EV_DEFAULT_ &thread_pool_watcher);
- eio_init(thread_pool_want_poll, NULL);
-
- ExecuteString(source, String::New(filename.c_str()), false, true);
+ // NATIVE JAVASCRIPT MODULES
+ TryCatch try_catch;
+ Handle<Value> result = ExecuteString(String::New(native_main),
+ String::New("main.js"));
+ if (try_catch.HasCaught()) {
+ ReportException(&try_catch);
+ return 1;
+ }
ev_loop(node_loop(), 0);
@@ -262,4 +273,3 @@ main (int argc, char *argv[])
return exit_code;
}
-
diff --git a/src/node.h b/src/node.h
index 3d075a9abd..3547a403d7 100644
--- a/src/node.h
+++ b/src/node.h
@@ -5,8 +5,15 @@
#include <eio.h>
#include <v8.h>
+#define JS_SYMBOL(name) v8::String::NewSymbol(name)
+#define JS_METHOD(name) v8::Handle<v8::Value> jsmethod_##name (const v8::Arguments& args)
+#define JS_SET_METHOD(obj, name, callback) \
+ obj->Set(JS_SYMBOL(name), v8::FunctionTemplate::New(jsmethod_##callback)->GetFunction())
+
+
void node_fatal_exception (v8::TryCatch &try_catch);
#define node_loop() ev_default_loop(0)
+void node_exit (int code);
// call this after creating a new eio event.
void node_eio_submit(eio_req *req);
diff --git a/src/node_tcp.cc b/src/node_tcp.cc
deleted file mode 100644
index febbe106fa..0000000000
--- a/src/node_tcp.cc
+++ /dev/null
@@ -1,341 +0,0 @@
-#include "node_tcp.h"
-#include "node.h"
-
-#include <oi_socket.h>
-#include <oi_buf.h>
-
-#include <assert.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-
-using namespace v8;
-
-static Persistent<String> readyState_str;
-
-static Persistent<Integer> readyState_CONNECTING;
-static Persistent<Integer> readyState_OPEN;
-static Persistent<Integer> readyState_CLOSED;
-
-enum readyState { READY_STATE_CONNECTING = 0
- , READY_STATE_OPEN = 1
- , READY_STATE_CLOSED = 2
- } ;
-
-class TCPClient {
-public:
- TCPClient(Handle<Object> obj);
- ~TCPClient();
-
- int Connect(char *host, char *port);
- void Write (Handle<Value> arg);
- void Disconnect();
-
- void OnOpen();
- void OnRead(const void *buf, size_t count);
- void OnClose();
-
-private:
- int ReadyState();
- oi_socket socket;
- struct addrinfo *address;
- Persistent<Object> js_client;
-};
-
-
-static void
-on_connect (oi_socket *socket)
-{
- TCPClient *client = static_cast<TCPClient*> (socket->data);
- client->OnOpen();
-}
-
-static void
-on_close (oi_socket *socket)
-{
- TCPClient *client = static_cast<TCPClient*> (socket->data);
- client->OnClose();
-}
-
-static void
-on_read (oi_socket *socket, const void *buf, size_t count)
-{
- TCPClient *client = static_cast<TCPClient*> (socket->data);
- client->OnRead(buf, count);
-}
-
-static struct addrinfo tcp_hints =
-/* ai_flags */ { AI_PASSIVE
-/* ai_family */ , AF_UNSPEC
-/* ai_socktype */ , SOCK_STREAM
-/* ai_protocol */ , 0
-/* ai_addrlen */ , 0
-/* ai_addr */ , 0
-/* ai_canonname */ , 0
-/* ai_next */ , 0
- };
-
-static Handle<Value> newTCPClient
- ( const Arguments& args
- )
-{
- if (args.Length() < 1)
- return Undefined();
-
- HandleScope scope;
-
- char *host = NULL;
- String::AsciiValue host_v(args[0]->ToString());
- if(args[0]->IsString()) {
- host = *host_v;
- }
- String::AsciiValue port(args[1]);
-
- TCPClient *client = new TCPClient(args.This());
- if(client == NULL)
- return Undefined(); // XXX raise error?
-
- int r = client->Connect(host, *port);
- if (r != 0)
- return Undefined(); // XXX raise error?
-
-
- return args.This();
-}
-
-static TCPClient*
-UnwrapClient (Handle<Object> obj)
-{
- HandleScope scope;
- Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
- TCPClient* client = static_cast<TCPClient*>(field->Value());
- return client;
-}
-
-static Handle<Value>
-WriteCallback (const Arguments& args)
-{
- HandleScope scope;
- TCPClient *client = UnwrapClient(args.Holder());
- client->Write(args[0]);
- return Undefined();
-}
-
-static Handle<Value>
-DisconnectCallback (const Arguments& args)
-{
- HandleScope scope;
- TCPClient *client = UnwrapClient(args.Holder());
- client->Disconnect();
- return Undefined();
-}
-
-static void
-client_destroy (Persistent<Value> _, void *data)
-{
- TCPClient *client = static_cast<TCPClient *> (data);
- delete client;
-}
-
-TCPClient::TCPClient(Handle<Object> _js_client)
-{
- oi_socket_init(&socket, 300.0); // TODO adjustable timeout
- socket.on_connect = on_connect;
- socket.on_read = on_read;
- socket.on_drain = NULL;
- socket.on_error = NULL;
- socket.on_close = on_close;
- socket.on_timeout = on_close;
- socket.data = this;
-
- HandleScope scope;
- js_client = Persistent<Object>::New(_js_client);
- js_client->SetInternalField (0, External::New(this));
- js_client.MakeWeak (this, client_destroy);
-}
-
-TCPClient::~TCPClient ()
-{
- Disconnect();
- oi_socket_detach (&socket);
- js_client.Dispose();
- js_client.Clear(); // necessary?
-}
-
-int
-TCPClient::Connect(char *host, char *port)
-{
- int r;
-
- HandleScope scope;
-
- js_client->Set(readyState_str, readyState_CONNECTING);
-
- /* FIXME Blocking DNS resolution. Use oi_async. */
- printf("resolving host: %s, port: %s\n", host, port);
- r = getaddrinfo (host, port, &tcp_hints, &address);
- if(r != 0) {
- perror("getaddrinfo");
- return r;
- }
-
- r = oi_socket_connect (&socket, address);
- if(r != 0) {
- perror("oi_socket_connect");
- return r;
- }
- oi_socket_attach (&socket, node_loop());
-
- freeaddrinfo(address);
- address = NULL;
-}
-
-void TCPClient::Write (Handle<Value> arg)
-{
- HandleScope scope;
-
- //
- // TODO if ReadyState() is not READY_STATE_OPEN then raise INVALID_STATE_ERR
- //
-
- if(arg == Null()) {
-
- oi_socket_write_eof(&socket);
-
- } else {
- Local<String> s = arg->ToString();
-
- size_t l1 = s->Utf8Length(), l2;
- oi_buf *buf = oi_buf_new2(l1);
- l2 = s->WriteUtf8(buf->base, l1);
- assert(l1 == l2);
-
- oi_socket_write(&socket, buf);
- }
-}
-
-int
-TCPClient::ReadyState()
-{
- return js_client->Get(readyState_str)->IntegerValue();
-}
-
-void
-TCPClient::Disconnect()
-{
- oi_socket_close(&socket);
-}
-
-void
-TCPClient::OnOpen()
-{
- HandleScope scope;
-
- assert(READY_STATE_CONNECTING == ReadyState());
- js_client->Set(readyState_str, readyState_OPEN);
-
- Handle<Value> onopen_value = js_client->Get( String::NewSymbol("onopen") );
- if (!onopen_value->IsFunction())
- return;
- Handle<Function> onopen = Handle<Function>::Cast(onopen_value);
-
- TryCatch try_catch;
-
- Handle<Value> r = onopen->Call(js_client, 0, NULL);
-
- if(try_catch.HasCaught())
- node_fatal_exception(try_catch);
-}
-
-void
-TCPClient::OnRead(const void *buf, size_t count)
-{
- HandleScope scope;
-
- assert(READY_STATE_OPEN == ReadyState());
-
- Handle<Value> onread_value = js_client->Get( String::NewSymbol("onread") );
- if (!onread_value->IsFunction()) return;
- Handle<Function> onread = Handle<Function>::Cast(onread_value);
-
- const int argc = 1;
- Handle<Value> argv[argc];
-
- if(count) {
- Handle<String> chunk = String::New((const char*)buf, count); // TODO binary data?
- argv[0] = chunk;
- } else {
- // TODO eof? delete write method?
- argv[0] = Null();
- }
-
- TryCatch try_catch;
-
- Handle<Value> r = onread->Call(js_client, argc, argv);
-
- if(try_catch.HasCaught())
- node_fatal_exception(try_catch);
-}
-
-void
-TCPClient::OnClose()
-{
- HandleScope scope;
-
- printf("onclose readyState %d\n", ReadyState());
-
- assert(READY_STATE_OPEN == ReadyState());
- js_client->Set(readyState_str, readyState_CLOSED);
-
- Handle<Value> onclose_value = js_client->Get( String::NewSymbol("onclose") );
- if (!onclose_value->IsFunction()) return;
- Handle<Function> onclose = Handle<Function>::Cast(onclose_value);
-
- TryCatch try_catch;
-
- Handle<Value> r = onclose->Call(js_client, 0, NULL);
-
- if(try_catch.HasCaught())
- node_fatal_exception(try_catch);
-}
-
-void
-Init_tcp (Handle<Object> target)
-{
- HandleScope scope;
- readyState_str = Persistent<String>::New(String::NewSymbol("readyState"));
-
- Local<FunctionTemplate> client_t = FunctionTemplate::New(newTCPClient);
-
- client_t->InstanceTemplate()->SetInternalFieldCount(1);
-
- /* readyState constants */
-
- readyState_CONNECTING = Persistent<Integer>::New(Integer::New(READY_STATE_CONNECTING));
- client_t->Set ("CONNECTING", readyState_CONNECTING);
-
- readyState_OPEN = Persistent<Integer>::New(Integer::New(READY_STATE_OPEN));
- client_t->Set ("OPEN", readyState_OPEN);
-
- readyState_CLOSED = Persistent<Integer>::New(Integer::New(READY_STATE_CLOSED));
- client_t->Set ("CLOSED", readyState_CLOSED);
-
- /* write callback */
-
- Local<FunctionTemplate> write_t = FunctionTemplate::New(WriteCallback);
-
- client_t->InstanceTemplate()->Set ( String::NewSymbol("write")
- , write_t->GetFunction()
- );
-
- /* disconnect callback */
-
- Local<FunctionTemplate> disconnect_t = FunctionTemplate::New(DisconnectCallback);
-
- client_t->InstanceTemplate()->Set ( String::NewSymbol("disconnect")
- , disconnect_t->GetFunction()
- );
-
- target->Set(String::NewSymbol("TCPClient"), client_t->GetFunction());
-}
-
diff --git a/src/node_tcp.h b/src/node_tcp.h
deleted file mode 100644
index 4193e160fc..0000000000
--- a/src/node_tcp.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef node_tcp_h
-#define node_tcp_h
-
-#include <v8.h>
-
-void Init_tcp (v8::Handle<v8::Object> target);
-
-#endif
diff --git a/src/process.cc b/src/process.cc
new file mode 100644
index 0000000000..db18ad29d7
--- /dev/null
+++ b/src/process.cc
@@ -0,0 +1,39 @@
+#include "process.h"
+#include "node.h"
+#include <v8.h>
+#include <stdlib.h>
+
+using namespace v8;
+
+static Handle<Value>
+ExitCallback (const Arguments& args)
+{
+ int exit_code = 0;
+ if (args.Length() > 0) exit_code = args[0]->IntegerValue();
+ exit(exit_code);
+ return Undefined();
+}
+
+static Handle<Value>
+OnCallback (const Arguments& args)
+{
+ return Undefined();
+}
+
+void
+NodeInit_process (Handle<Object> target)
+{
+ HandleScope scope;
+
+ Local<Object> process = ObjectTemplate::New()->NewInstance();
+
+ target->Set(String::NewSymbol("process"), process);
+
+ // process.exit()
+ Local<FunctionTemplate> process_exit = FunctionTemplate::New(ExitCallback);
+ process->Set(String::NewSymbol("exit"), process_exit->GetFunction());
+
+ // process.on()
+ Local<FunctionTemplate> process_on = FunctionTemplate::New(OnCallback);
+ process->Set(String::NewSymbol("on"), process_exit->GetFunction());
+}
diff --git a/src/process.h b/src/process.h
new file mode 100644
index 0000000000..b7ea663ed2
--- /dev/null
+++ b/src/process.h
@@ -0,0 +1,8 @@
+#ifndef node_process_h
+#define node_process_h
+
+#include <v8.h>
+
+void NodeInit_process (v8::Handle<v8::Object> target);
+
+#endif
diff --git a/test/mjsunit.js b/test/mjsunit.js
new file mode 100644
index 0000000000..7c5e38dbfb
--- /dev/null
+++ b/test/mjsunit.js
@@ -0,0 +1,165 @@
+// Copyright 2008 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+//
+
+/*jslint
+ evil: true
+*/
+
+function MjsUnitAssertionError(message) {
+ this.message = message;
+}
+
+MjsUnitAssertionError.prototype.toString = function () {
+ return this.message;
+};
+
+/*
+ * This file is included in all mini jsunit test cases. The test
+ * framework expects lines that signal failed tests to start with
+ * the f-word and ignore all other lines.
+ */
+
+function fail (expected, found, name_opt) {
+ var start;
+ if (name_opt) {
+ // Fix this when we ditch the old test runner.
+ start = "Fail" + "ure (" + name_opt + "): ";
+ } else {
+ start = "Fail" + "ure:";
+ }
+ throw new MjsUnitAssertionError(start + " expected <" + expected + "> found <" + found + ">");
+};
+
+
+function deepEquals (a, b) {
+ if (a == b) {
+ return true;
+ }
+ if ((typeof a) !== 'object' || (typeof b) !== 'object' ||
+ (a === null) || (b === null)) {
+ return false;
+ }
+ if (a.constructor === Array) {
+ if (b.constructor !== Array) {
+ return false;
+ }
+ if (a.length != b.length) {
+ return false;
+ }
+ for (var i = 0; i < a.length; i++) {
+ if (i in a) {
+ if (!(i in b) || !(deepEquals(a[i], b[i]))) {
+ return false;
+ }
+ } else if (i in b) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+};
+
+
+exports.assertEquals = function (expected, found, name_opt) {
+ if (!deepEquals(found, expected)) {
+ fail(expected, found, name_opt);
+ }
+};
+
+
+exports.assertArrayEquals = function (expected, found, name_opt) {
+ var start = "";
+ if (name_opt) {
+ start = name_opt + " - ";
+ }
+ exports.assertEquals(expected.length, found.length, start + "array length");
+ if (expected.length == found.length) {
+ for (var i = 0; i < expected.length; ++i) {
+ exports.assertEquals(expected[i], found[i], start + "array element at index " + i);
+ }
+ }
+};
+
+
+exports.assertTrue = function (value, name_opt) {
+ exports.assertEquals(true, value, name_opt);
+};
+
+
+exports.assertFalse = function (value, name_opt) {
+ exports.assertEquals(false, value, name_opt);
+};
+
+
+exports.assertNaN = function (value, name_opt) {
+ if (!isNaN(value)) {
+ fail("NaN", value, name_opt);
+ }
+};
+
+
+exports.assertThrows = function (code) {
+ var threwException = true;
+ try {
+ eval(code);
+ threwException = false;
+ } catch (e) {
+ // Do nothing.
+ }
+ if (!threwException) {
+ exports.assertTrue(false, "did not throw exception");
+ }
+};
+
+
+exports.assertInstanceof = function (obj, type) {
+ if (!(obj instanceof type)) {
+ exports.assertTrue(false, "Object <" + obj + "> is not an instance of <" + type + ">");
+ }
+};
+
+
+exports.assertDoesNotThrow = function (code) {
+ try {
+ eval(code);
+ } catch (e) {
+ exports.assertTrue(false, "threw an exception");
+ }
+};
+
+
+exports.assertUnreachable = function (name_opt) {
+ // Fix this when we ditch the old test runner.
+ var message = "Fail" + "ure: unreachable";
+ if (name_opt) {
+ message += " - " + name_opt;
+ }
+ throw new MjsUnitAssertionError(message);
+};
diff --git a/test/test-test.js b/test/test-test.js
new file mode 100644
index 0000000000..18a364f416
--- /dev/null
+++ b/test/test-test.js
@@ -0,0 +1,12 @@
+node.blocking.print(__file__);
+/*
+if (node.path.dirname(__file__) !== "test-test.js") {
+ throw "wrong __file__ argument";
+}
+*/
+
+var mjsunit = require("./mjsunit.js");
+node.blocking.print(__file__);
+
+mjsunit.assertFalse(false, "testing the test program.");
+//mjsunit.assertEquals("test-test.js", __file__);
diff --git a/wscript b/wscript
index 361ecffe06..e422cfa0c0 100644
--- a/wscript
+++ b/wscript
@@ -1,8 +1,11 @@
#! /usr/bin/env python
import Options
+import sys
import os
from os.path import join, dirname, abspath
+import js2c
+
VERSION='0.0.1'
APPNAME='node'
@@ -13,12 +16,12 @@ def set_options(opt):
# the gcc module provides a --debug-level option
opt.tool_options('compiler_cxx')
opt.tool_options('compiler_cc')
- opt.tool_options('ragel', tdir = '.')
+ opt.tool_options('ragel', tdir=".")
def configure(conf):
conf.check_tool('compiler_cxx')
conf.check_tool('compiler_cc')
- conf.check_tool('ragel', tooldir = '.')
+ conf.check_tool('ragel', tooldir=".")
conf.sub_config('deps/libeio')
conf.sub_config('deps/libev')
@@ -42,6 +45,7 @@ def configure(conf):
conf.define("HAVE_CONFIG_H", 1)
conf.write_config_header('config.h')
+
def build(bld):
bld.add_subdirs('deps/libeio deps/libev')
@@ -53,7 +57,7 @@ def build(bld):
v8lib = bld.env["staticlib_PATTERN"] % "v8"
v8 = bld.new_task_gen(
target=join("deps/v8",v8lib),
- rule='cp -rf %s %s && cd %s && scons library=static'
+ rule='cp -rf %s %s && cd %s && scons -Q library=static snapshot=on'
% ( v8dir_src
, deps_tgt
, v8dir_tgt
@@ -80,13 +84,29 @@ def build(bld):
ebb.name = "ebb"
ebb.target = "ebb"
+ ### src/native.cc
+ def javascript_in_c(task):
+ env = task.env
+ source = map(lambda x: x.srcpath(env), task.inputs)
+ targets = map(lambda x: x.srcpath(env), task.outputs)
+ js2c.JS2C(source, targets)
+
+ native_cc = bld.new_task_gen(
+ source="src/main.js",
+ target="src/natives.h",
+ rule=javascript_in_c,
+ before="cxx"
+ )
+
+
### node
node = bld.new_task_gen("cxx", "program")
node.target = 'node'
node.source = """
src/node.cc
src/node_http.cc
- src/node_tcp.cc
+ src/process.cc
+ src/file.cc
src/node_timer.cc
"""
node.includes = """