everything is changed. i've waited much too long to commit.
authorRyan <ry@tinyclouds.org>
Wed, 15 Apr 2009 08:08:28 +0000 (10:08 +0200)
committerRyan <ry@tinyclouds.org>
Wed, 15 Apr 2009 08:08:28 +0000 (10:08 +0200)
this is awful. i'm sorry for being so messy.

17 files changed:
configure [new file with mode: 0755]
js2c.py [new file with mode: 0755]
jsmin.py [new file with mode: 0644]
src/file.cc [new file with mode: 0644]
src/file.h [new file with mode: 0644]
src/main.js [new file with mode: 0644]
src/net.cc [new file with mode: 0644]
src/net.h [new file with mode: 0644]
src/node.cc
src/node.h
src/node_tcp.cc [deleted file]
src/node_tcp.h [deleted file]
src/process.cc [new file with mode: 0644]
src/process.h [new file with mode: 0644]
test/mjsunit.js [new file with mode: 0644]
test/test-test.js [new file with mode: 0644]
wscript

diff --git a/configure b/configure
new file mode 100755 (executable)
index 0000000..0187691
--- /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 (executable)
index 0000000..3126261
--- /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 (file)
index 0000000..ae75814
--- /dev/null
+++ b/jsmin.py
@@ -0,0 +1,218 @@
+#!/usr/bin/python\r
+\r
+# This code is original from jsmin by Douglas Crockford, it was translated to\r
+# Python by Baruch Even. The original code had the following copyright and\r
+# license.\r
+#\r
+# /* jsmin.c\r
+#    2007-05-22\r
+#\r
+# Copyright (c) 2002 Douglas Crockford  (www.crockford.com)\r
+#\r
+# Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+# this software and associated documentation files (the "Software"), to deal in\r
+# the Software without restriction, including without limitation the rights to\r
+# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\r
+# of the Software, and to permit persons to whom the Software is furnished to do\r
+# so, subject to the following conditions:\r
+#\r
+# The above copyright notice and this permission notice shall be included in all\r
+# copies or substantial portions of the Software.\r
+#\r
+# The Software shall be used for Good, not Evil.\r
+#\r
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
+# SOFTWARE.\r
+# */\r
+\r
+from StringIO import StringIO\r
+\r
+def jsmin(js):\r
+    ins = StringIO(js)\r
+    outs = StringIO()\r
+    JavascriptMinify().minify(ins, outs)\r
+    str = outs.getvalue()\r
+    if len(str) > 0 and str[0] == '\n':\r
+        str = str[1:]\r
+    return str\r
+\r
+def isAlphanum(c):\r
+    """return true if the character is a letter, digit, underscore,\r
+           dollar sign, or non-ASCII character.\r
+    """\r
+    return ((c >= 'a' and c <= 'z') or (c >= '0' and c <= '9') or\r
+            (c >= 'A' and c <= 'Z') or c == '_' or c == '$' or c == '\\' or (c is not None and ord(c) > 126));\r
+\r
+class UnterminatedComment(Exception):\r
+    pass\r
+\r
+class UnterminatedStringLiteral(Exception):\r
+    pass\r
+\r
+class UnterminatedRegularExpression(Exception):\r
+    pass\r
+\r
+class JavascriptMinify(object):\r
+\r
+    def _outA(self):\r
+        self.outstream.write(self.theA)\r
+    def _outB(self):\r
+        self.outstream.write(self.theB)\r
+\r
+    def _get(self):\r
+        """return the next character from stdin. Watch out for lookahead. If\r
+           the character is a control character, translate it to a space or\r
+           linefeed.\r
+        """\r
+        c = self.theLookahead\r
+        self.theLookahead = None\r
+        if c == None:\r
+            c = self.instream.read(1)\r
+        if c >= ' ' or c == '\n':\r
+            return c\r
+        if c == '': # EOF\r
+            return '\000'\r
+        if c == '\r':\r
+            return '\n'\r
+        return ' '\r
+\r
+    def _peek(self):\r
+        self.theLookahead = self._get()\r
+        return self.theLookahead\r
+\r
+    def _next(self):\r
+        """get the next character, excluding comments. peek() is used to see\r
+           if an unescaped '/' is followed by a '/' or '*'.\r
+        """\r
+        c = self._get()\r
+        if c == '/' and self.theA != '\\':\r
+            p = self._peek()\r
+            if p == '/':\r
+                c = self._get()\r
+                while c > '\n':\r
+                    c = self._get()\r
+                return c\r
+            if p == '*':\r
+                c = self._get()\r
+                while 1:\r
+                    c = self._get()\r
+                    if c == '*':\r
+                        if self._peek() == '/':\r
+                            self._get()\r
+                            return ' '\r
+                    if c == '\000':\r
+                        raise UnterminatedComment()\r
+\r
+        return c\r
+\r
+    def _action(self, action):\r
+        """do something! What you do is determined by the argument:\r
+           1   Output A. Copy B to A. Get the next B.\r
+           2   Copy B to A. Get the next B. (Delete A).\r
+           3   Get the next B. (Delete B).\r
+           action treats a string as a single character. Wow!\r
+           action recognizes a regular expression if it is preceded by ( or , or =.\r
+        """\r
+        if action <= 1:\r
+            self._outA()\r
+\r
+        if action <= 2:\r
+            self.theA = self.theB\r
+            if self.theA == "'" or self.theA == '"':\r
+                while 1:\r
+                    self._outA()\r
+                    self.theA = self._get()\r
+                    if self.theA == self.theB:\r
+                        break\r
+                    if self.theA <= '\n':\r
+                        raise UnterminatedStringLiteral()\r
+                    if self.theA == '\\':\r
+                        self._outA()\r
+                        self.theA = self._get()\r
+\r
+\r
+        if action <= 3:\r
+            self.theB = self._next()\r
+            if self.theB == '/' and (self.theA == '(' or self.theA == ',' or\r
+                                     self.theA == '=' or self.theA == ':' or\r
+                                     self.theA == '[' or self.theA == '?' or\r
+                                     self.theA == '!' or self.theA == '&' or\r
+                                     self.theA == '|' or self.theA == ';' or\r
+                                     self.theA == '{' or self.theA == '}' or\r
+                                     self.theA == '\n'):\r
+                self._outA()\r
+                self._outB()\r
+                while 1:\r
+                    self.theA = self._get()\r
+                    if self.theA == '/':\r
+                        break\r
+                    elif self.theA == '\\':\r
+                        self._outA()\r
+                        self.theA = self._get()\r
+                    elif self.theA <= '\n':\r
+                        raise UnterminatedRegularExpression()\r
+                    self._outA()\r
+                self.theB = self._next()\r
+\r
+\r
+    def _jsmin(self):\r
+        """Copy the input to the output, deleting the characters which are\r
+           insignificant to JavaScript. Comments will be removed. Tabs will be\r
+           replaced with spaces. Carriage returns will be replaced with linefeeds.\r
+           Most spaces and linefeeds will be removed.\r
+        """\r
+        self.theA = '\n'\r
+        self._action(3)\r
+\r
+        while self.theA != '\000':\r
+            if self.theA == ' ':\r
+                if isAlphanum(self.theB):\r
+                    self._action(1)\r
+                else:\r
+                    self._action(2)\r
+            elif self.theA == '\n':\r
+                if self.theB in ['{', '[', '(', '+', '-']:\r
+                    self._action(1)\r
+                elif self.theB == ' ':\r
+                    self._action(3)\r
+                else:\r
+                    if isAlphanum(self.theB):\r
+                        self._action(1)\r
+                    else:\r
+                        self._action(2)\r
+            else:\r
+                if self.theB == ' ':\r
+                    if isAlphanum(self.theA):\r
+                        self._action(1)\r
+                    else:\r
+                        self._action(3)\r
+                elif self.theB == '\n':\r
+                    if self.theA in ['}', ']', ')', '+', '-', '"', '\'']:\r
+                        self._action(1)\r
+                    else:\r
+                        if isAlphanum(self.theA):\r
+                            self._action(1)\r
+                        else:\r
+                            self._action(3)\r
+                else:\r
+                    self._action(1)\r
+\r
+    def minify(self, instream, outstream):\r
+        self.instream = instream\r
+        self.outstream = outstream\r
+        self.theA = '\n'\r
+        self.theB = None\r
+        self.theLookahead = None\r
+\r
+        self._jsmin()\r
+        self.instream.close()\r
+\r
+if __name__ == '__main__':\r
+    import sys\r
+    jsm = JavascriptMinify()\r
+    jsm.minify(sys.stdin, sys.stdout)\r
diff --git a/src/file.cc b/src/file.cc
new file mode 100644 (file)
index 0000000..6638f07
--- /dev/null
@@ -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 (file)
index 0000000..75fddb7
--- /dev/null
@@ -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 (file)
index 0000000..ae847e7
--- /dev/null
@@ -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 (file)
index 0000000..b381ed9
--- /dev/null
@@ -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 (file)
index 0000000..347a3eb
--- /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
index e89b88c..07d31b2 100644 (file)
@@ -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>
 
 #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;
 }
-
index 3d075a9..3547a40 100644 (file)
@@ -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 (file)
index febbe10..0000000
+++ /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 (file)
index 4193e16..0000000
+++ /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 (file)
index 0000000..db18ad2
--- /dev/null
@@ -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 (file)
index 0000000..b7ea663
--- /dev/null
@@ -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 (file)
index 0000000..7c5e38d
--- /dev/null
@@ -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 (file)
index 0000000..18a364f
--- /dev/null
@@ -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 361ecff..e422cfa 100644 (file)
--- 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 = """