Browse Source

everything is changed. i've waited much too long to commit.

this is awful. i'm sorry for being so messy.
v0.7.4-release
Ryan 16 years ago
parent
commit
63a9cd3897
  1. 137
      configure
  2. 317
      js2c.py
  3. 218
      jsmin.py
  4. 153
      src/file.cc
  5. 8
      src/file.h
  6. 85
      src/main.js
  7. 425
      src/net.cc
  8. 8
      src/net.h
  9. 296
      src/node.cc
  10. 7
      src/node.h
  11. 341
      src/node_tcp.cc
  12. 8
      src/node_tcp.h
  13. 39
      src/process.cc
  14. 8
      src/process.h
  15. 165
      test/mjsunit.js
  16. 12
      test/test-test.js
  17. 28
      wscript

137
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 $?

317
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()

218
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)

153
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);
}

8
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

85
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());
});

425
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());
}

8
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

296
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);
}
int r = fseek(file, 0, SEEK_END);
if (r < 0) {
perror("fseek()");
return ThrowException(error_msg);
}
fseek(file, 0, SEEK_END);
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();
HandleScope scope;
Handle<Value> arg = args[0];
String::Utf8Value value(arg);
printf("%s\n", *value);
fflush(stdout);
if (args.Length() < 2)
return Undefined();
return Undefined();
}
HandleScope scope;
Local<String> source = args[0]->ToString();
Local<String> filename = args[1]->ToString();
static Handle<Value>
BlockingFileRead (const Arguments& args)
{
if (args.Length() < 1) return v8::Undefined();
HandleScope scope;
Handle<Value> result = ExecuteString(source, filename);
String::Utf8Value filename(args[0]);
Handle<String> output = ReadFile (*filename);
return scope.Close(output);
return scope.Close(result);
}
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;
}

7
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);

341
src/node_tcp.cc

@ -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());
}

8
src/node_tcp.h

@ -1,8 +0,0 @@
#ifndef node_tcp_h
#define node_tcp_h
#include <v8.h>
void Init_tcp (v8::Handle<v8::Object> target);
#endif

39
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());
}

8
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

165
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);
};

12
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__);

28
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 = """

Loading…
Cancel
Save