changeset 167:4c0131c2b650

merge luan/lib into modules git-svn-id: https://luan-java.googlecode.com/svn/trunk@168 21e917c8-12df-6dd8-5cb6-c86387c605b9
author fschmidt@gmail.com <fschmidt@gmail.com@21e917c8-12df-6dd8-5cb6-c86387c605b9>
date Sun, 22 Jun 2014 04:28:32 +0000
parents 4eaee12f6c65
children ebe9db183eb7
files src/luan/Luan.java src/luan/LuanState.java src/luan/init.luan src/luan/lib/BasicLib.java src/luan/lib/BinaryLib.java src/luan/lib/HtmlLib.java src/luan/lib/HttpLib.java src/luan/lib/IoLib.java src/luan/lib/JavaLib.java src/luan/lib/MathLib.java src/luan/lib/PackageLib.java src/luan/lib/PickleClient.java src/luan/lib/PickleCon.java src/luan/lib/PickleServer.java src/luan/lib/StringLib.java src/luan/lib/TableLib.java src/luan/lib/ThreadLib.java src/luan/lib/Utils.java src/luan/lib/init.luan src/luan/modules/BasicLib.java src/luan/modules/BinaryLib.java src/luan/modules/HtmlLib.java src/luan/modules/HttpLib.java src/luan/modules/IoLib.java src/luan/modules/JavaLib.java src/luan/modules/MathLib.java src/luan/modules/PackageLib.java src/luan/modules/PickleClient.java src/luan/modules/PickleCon.java src/luan/modules/PickleServer.java src/luan/modules/StringLib.java src/luan/modules/TableLib.java src/luan/modules/ThreadLib.java src/luan/modules/Utils.java
diffstat 34 files changed, 2861 insertions(+), 2870 deletions(-) [+]
line wrap: on
line diff
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/Luan.java
--- a/src/luan/Luan.java	Sun Jun 22 04:17:38 2014 +0000
+++ b/src/luan/Luan.java	Sun Jun 22 04:28:32 2014 +0000
@@ -1,6 +1,6 @@
 package luan;
 
-import luan.lib.BasicLib;
+import luan.modules.BasicLib;
 
 
 public final class Luan {
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/LuanState.java
--- a/src/luan/LuanState.java	Sun Jun 22 04:17:38 2014 +0000
+++ b/src/luan/LuanState.java	Sun Jun 22 04:28:32 2014 +0000
@@ -7,17 +7,8 @@
 import java.util.Map;
 import java.util.LinkedHashMap;
 import luan.impl.LuanCompiler;
-import luan.lib.BasicLib;
-import luan.lib.PackageLib;
-import luan.lib.JavaLib;
-import luan.lib.MathLib;
-import luan.lib.StringLib;
-import luan.lib.TableLib;
-import luan.lib.HtmlLib;
-import luan.lib.BinaryLib;
-import luan.lib.IoLib;
-import luan.lib.ThreadLib;
-import luan.lib.HttpLib;
+import luan.modules.BasicLib;
+import luan.modules.PackageLib;
 
 
 public abstract class LuanState implements DeepCloneable<LuanState> {
@@ -141,7 +132,7 @@
 		try {
 			LuanState luan = LuanCompiler.newLuanState();
 			luan.globalImport("Package");
-			BasicLib.do_file(luan,"java:luan/lib/init.luan");
+			BasicLib.do_file(luan,"java:luan/init.luan");
 			return luan;
 		} catch(LuanException e) {
 			throw new RuntimeException(e);
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/init.luan
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/init.luan	Sun Jun 22 04:28:32 2014 +0000
@@ -0,0 +1,107 @@
+function Package.global(module,fn_name)
+	local function fn(...)
+		return module[fn_name](...)
+	end
+	_G[fn_name] = fn
+	return fn
+end
+
+local require = Package.global(Package,"require")
+
+function Package.global_import(name)
+	local mod = require(name)
+	_G[name] = mod
+	return mod
+end
+
+local Basic = Package.global_import("Basic","luan.lib.BasicLib.LOADER")
+Package.global(Basic,"assert")
+Package.global(Basic,"assert_boolean")
+Package.global(Basic,"assert_nil")
+Package.global(Basic,"assert_number")
+Package.global(Basic,"assert_string")
+Package.global(Basic,"assert_table")
+Package.global(Basic,"do_file")
+Package.global(Basic,"error")
+Package.global(Basic,"get_metatable")
+Package.global(Basic,"ipairs")
+local load = Package.global(Basic,"load")
+Package.global(Basic,"load_file")
+Package.global(Basic,"pairs")
+Package.global(Basic,"range")
+Package.global(Basic,"raw_equal")
+Package.global(Basic,"raw_get")
+Package.global(Basic,"raw_len")
+Package.global(Basic,"raw_set")
+Package.global(Basic,"repr")
+Package.global(Basic,"set_metatable")
+Package.global(Basic,"to_number")
+local to_string = Package.global(Basic,"to_string")
+Package.global(Basic,"type")
+
+local String = Package.global_import("String","luan.lib.StringLib.LOADER")
+
+-- improved global_import
+function Package.global_import(name)
+	local short = name.match("\.([^.]+)$") or name
+	local mod = require(name)
+	_G[short] = mod
+	return mod
+end
+
+local Table = Package.global_import("Table","luan.lib.TableLib.LOADER")
+local Io = Package.global_import("Io","luan.lib.IoLib.LOADER")
+Package.global_import("Math","luan.lib.MathLib.LOADER")
+Package.global_import("Html","luan.lib.HtmlLib.LOADER")
+Package.global_import("Thread","luan.lib.ThreadLib.LOADER")
+Package.global_import("Binary","luan.lib.BinaryLib.LOADER")
+
+
+function Io.print_to(out,...)
+	local list = {}
+	for _,v in Basic.values(...) do
+		list[#list+1] = to_string(v)
+		list[#list+1] = '\t'
+	end
+	if #list == 0 then
+		out.write( '\n' )
+	else
+		list[#list] = '\n'
+		out.write( Table.unpack(list) )
+	end
+end
+
+function Basic.print(...)
+	Io.print_to(Io.stdout,...)
+end
+local print = Package.global(Basic,"print")
+
+local Debug = {}
+Package.loaded.Debug = Debug
+_G.Debug = Debug
+
+function Debug.print_if_something(...)
+	if Table.pack(...).n > 0 then
+		print(...)
+	end
+end
+
+function Debug.debug(prompt)
+	prompt = prompt or "luan_debug> "
+	local function console()
+		return Io.read_console_line(prompt)
+	end
+	local env = {}
+	for line in console do
+		try
+			local fn = load(line,"stdin",env,true)
+			Debug.print_if_something( fn() )
+		catch e do
+			print(e)
+		end
+	end
+end
+
+
+-- import modules
+Package.global_import("Reactionary")
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/lib/BasicLib.java
--- a/src/luan/lib/BasicLib.java	Sun Jun 22 04:17:38 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,222 +0,0 @@
-package luan.lib;
-
-import java.io.File;
-import java.io.InputStreamReader;
-import java.io.IOException;
-import java.lang.reflect.Method;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.List;
-import java.util.ArrayList;
-import luan.Luan;
-import luan.LuanState;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanException;
-import luan.LuanSource;
-import luan.LuanElement;
-import luan.impl.LuanCompiler;
-
-
-public final class BasicLib {
-
-	public static final LuanFunction LOADER = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
-			try {
-				module.put( "assert", new LuanJavaFunction(BasicLib.class.getMethod("assert_",LuanState.class,Object.class,String.class),null) );
-				add( module, "assert_boolean", LuanState.class, Boolean.TYPE );
-				add( module, "assert_nil", LuanState.class, Object.class );
-				add( module, "assert_number", LuanState.class, Number.class );
-				add( module, "assert_string", LuanState.class, String.class );
-				add( module, "assert_table", LuanState.class, LuanTable.class );
-				add( module, "do_file", LuanState.class, String.class );
-				add( module, "error", LuanState.class, Object.class );
-				add( module, "get_metatable", LuanState.class, Object.class );
-				add( module, "ipairs", LuanState.class, LuanTable.class );
-				add( module, "load", LuanState.class, String.class, String.class, LuanTable.class, Boolean.class );
-				add( module, "load_file", LuanState.class, String.class );
-				add( module, "pairs", LuanState.class, LuanTable.class );
-				add( module, "range", LuanState.class, Double.TYPE, Double.TYPE, Double.class );
-				add( module, "raw_equal", Object.class, Object.class );
-				add( module, "raw_get", LuanTable.class, Object.class );
-				add( module, "raw_len", LuanState.class, Object.class );
-				add( module, "raw_set", LuanTable.class, Object.class, Object.class );
-				add( module, "repr", LuanState.class, Object.class );
-				add( module, "set_metatable", LuanTable.class, LuanTable.class );
-				add( module, "to_number", Object.class, Integer.class );
-				add( module, "to_string", LuanState.class, Object.class );
-				add( module, "type", Object.class );
-				add( module, "values", new Object[0].getClass() );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			return module;
-		}
-	};
-
-	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
-		t.put( method, new LuanJavaFunction(BasicLib.class.getMethod(method,parameterTypes),null) );
-	}
-
-	public static String type(Object obj) {
-		return Luan.type(obj);
-	}
-
-	public static LuanFunction load(LuanState luan,String text,String sourceName,LuanTable env,Boolean allowExpr)
-		throws LuanException
-	{
-		if( allowExpr==null )
-			allowExpr = false;
-		return LuanCompiler.compile(luan,new LuanSource(sourceName,text),env,allowExpr);
-	}
-
-	public static LuanFunction load_file(LuanState luan,String fileName) throws LuanException {
-		try {
-			String src = fileName==null ? Utils.readAll(new InputStreamReader(System.in)) : IoLib.luanIo(luan,fileName).read_text();
-			return load(luan,src,fileName,null,false);
-		} catch(IOException e) {
-			throw luan.exception(e);
-		}
-	}
-
-	public static Object do_file(LuanState luan,String fileName) throws LuanException {
-		LuanFunction fn = load_file(luan,fileName);
-		return luan.call(fn);
-	}
-
-	private static LuanFunction pairs(final Iterator<Map.Entry<Object,Object>> iter) {
-		return new LuanFunction() {
-			@Override public Object[] call(LuanState luan,Object[] args) {
-				if( !iter.hasNext() )
-					return LuanFunction.NOTHING;
-				Map.Entry<Object,Object> entry = iter.next();
-				return new Object[]{entry.getKey(),entry.getValue()};
-			}
-		};
-	}
-
-	public static LuanFunction pairs(LuanState luan,LuanTable t) throws LuanException {
-		Utils.checkNotNull(luan,t,"table");
-		return pairs( t.iterator() );
-	}
-
-	public static LuanFunction ipairs(LuanState luan,LuanTable t) throws LuanException {
-		Utils.checkNotNull(luan,t,"table");
-		return pairs( t.listIterator() );
-	}
-
-	public static LuanTable get_metatable(LuanState luan,Object obj) {
-		return luan.getMetatable(obj);
-	}
-
-	public static LuanTable set_metatable(LuanTable table,LuanTable metatable) {
-		table.setMetatable(metatable);
-		return table;
-	}
-
-	public static boolean raw_equal(Object v1,Object v2) {
-		return v1 == v2 || v1 != null && v1.equals(v2);
-	}
-
-	public static Object raw_get(LuanTable table,Object index) {
-		return table.get(index);
-	}
-
-	public static LuanTable raw_set(LuanTable table,Object index,Object value) {
-		table.put(index,value);
-		return table;
-	}
-
-	public static int raw_len(LuanState luan,Object v) throws LuanException {
-		if( v instanceof String ) {
-			String s = (String)v;
-			return s.length();
-		}
-		if( v instanceof LuanTable ) {
-			LuanTable t = (LuanTable)v;
-			return t.length();
-		}
-		throw luan.exception( "bad argument #1 to 'raw_len' (table or string expected)" );
-	}
-
-	public static Number to_number(Object e,Integer base) {
-		return Luan.toNumber(e,base);
-	}
-
-	public static String to_string(LuanState luan,Object v) throws LuanException {
-		return luan.toString(v);
-	}
-
-	public static void error(LuanState luan,Object msg) throws LuanException {
-		throw luan.exception(msg);
-	}
-
-	public static Object assert_(LuanState luan,Object v,String msg) throws LuanException {
-		if( Luan.toBoolean(v) )
-			return v;
-		if( msg == null )
-			msg = "assertion failed!";
-		throw luan.exception( msg );
-	}
-
-	public static String assert_string(LuanState luan,String v) throws LuanException {
-		Utils.checkNotNull(luan,v,"string");
-		return v;
-	}
-
-	public static Number assert_number(LuanState luan,Number v) throws LuanException {
-		Utils.checkNotNull(luan,v,"number");
-		return v;
-	}
-
-	public static LuanTable assert_table(LuanState luan,LuanTable v) throws LuanException {
-		Utils.checkNotNull(luan,v,"table");
-		return v;
-	}
-
-	public static boolean assert_boolean(LuanState luan,boolean v) throws LuanException {
-		return v;
-	}
-
-	public static Object assert_nil(LuanState luan,Object v) throws LuanException {
-		if( v != null )
-			throw luan.exception("bad argument #1 (nil expected, got "+Luan.type(v)+")");
-		return v;
-	}
-
-	public static String repr(LuanState luan,Object v) throws LuanException {
-		return luan.repr(v);
-	}
-
-	public static LuanFunction range(LuanState luan,final double from,final double to,Double stepV) throws LuanException {
-		final double step = stepV==null ? 1.0 : stepV;
-		if( step == 0.0 )
-			throw luan.exception("bad argument #3 (step may not be zero)");
-		return new LuanFunction() {
-			double v = from;
-
-			@Override public Object call(LuanState luan,Object[] args) {
-				if( step > 0.0 && v > to || step < 0.0 && v < to )
-					return LuanFunction.NOTHING;
-				double rtn = v;
-				v += step;
-				return rtn;
-			}
-		};
-	}
-
-	public static LuanFunction values(final Object... args) throws LuanException {
-		return new LuanFunction() {
-			int i = 0;
-
-			@Override public Object call(LuanState luan,Object[] unused) {
-				if( ++i > args.length )
-					return LuanFunction.NOTHING;
-				return new Object[]{i,args[i-1]};
-			}
-		};
-	}
-
-}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/lib/BinaryLib.java
--- a/src/luan/lib/BinaryLib.java	Sun Jun 22 04:17:38 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-package luan.lib;
-
-import luan.LuanState;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanException;
-
-
-public final class BinaryLib {
-
-	public static final LuanFunction LOADER = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
-			try {
-				add( module, "to_string", new byte[0].getClass() );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			return module;
-		}
-	};
-
-	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
-		t.put( method, new LuanJavaFunction(BinaryLib.class.getMethod(method,parameterTypes),null) );
-	}
-
-	public static String to_string(byte[] bytes) {
-		return new String(bytes);
-	}
-
-}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/lib/HtmlLib.java
--- a/src/luan/lib/HtmlLib.java	Sun Jun 22 04:17:38 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-package luan.lib;
-
-import luan.LuanState;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanException;
-
-
-public final class HtmlLib {
-
-	public static final LuanFunction LOADER = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
-			try {
-				add( module, "encode", String.class );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			return module;
-		}
-	};
-
-	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
-		t.put( method, new LuanJavaFunction(HtmlLib.class.getMethod(method,parameterTypes),null) );
-	}
-
-	public static String encode(String s) {
-		char[] a = s.toCharArray();
-		StringBuilder buf = new StringBuilder();
-		for( int i=0; i<a.length; i++ ) {
-			char c = a[i];
-			switch(c) {
-			case '&':
-				buf.append("&amp;");
-				break;
-			case '<':
-				buf.append("&lt;");
-				break;
-			case '>':
-				buf.append("&gt;");
-				break;
-			case '"':
-				buf.append("&quot;");
-				break;
-			default:
-				buf.append(c);
-			}
-		}
-		return buf.toString();
-	}
-
-}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/lib/HttpLib.java
--- a/src/luan/lib/HttpLib.java	Sun Jun 22 04:17:38 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,285 +0,0 @@
-package luan.lib;
-
-import java.io.PrintWriter;
-import java.io.IOException;
-import java.util.Map;
-import java.util.Set;
-import java.util.Arrays;
-import java.util.Enumeration;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-import luan.Luan;
-import luan.LuanState;
-import luan.LuanFunction;
-import luan.LuanElement;
-import luan.LuanException;
-import luan.LuanTable;
-import luan.LuanJavaFunction;
-import luan.LuanExitException;
-import luan.DeepCloner;
-
-
-public final class HttpLib {
-
-	public static final LuanFunction LOADER = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			return new LuanTable();  // starts empty
-		}
-	};
-
-	public static void service(LuanState luan,HttpServletRequest request,HttpServletResponse response,String modName)
-		throws LuanException
-	{
-		LuanFunction fn;
-		synchronized(luan) {
-			Object mod = PackageLib.require(luan,modName);
-			if( !(mod instanceof LuanTable) )
-				throw luan.exception( "module '"+modName+"' must return a table" );
-			LuanTable tbl = (LuanTable)mod;
-			if( Luan.toBoolean( tbl.get("per_session") ) ) {
-				HttpSession session = request.getSession();
-				LuanState sessionLuan  = (LuanState)session.getValue("luan");
-				if( sessionLuan!=null ) {
-					luan = sessionLuan;
-				} else {
-					DeepCloner cloner = new DeepCloner();
-					luan = cloner.deepClone(luan);
-					session.putValue("luan",luan);
-				}
-				tbl = (LuanTable)PackageLib.require(luan,modName);
-				fn = (LuanFunction)tbl.get("page");
-			} else {
-				fn = (LuanFunction)tbl.get("page");
-				if( fn == null )
-					throw luan.exception( "function 'page' is not defined" );
-				DeepCloner cloner = new DeepCloner();
-				luan = cloner.deepClone(luan);
-				fn = cloner.get(fn);
-			}
-		}
-
-		LuanTable module = (LuanTable)luan.loaded().get("Http");
-		if( module == null )
-			throw luan.exception( "module 'Http' not defined" );
-		HttpLib lib = new HttpLib(request,response);
-		try {
-			module.put( "request", lib.requestTable() );
-			module.put( "response", lib.responseTable() );
-/*
-			module.put( "write", new LuanJavaFunction(
-				HttpLib.class.getMethod( "text_write", LuanState.class, new Object[0].getClass() ), lib
-			) );
-*/
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-
-		luan.call(fn,"<http>");
-	}
-
-
-
-	private final HttpServletRequest request;
-	private final HttpServletResponse response;
-//	private PrintWriter writer = null;
-//	private ServletOutputStream sos = null;
-
-	private HttpLib(HttpServletRequest request,HttpServletResponse response) {
-		this.request = request;
-		this.response = response;
-	}
-
-	private LuanTable requestTable() throws NoSuchMethodException {
-		LuanTable req = new LuanTable();
-		req.put("java",request);
-		req.put( "get_attribute", new LuanJavaFunction(HttpServletRequest.class.getMethod("getAttribute",String.class),request) );
-		req.put( "set_attribute", new LuanJavaFunction(HttpServletRequest.class.getMethod("setAttribute",String.class,Object.class),request) );
-		req.put( "get_parameter", new LuanJavaFunction(HttpServletRequest.class.getMethod("getParameter",String.class),request) );
-		req.put( "get_parameter_values", new LuanJavaFunction(HttpServletRequest.class.getMethod("getParameterValues",String.class),request) );
-		req.put( "get_header", new LuanJavaFunction(HttpServletRequest.class.getMethod("getHeader",String.class),request) );
-		add( req, "get_cookie_value", String.class );
-		req.put( "method", new LuanJavaFunction(HttpServletRequest.class.getMethod("getMethod"),request) );
-		req.put( "servlet_path", new LuanJavaFunction(HttpServletRequest.class.getMethod("getServletPath"),request) );
-		req.put( "server_name", new LuanJavaFunction(HttpServletRequest.class.getMethod("getServerName"),request) );
-		add( req, "current_url" );
-		req.put( "remote_address", new LuanJavaFunction(HttpServletRequest.class.getMethod("getRemoteAddr"),request) );
-		add( req, "get_session_attribute", String.class );
-		add( req, "set_session_attribute", String.class, Object.class );
-		return req;
-	}
-
-	private LuanTable responseTable() throws NoSuchMethodException {
-		LuanTable resp = new LuanTable();
-		resp.put("java",response);
-		add( resp, "send_redirect", String.class );
-		add( resp, "send_error", Integer.TYPE, String.class );
-		resp.put( "contains_header", new LuanJavaFunction(HttpServletResponse.class.getMethod("containsHeader",String.class),response) );
-		resp.put( "set_header", new LuanJavaFunction(HttpServletResponse.class.getMethod("setHeader",String.class,String.class),response) );
-		add( resp, "set_cookie", String.class, String.class, Boolean.TYPE, String.class );
-		add( resp, "remove_cookie", String.class, String.class );
-		resp.put( "set_content_type", new LuanJavaFunction(HttpServletResponse.class.getMethod("setContentType",String.class),response) );
-		add( resp, "text_writer" );
-		return resp;
-	}
-
-	private void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
-		t.put( method, new LuanJavaFunction(HttpLib.class.getMethod(method,parameterTypes),this) );
-	}
-/*
-	public void text_write(LuanState luan,Object... args) throws LuanException, IOException {
-		if( writer == null )
-			writer = response.getWriter();
-		for( Object obj : args ) {
-			writer.print( luan.toString(obj) );
-		}
-	}
-*/
-	public LuanTable text_writer() throws IOException {
-		return IoLib.textWriter(response.getWriter());
-	}
-
-	public String get_cookie_value(String name) {
-		return getCookieValue(request, name);
-	}
-
-	public String current_url() {
-		return getCurrentURL(request);
-	}
-
-	public void send_redirect(String redirectUrl)
-		throws IOException
-	{
-		response.sendRedirect(redirectUrl);
-		throw new LuanExitException();
-	}
-
-	public void send_error(int code,String text)
-		throws IOException
-	{
-		response.sendError(code, text);
-		throw new LuanExitException();
-	}
-
-	public void set_cookie(String name,String value,boolean isPersistent, String domain) {
-		setCookie(request,response,name,value,isPersistent,domain);
-	}
-
-	public void remove_cookie(String name, String domain) {
-		removeCookie(request,response,name,domain);
-	}
-
-	public Object get_session_attribute(String name) {
-		return request.getSession().getAttribute(name);
-	}
-
-	public void set_session_attribute(String name,Object value) {
-		request.getSession().setAttribute(name,value);
-	}
-
-
-	// static utils
-
-	public static String getQueryString(HttpServletRequest request) {
-		return getQueryString(request,0);
-	}
-
-	public static String getQueryString(HttpServletRequest request,int maxValueLen) {
-		String method = request.getMethod();
-		if( method.equals("GET") )
-			return request.getQueryString();
-		if( !method.equals("POST") && !method.equals("HEAD") )
-			throw new RuntimeException(method);
-		Enumeration en = request.getParameterNames();
-		StringBuilder queryBuf = new StringBuilder();
-		if( !en.hasMoreElements() )
-			return null;
-		do {
-			String param = (String)en.nextElement();
-			String value = request.getParameter(param);
-			if( maxValueLen > 0 ) {
-				int len = value.length();
-				if( len > maxValueLen )
-					value = value.substring(0,maxValueLen) + "..." + (len-maxValueLen);
-			}
-			queryBuf.append(param);
-			queryBuf.append('=');
-			queryBuf.append(value);
-			queryBuf.append('&');
-		} while( en.hasMoreElements() );
-		queryBuf.deleteCharAt(queryBuf.length() - 1);
-		return queryBuf.toString();
-	}
-
-	public static String getCurrentURL(HttpServletRequest request) {
-		return getCurrentURL(request,0);
-	}
-
-	public static String getCurrentURL(HttpServletRequest request,int maxValueLen) {
-//		StringBuffer buf = HttpUtils.getRequestURL(request);
-		StringBuffer buf = request.getRequestURL();
-		String qStr = getQueryString(request,maxValueLen);
-		if(qStr != null && qStr.length() > 0) {
-			buf.append('?');
-			buf.append(qStr);
-		}
-		return buf.toString();
-	}
-
-	private static String escape(String value) {
-		return value.replaceAll(";", "%3B");
-	}
-
-	private static String unescape(String value) {
-		return value.replaceAll("%3B", ";");
-	}
-
-	private static Cookie getCookie(HttpServletRequest request,String name) {
-		Cookie[] cookies = request.getCookies();
-		if( cookies == null )
-			return null;
-		for (Cookie cookie : cookies) {
-			if (cookie.getName().equals(name))
-				return cookie;
-		}
-		return null;
-	}
-
-	public static String getCookieValue(HttpServletRequest request,String name) {
-		Cookie cookie = getCookie(request,name);
-		return cookie==null ? null : unescape(cookie.getValue());
-	}
-
-	public static void setCookie(HttpServletRequest request,HttpServletResponse response,String name,String value,boolean isPersistent, String domain) {
-		Cookie cookie = getCookie(request,name);
-		if( cookie==null || !cookie.getValue().equals(value) ) {
-			cookie = new Cookie(name, escape(value));
-			cookie.setPath("/");
-			if (domain != null && domain.length() > 0)
-				cookie.setDomain(domain);
-			if( isPersistent )
-				cookie.setMaxAge(10000000);
-			response.addCookie(cookie);
-		}
-	}
-
-	public static void removeCookie(HttpServletRequest request,
-									HttpServletResponse response,
-									String name,
-									String domain
-
-	) {
-		Cookie cookie = getCookie(request, name);
-		if(cookie != null) {
-			Cookie delCookie = new Cookie(name, "delete");
-			delCookie.setPath("/");
-			delCookie.setMaxAge(0);
-			if (domain != null && domain.length() > 0)
-				delCookie.setDomain(domain);
-			response.addCookie(delCookie);
-		}
-	}
-
-}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/lib/IoLib.java
--- a/src/luan/lib/IoLib.java	Sun Jun 22 04:17:38 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,504 +0,0 @@
-package luan.lib;
-
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.io.Reader;
-import java.io.Writer;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.ByteArrayInputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.net.URL;
-import java.net.Socket;
-import java.net.ServerSocket;
-import java.net.MalformedURLException;
-import luan.LuanState;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanException;
-
-
-public final class IoLib {
-
-	public static final LuanFunction LOADER = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
-			try {
-				add( module, "File", LuanState.class, String.class );
-				add( module, "read_console_line", String.class );
-
-				LuanTable stdin = new LuanTable();
-				stdin.put( "read_text", new LuanJavaFunction(
-					IoLib.class.getMethod( "stdin_read_text" ), null
-				) );
-				stdin.put( "read_binary", new LuanJavaFunction(
-					IoLib.class.getMethod( "stdin_read_binary" ), null
-				) );
-				stdin.put( "read_lines", new LuanJavaFunction(
-					IoLib.class.getMethod( "stdin_read_lines" ), null
-				) );
-				stdin.put( "read_blocks", new LuanJavaFunction(
-					IoLib.class.getMethod( "stdin_read_blocks", Integer.class ), null
-				) );
-				module.put( "stdin", stdin );
-
-				add( module, "Socket", String.class, Integer.TYPE );
-				add( module, "socket_server", Integer.TYPE );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			module.put( "stdout", textWriter(System.out) );
-			module.put( "stderr", textWriter(System.err) );
-			return module;
-		}
-	};
-
-	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
-		t.put( method, new LuanJavaFunction(IoLib.class.getMethod(method,parameterTypes),null) );
-	}
-
-
-	public static String stdin_read_text() throws IOException {
-		return Utils.readAll(new InputStreamReader(System.in));
-	}
-
-	public static byte[] stdin_read_binary() throws IOException {
-		return Utils.readAll(System.in);
-	}
-
-	public static LuanFunction stdin_read_lines() throws IOException {
-		return lines(new BufferedReader(new InputStreamReader(System.in)));
-	}
-
-	public static LuanFunction stdin_read_blocks(Integer blockSize) throws IOException {
-		int n = blockSize!=null ? blockSize : Utils.bufSize;
-		return blocks(System.in,n);
-	}
-
-	public static String read_console_line(String prompt) throws IOException {
-		if( prompt==null )
-			prompt = "> ";
-		return System.console().readLine(prompt);
-	}
-
-
-	public interface LuanWriter {
-		public void write(LuanState luan,Object... args) throws LuanException, IOException;
-		public void close() throws IOException;
-	}
-
-	public static LuanTable textWriter(final PrintStream out) {
-		LuanWriter luanWriter = new LuanWriter() {
-
-			public void write(LuanState luan,Object... args) throws LuanException {
-				for( Object obj : args ) {
-					out.print( luan.toString(obj) );
-				}
-			}
-
-			public void close() {
-				out.close();
-			}
-		};
-		return writer(luanWriter);
-	}
-
-	public static LuanTable textWriter(final Writer out) {
-		LuanWriter luanWriter = new LuanWriter() {
-
-			public void write(LuanState luan,Object... args) throws LuanException, IOException {
-				for( Object obj : args ) {
-					out.write( luan.toString(obj) );
-				}
-			}
-
-			public void close() throws IOException {
-				out.close();
-			}
-		};
-		return writer(luanWriter);
-	}
-
-	private static LuanTable writer(LuanWriter luanWriter) {
-		LuanTable writer = new LuanTable();
-		try {
-			writer.put( "write", new LuanJavaFunction(
-				LuanWriter.class.getMethod( "write", LuanState.class, new Object[0].getClass() ), luanWriter
-			) );
-			writer.put( "close", new LuanJavaFunction(
-				LuanWriter.class.getMethod( "close" ), luanWriter
-			) );
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-		return writer;
-	}
-
-
-	public static LuanTable binaryWriter(final OutputStream out) {
-		LuanTable writer = new LuanTable();
-		try {
-			writer.put( "write", new LuanJavaFunction(
-				OutputStream.class.getMethod( "write", new byte[0].getClass() ), out
-			) );
-			writer.put( "close", new LuanJavaFunction(
-				OutputStream.class.getMethod( "close" ), out
-			) );
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-		return writer;
-	}
-
-	static LuanFunction lines(final BufferedReader in) {
-		return new LuanFunction() {
-			@Override public Object call(LuanState luan,Object[] args) throws LuanException {
-				try {
-					if( args.length > 0 ) {
-						if( args.length > 1 || !"close".equals(args[0]) )
-							throw luan.exception( "the only argument allowed is 'close'" );
-						in.close();
-						return null;
-					}
-					String rtn = in.readLine();
-					if( rtn==null )
-						in.close();
-					return rtn;
-				} catch(IOException e) {
-					throw luan.exception(e);
-				}
-			}
-		};
-	}
-
-	static LuanFunction blocks(final InputStream in,final int blockSize) {
-		return new LuanFunction() {
-			final byte[] a = new byte[blockSize];
-
-			@Override public Object call(LuanState luan,Object[] args) throws LuanException {
-				try {
-					if( args.length > 0 ) {
-						if( args.length > 1 || !"close".equals(args[0]) )
-							throw luan.exception( "the only argument allowed is 'close'" );
-						in.close();
-						return null;
-					}
-					if( in.read(a) == -1 ) {
-						in.close();
-						return null;
-					}
-					return a;
-				} catch(IOException e) {
-					throw luan.exception(e);
-				}
-			}
-		};
-	}
-
-
-
-	public static abstract class LuanIn {
-		abstract InputStream inputStream() throws IOException;
-		public abstract String to_string();
-
-		public String read_text() throws IOException {
-			Reader in = new InputStreamReader(inputStream());
-			String s = Utils.readAll(in);
-			in.close();
-			return s;
-		}
-
-		public byte[] read_binary() throws IOException {
-			InputStream in = inputStream();
-			byte[] a = Utils.readAll(in);
-			in.close();
-			return a;
-		}
-
-		public LuanFunction read_lines() throws IOException {
-			return lines(new BufferedReader(new InputStreamReader(inputStream())));
-		}
-
-		public LuanFunction read_blocks(Integer blockSize) throws IOException {
-			int n = blockSize!=null ? blockSize : Utils.bufSize;
-			return blocks(inputStream(),n);
-		}
-
-		LuanTable table() {
-			LuanTable tbl = new LuanTable();
-			try {
-				tbl.put( "to_string", new LuanJavaFunction(
-					LuanIn.class.getMethod( "to_string" ), this
-				) );
-				tbl.put( "read_text", new LuanJavaFunction(
-					LuanIn.class.getMethod( "read_text" ), this
-				) );
-				tbl.put( "read_binary", new LuanJavaFunction(
-					LuanIn.class.getMethod( "read_binary" ), this
-				) );
-				tbl.put( "read_lines", new LuanJavaFunction(
-					LuanIn.class.getMethod( "read_lines" ), this
-				) );
-				tbl.put( "read_blocks", new LuanJavaFunction(
-					LuanIn.class.getMethod( "read_blocks", Integer.class ), this
-				) );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			return tbl;
-		}
-	}
-
-	public static abstract class LuanIO extends LuanIn {
-		abstract OutputStream outputStream() throws IOException;
-
-		public void write(LuanState luan,Object obj) throws LuanException, IOException {
-			if( obj instanceof String ) {
-				String s = (String)obj;
-				Writer out = new OutputStreamWriter(outputStream());
-				out.write(s);
-				out.close();
-				return;
-			}
-			if( obj instanceof byte[] ) {
-				byte[] a = (byte[])obj;
-				OutputStream out = outputStream();
-				Utils.copyAll(new ByteArrayInputStream(a),out);
-				out.close();
-				return;
-			}
-			throw luan.exception( "bad argument #1 to 'write' (string or binary expected)" );
-		}
-
-		public LuanTable text_writer() throws IOException {
-			return textWriter(new BufferedWriter(new OutputStreamWriter(outputStream())));
-		}
-
-		public LuanTable binary_writer() throws IOException {
-			return binaryWriter(new BufferedOutputStream(outputStream()));
-		}
-
-		@Override LuanTable table() {
-			LuanTable tbl = super.table();
-			try {
-				tbl.put( "write", new LuanJavaFunction(
-					LuanIO.class.getMethod( "write", LuanState.class, Object.class ), this
-				) );
-				tbl.put( "text_writer", new LuanJavaFunction(
-					LuanIO.class.getMethod( "text_writer" ), this
-				) );
-				tbl.put( "binary_writer", new LuanJavaFunction(
-					LuanIO.class.getMethod( "binary_writer" ), this
-				) );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			return tbl;
-		}
-	}
-
-	public static final class LuanUrl extends LuanIn {
-		private final URL url;
-
-		public LuanUrl(String s) throws MalformedURLException {
-			this.url = new URL(s);
-		}
-
-		@Override InputStream inputStream() throws IOException {
-			return url.openStream();
-		}
-
-		@Override public String to_string() {
-			return url.toString();
-		}
-	}
-
-	public static final class LuanFile extends LuanIO {
-		private final File file;
-
-		public LuanFile(String name) {
-			this(new File(name));
-		}
-
-		public LuanFile(File file) {
-			this.file = file;
-		}
-
-		@Override InputStream inputStream() throws IOException {
-			return new FileInputStream(file);
-		}
-
-		@Override OutputStream outputStream() throws IOException {
-			return new FileOutputStream(file);
-		}
-
-		@Override public String to_string() {
-			return file.toString();
-		}
-
-		public LuanTable child(String name) {
-			return new LuanFile(new File(file,name)).table();
-		}
-
-		public LuanTable children() {
-			File[] files = file.listFiles();
-			if( files==null )
-				return null;
-			LuanTable list = new LuanTable();
-			for( File f : files ) {
-				list.add(new LuanFile(f).table());
-			}
-			return list;
-		}
-
-		public boolean exists() {
-			return Utils.exists(file);
-		}
-
-		@Override LuanTable table() {
-			LuanTable tbl = super.table();
-			try {
-				tbl.put( "name", new LuanJavaFunction(
-					File.class.getMethod( "getName" ), file
-				) );
-				tbl.put( "exists", new LuanJavaFunction(
-					LuanFile.class.getMethod( "exists" ), this
-				) );
-				tbl.put( "is_directory", new LuanJavaFunction(
-					File.class.getMethod( "isDirectory" ), file
-				) );
-				tbl.put( "is_file", new LuanJavaFunction(
-					File.class.getMethod( "isFile" ), file
-				) );
-				tbl.put( "delete", new LuanJavaFunction(
-					File.class.getMethod( "delete" ), file
-				) );
-				tbl.put( "mkdir", new LuanJavaFunction(
-					File.class.getMethod( "mkdir" ), file
-				) );
-				tbl.put( "mkdirs", new LuanJavaFunction(
-					File.class.getMethod( "mkdirs" ), file
-				) );
-				tbl.put( "last_modified", new LuanJavaFunction(
-					File.class.getMethod( "lastModified" ), file
-				) );
-				tbl.put( "child", new LuanJavaFunction(
-					LuanFile.class.getMethod( "child", String.class ), this
-				) );
-				tbl.put( "children", new LuanJavaFunction(
-					LuanFile.class.getMethod( "children" ), this
-				) );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			return tbl;
-		}
-	}
-
-	public static LuanIn luanIo(LuanState luan,String name) throws LuanException {
-		if( Utils.isFile(name) )
-			return new LuanFile(name);
-		String url = Utils.toUrl(name);
-		if( url != null ) {
-			try {
-				return new LuanUrl(url);
-			} catch(MalformedURLException e) {
-				throw new RuntimeException(e);
-			}
-		}
-		throw luan.exception( "file '"+name+"' not found" );
-	}
-
-	public static LuanTable File(LuanState luan,String name) throws LuanException {
-		return luanIo(luan,name).table();
-	}
-
-	public static final class LuanSocket extends LuanIO {
-		private final Socket socket;
-
-		public LuanSocket(String host,int port) throws IOException {
-			this(new Socket(host,port));
-		}
-
-		public LuanSocket(Socket socket) throws IOException {
-			this.socket = socket;
-		}
-
-		@Override InputStream inputStream() throws IOException {
-			return socket.getInputStream();
-		}
-
-		@Override OutputStream outputStream() throws IOException {
-			return socket.getOutputStream();
-		}
-
-		@Override public String to_string() {
-			return socket.toString();
-		}
-
-		public LuanTable Pickle_client(LuanState luan) throws IOException {
-			DataInputStream in = new DataInputStream(new BufferedInputStream(inputStream()));
-			DataOutputStream out = new DataOutputStream(new BufferedOutputStream(outputStream()));
-			return new PickleClient(luan,in,out).table();
-		}
-
-		public void run_pickle_server(LuanState luan) throws IOException {
-			DataInputStream in = new DataInputStream(new BufferedInputStream(inputStream()));
-			DataOutputStream out = new DataOutputStream(new BufferedOutputStream(outputStream()));
-			new PickleServer(luan,in,out).run();
-		}
-
-		@Override LuanTable table() {
-			LuanTable tbl = super.table();
-			try {
-				tbl.put( "Pickle_client", new LuanJavaFunction(
-					LuanSocket.class.getMethod( "Pickle_client", LuanState.class ), this
-				) );
-				tbl.put( "run_pickle_server", new LuanJavaFunction(
-					LuanSocket.class.getMethod( "run_pickle_server", LuanState.class ), this
-				) );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			return tbl;
-		}
-	}
-
-	public static LuanTable Socket(String host,int port) throws IOException {
-		return new LuanSocket(host,port).table();
-	}
-
-	public static LuanFunction socket_server(int port) throws IOException {
-		final ServerSocket ss = new ServerSocket(port);
-		return new LuanFunction() {
-			@Override public Object call(LuanState luan,Object[] args) throws LuanException {
-				try {
-					if( args.length > 0 ) {
-						if( args.length > 1 || !"close".equals(args[0]) )
-							throw luan.exception( "the only argument allowed is 'close'" );
-						ss.close();
-						return null;
-					}
-					return new LuanSocket(ss.accept()).table();
-				} catch(IOException e) {
-					throw luan.exception(e);
-				}
-			}
-		};
-	}
-
-	private void IoLib() {}  // never
-}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/lib/JavaLib.java
--- a/src/luan/lib/JavaLib.java	Sun Jun 22 04:17:38 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,450 +0,0 @@
-package luan.lib;
-
-import java.lang.reflect.Array;
-import java.lang.reflect.AccessibleObject;
-import java.lang.reflect.Member;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Proxy;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.Collections;
-import java.util.Arrays;
-import luan.Luan;
-import luan.LuanState;
-import luan.LuanTable;
-import luan.MetatableGetter;
-import luan.LuanException;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanElement;
-
-
-public final class JavaLib {
-
-	public static final LuanFunction LOADER = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			luan.addMetatableGetter(mg);
-			LuanTable module = new LuanTable();
-			try {
-				module.put( "class", new LuanJavaFunction(JavaLib.class.getMethod("getClass",LuanState.class,String.class),null) );
-				add( module, "proxy", LuanState.class, Static.class, LuanTable.class, Object.class );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			luan.searchers().add(javaSearcher);
-			return module;
-		}
-	};
-
-	public static final LuanFunction javaSearcher = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) throws LuanException {
-			String modName = (String)args[0];
-			final Static s = JavaLib.getClass(luan,modName);
-			if( s==null )
-				return null;
-			LuanFunction loader = new LuanFunction() {
-				@Override public Object call(LuanState luan,Object[] args) {
-					return s;
-				}
-			};
-			return loader;
-		}
-	};
-
-	private static void add(LuanTable t,String method,Class<?>... parameterTypes) {
-		try {
-			t.put( method, new LuanJavaFunction(JavaLib.class.getMethod(method,parameterTypes),null) );
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	private static final LuanTable mt = new LuanTable();
-	static {
-		add( mt, "__index", LuanState.class, Object.class, Object.class );
-		add( mt, "__newindex", LuanState.class, Object.class, Object.class, Object.class );
-	}
-
-	private static final MetatableGetter mg = new MetatableGetter() {
-		public LuanTable getMetatable(Object obj) {
-			if( obj==null )
-				return null;
-			return mt;
-		}
-	};
-
-	public static Object __index(LuanState luan,Object obj,Object key) throws LuanException {
-		if( obj instanceof Static ) {
-			if( key instanceof String ) {
-				String name = (String)key;
-				Static st = (Static)obj;
-				Class cls = st.cls;
-				if( "class".equals(name) ) {
-					return cls;
-				} else if( "new".equals(name) ) {
-					Constructor<?>[] constructors = cls.getConstructors();
-					if( constructors.length > 0 ) {
-						if( constructors.length==1 ) {
-							return new LuanJavaFunction(constructors[0],null);
-						} else {
-							List<LuanJavaFunction> fns = new ArrayList<LuanJavaFunction>();
-							for( Constructor constructor : constructors ) {
-								fns.add(new LuanJavaFunction(constructor,null));
-							}
-							return new AmbiguousJavaFunction(fns);
-						}
-					}
-				} else if( "assert".equals(name) ) {
-					return new LuanJavaFunction(assertClass,new AssertClass(cls));
-				} else {
-					List<Member> members = getStaticMembers(cls,name);
-					if( !members.isEmpty() ) {
-						return member(null,members);
-					}
-				}
-			}
-			throw luan.exception("invalid member '"+key+"' for: "+obj);
-		}
-		Class cls = obj.getClass();
-		if( cls.isArray() ) {
-			if( "length".equals(key) ) {
-				return Array.getLength(obj);
-			}
-			Integer i = Luan.asInteger(key);
-			if( i != null ) {
-				return Array.get(obj,i);
-			}
-			throw luan.exception("invalid member '"+key+"' for java array: "+obj);
-		}
-		if( key instanceof String ) {
-			String name = (String)key;
-			if( "instanceof".equals(name) ) {
-				return new LuanJavaFunction(instanceOf,new InstanceOf(obj));
-			} else {
-				List<Member> members = getMembers(cls,name);
-				if( !members.isEmpty() ) {
-					return member(obj,members);
-				}
-			}
-		}
-//		throw luan.exception("invalid member '"+key+"' for java object: "+obj);
-		return null;
-	}
-
-	private static Object member(Object obj,List<Member> members) throws LuanException {
-		try {
-			if( members.size()==1 ) {
-				Member member = members.get(0);
-				if( member instanceof Static ) {
-					return member;
-				} else if( member instanceof Field ) {
-					Field field = (Field)member;
-					Object rtn = field.get(obj);
-					return rtn instanceof Object[] ? Arrays.asList((Object[])rtn) : rtn;
-				} else {
-					Method method = (Method)member;
-					return new LuanJavaFunction(method,obj);
-				}
-			} else {
-				List<LuanJavaFunction> fns = new ArrayList<LuanJavaFunction>();
-				for( Member member : members ) {
-					Method method = (Method)member;
-					fns.add(new LuanJavaFunction(method,obj));
-				}
-				return new AmbiguousJavaFunction(fns);
-			}
-		} catch(IllegalAccessException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	public static void __newindex(LuanState luan,Object obj,Object key,Object value) throws LuanException {
-		if( obj instanceof Static ) {
-			if( key instanceof String ) {
-				String name = (String)key;
-				Static st = (Static)obj;
-				Class cls = st.cls;
-				List<Member> members = getStaticMembers(cls,name);
-				if( !members.isEmpty() ) {
-					if( members.size() != 1 )
-						throw new RuntimeException("not field '"+name+"' of "+obj);
-					setMember(obj,members,value);
-					return;
-				}
-			}
-			throw luan.exception("invalid member '"+key+"' for: "+obj);
-		}
-		Class cls = obj.getClass();
-		if( cls.isArray() ) {
-			Integer i = Luan.asInteger(key);
-			if( i != null ) {
-				Array.set(obj,i,value);
-				return;
-			}
-			throw luan.exception("invalid member '"+key+"' for java array: "+obj);
-		}
-		if( key instanceof String ) {
-			String name = (String)key;
-			List<Member> members = getMembers(cls,name);
-			if( !members.isEmpty() ) {
-				if( members.size() != 1 )
-					throw new RuntimeException("not field '"+name+"' of "+obj);
-				setMember(obj,members,value);
-				return;
-			}
-		}
-		throw luan.exception("invalid member '"+key+"' for java object: "+obj);
-	}
-
-	private static void setMember(Object obj,List<Member> members,Object value) {
-		Field field = (Field)members.get(0);
-		try {
-			try {
-				field.set(obj,value);
-			} catch(IllegalArgumentException e) {
-				Class cls = field.getType();
-				if( value instanceof Number ) {
-					Number n = (Number)value;
-					if( cls.equals(Integer.TYPE) || cls.equals(Integer.class) ) {
-						int r = n.intValue();
-						if( r==n.doubleValue() ) {
-							field.setInt(obj,r);
-							return;
-						}
-					}
-				}
-				throw e;
-			}
-		} catch(IllegalAccessException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	public static boolean privateAccess = false;
-	private static Map<Class,Map<String,List<Member>>> memberMap = new HashMap<Class,Map<String,List<Member>>>();
-
-	private static synchronized List<Member> getMembers(Class cls,String name) {
-		Map<String,List<Member>> clsMap = memberMap.get(cls);
-		if( clsMap == null ) {
-			clsMap = new HashMap<String,List<Member>>();
-			for( Class c : cls.getClasses() ) {
-				String s = c.getSimpleName();
-				List<Member> list = new ArrayList<Member>();
-				clsMap.put(s,list);
-				list.add(new Static(c));
-			}
-			for( Field field : cls.getFields() ) {
-				String s = field.getName();
-				try {
-					if( !cls.getField(s).equals(field) )
-						continue;  // not accessible
-				} catch(NoSuchFieldException e) {
-					throw new RuntimeException(e);
-				}
-				List<Member> list = new ArrayList<Member>();
-				clsMap.put(s,list);
-				list.add(field);
-			}
-			for( Method method : cls.getMethods() ) {
-				String s = method.getName();
-				List<Member> list = clsMap.get(s);
-				if( list == null || !(list.get(0) instanceof Method) ) {
-					list = new ArrayList<Member>();
-					clsMap.put(s,list);
-				}
-				list.add(method);
-			}
-			if( privateAccess ) {
-				for( Method method : cls.getDeclaredMethods() ) {
-					String s = method.getName();
-					List<Member> list = clsMap.get(s);
-					if( list == null ) {
-						list = new ArrayList<Member>();
-						clsMap.put(s,list);
-					} else if( !(list.get(0) instanceof Method) )
-						continue;
-					if( !list.contains(method) ) {
-						list.add(method);
-					}
-				}
-				for( Field field : cls.getDeclaredFields() ) {
-					String s = field.getName();
-					List<Member> list = clsMap.get(s);
-					if( list == null ) {
-						list = new ArrayList<Member>();
-						clsMap.put(s,list);
-						list.add(field);
-					}
-				}
-			}
-			for( List<Member> members : clsMap.values() ) {
-				for( Member m : members ) {
-					if( m instanceof AccessibleObject )
-						((AccessibleObject)m).setAccessible(true);
-				}
-			}
-			memberMap.put(cls,clsMap);
-		}
-		List<Member> rtn = clsMap.get(name);
-		if( rtn==null )
-			rtn = Collections.emptyList();
-		return rtn;
-	}
-
-	private static synchronized List<Member> getStaticMembers(Class cls,String name) {
-		List<Member> staticMembers = new ArrayList<Member>();
-		for( Member m : getMembers(cls,name) ) {
-			if( Modifier.isStatic(m.getModifiers()) )
-				staticMembers.add(m);
-		}
-		return staticMembers;
-	}
-
-	static final class Static implements Member {
-		final Class cls;
-
-		Static(Class cls) {
-			this.cls = cls;
-		}
-
-		@Override public String toString() {
-			return cls.toString();
-		}
-
-		@Override public Class<?> getDeclaringClass() {
-			return cls.getDeclaringClass();
-		}
-
-		@Override public String getName() {
-			return cls.getName();
-		}
-
-		@Override public int getModifiers() {
-			return cls.getModifiers();
-		}
-
-		@Override public boolean isSynthetic() {
-			return cls.isSynthetic();
-		}
-	}
-
-	public static Static getClass(LuanState luan,String name) throws LuanException {
-		Class cls;
-		try {
-			cls = Class.forName(name);
-		} catch(ClassNotFoundException e) {
-			try {
-				cls = Thread.currentThread().getContextClassLoader().loadClass(name);
-			} catch(ClassNotFoundException e2) {
-				return null;
-			}
-		}
-		return new Static(cls);
-	}
-/*
-	public static void importClass(LuanState luan,String name) throws LuanException {
-		luan.currentEnvironment().put( name.substring(name.lastIndexOf('.')+1), getClass(luan,name) );
-	}
-*/
-	static class AmbiguousJavaFunction extends LuanFunction {
-		private final Map<Integer,List<LuanJavaFunction>> fnMap = new HashMap<Integer,List<LuanJavaFunction>>();
-
-		AmbiguousJavaFunction(List<LuanJavaFunction> fns) {
-			for( LuanJavaFunction fn : fns ) {
-				Integer n = fn.getParameterTypes().length;
-				List<LuanJavaFunction> list = fnMap.get(n);
-				if( list==null ) {
-					list = new ArrayList<LuanJavaFunction>();
-					fnMap.put(n,list);
-				}
-				list.add(fn);
-			}
-		}
-
-		@Override public Object call(LuanState luan,Object[] args) throws LuanException {
-			for( LuanJavaFunction fn : fnMap.get(args.length) ) {
-				try {
-					return fn.rawCall(luan,args);
-				} catch(IllegalArgumentException e) {}
-			}
-			throw luan.exception("no method matched args");
-		}
-	}
-
-	private static class InstanceOf {
-		private final Object obj;
-
-		InstanceOf(Object obj) {
-			this.obj = obj;
-		}
-
-		public boolean instanceOf(Static st) {
-			return st.cls.isInstance(obj);
-		}
-	}
-	private static final Method instanceOf;
-	static {
-		try {
-			instanceOf = InstanceOf.class.getMethod("instanceOf",Static.class);
-			instanceOf.setAccessible(true);
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-
-	private static class AssertClass {
-		private final Class cls;
-
-		AssertClass(Class cls) {
-			this.cls = cls;
-		}
-
-		public Object assertClass(LuanState luan,Object v) throws LuanException {
-			if( !cls.isInstance(v) ) {
-				String got = v.getClass().getSimpleName();
-				String expected = cls.getSimpleName();
-				throw luan.exception("bad argument #1 ("+expected+" expected, got "+got+")");
-			}
-			return v;
-		}
-	}
-	private static final Method assertClass;
-	static {
-		try {
-			assertClass = AssertClass.class.getMethod("assertClass",LuanState.class,Object.class);
-			assertClass.setAccessible(true);
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-
-	public static Object proxy(final LuanState luan,Static st,final LuanTable t,final Object base) throws LuanException {
-		return Proxy.newProxyInstance(
-			st.cls.getClassLoader(),
-			new Class[]{st.cls},
-			new InvocationHandler() {
-				public Object invoke(Object proxy,Method method, Object[] args)
-					throws Throwable
-				{
-					if( args==null )
-						args = new Object[0];
-					String name = method.getName();
-					Object fnObj = t.get(name);
-					if( fnObj==null && base!=null )
-						return method.invoke(base,args);
-					LuanFunction fn = luan.checkFunction(fnObj);
-					return Luan.first(luan.call(fn,name,args));
-				}
-			}
-		);
-	}
-}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/lib/MathLib.java
--- a/src/luan/lib/MathLib.java	Sun Jun 22 04:17:38 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,152 +0,0 @@
-package luan.lib;
-
-import luan.LuanState;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanException;
-
-
-public final class MathLib {
-
-	public static final LuanFunction LOADER = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
-			try {
-				add( module, "abs", Double.TYPE );
-				add( module, "acos", Double.TYPE );
-				add( module, "asin", Double.TYPE );
-				add( module, "atan", Double.TYPE );
-				add( module, "atan2", Double.TYPE, Double.TYPE );
-				add( module, "ceil", Double.TYPE );
-				add( module, "cos", Double.TYPE );
-				add( module, "cosh", Double.TYPE );
-				add( module, "deg", Double.TYPE );
-				add( module, "exp", Double.TYPE );
-				add( module, "floor", Double.TYPE );
-				add( module, "log", Double.TYPE );
-				add( module, "min", Double.TYPE, new double[0].getClass() );
-				add( module, "max", Double.TYPE, new double[0].getClass() );
-				add( module, "modf", Double.TYPE );
-				module.put("pi",Math.PI);
-				add( module, "pow", Double.TYPE, Double.TYPE );
-				add( module, "rad", Double.TYPE );
-				add( module, "random" );
-				add( module, "sin", Double.TYPE );
-				add( module, "sinh", Double.TYPE );
-				add( module, "sqrt", Double.TYPE );
-				add( module, "tan", Double.TYPE );
-				add( module, "tanh", Double.TYPE );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			return module;
-		}
-	};
-
-	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
-		t.put( method, new LuanJavaFunction(MathLib.class.getMethod(method,parameterTypes),null) );
-	}
-
-	public static double abs(double x) {
-		return Math.abs(x);
-	}
-
-	public static double acos(double x) {
-		return Math.acos(x);
-	}
-
-	public static double asin(double x) {
-		return Math.asin(x);
-	}
-
-	public static double atan(double x) {
-		return Math.atan(x);
-	}
-
-	public static double atan2(double y,double x) {
-		return Math.atan2(y,x);
-	}
-
-	public static double ceil(double x) {
-		return Math.ceil(x);
-	}
-
-	public static double cos(double x) {
-		return Math.cos(x);
-	}
-
-	public static double cosh(double x) {
-		return Math.cosh(x);
-	}
-
-	public static double deg(double x) {
-		return Math.toDegrees(x);
-	}
-
-	public static double exp(double x) {
-		return Math.exp(x);
-	}
-
-	public static double floor(double x) {
-		return Math.floor(x);
-	}
-
-	public static double log(double x) {
-		return Math.log(x);
-	}
-
-	public static double min(double x,double... a) {
-		for( double d : a ) {
-			if( x > d )
-				x = d;
-		}
-		return x;
-	}
-
-	public static double max(double x,double... a) {
-		for( double d : a ) {
-			if( x < d )
-				x = d;
-		}
-		return x;
-	}
-
-	public static double[] modf(double x) {
-		double i = (int)x;
-		return new double[]{i,x-i};
-	}
-
-	public static double pow(double x,double y) {
-		return Math.pow(x,y);
-	}
-
-	public static double rad(double x) {
-		return Math.toRadians(x);
-	}
-
-	public static double random() {
-		return Math.random();
-	}
-
-	public static double sin(double x) {
-		return Math.sin(x);
-	}
-
-	public static double sinh(double x) {
-		return Math.sinh(x);
-	}
-
-	public static double sqrt(double x) {
-		return Math.sqrt(x);
-	}
-
-	public static double tan(double x) {
-		return Math.tan(x);
-	}
-
-	public static double tanh(double x) {
-		return Math.tanh(x);
-	}
-
-}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/lib/PackageLib.java
--- a/src/luan/lib/PackageLib.java	Sun Jun 22 04:17:38 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,170 +0,0 @@
-package luan.lib;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import luan.Luan;
-import luan.LuanState;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanElement;
-import luan.LuanException;
-
-
-public final class PackageLib {
-
-	private static final String jpath = "luan.lib.?Lib.LOADER";
-
-	public static final LuanFunction LOADER = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
-			module.put("loaded",luan.loaded());
-			module.put("preload",luan.preload());
-			module.put("path","?.luan;java:luan/modules/?.luan");
-			module.put("jpath",jpath);
-			try {
-				add( module, "require", LuanState.class, String.class );
-				add( module, "load_lib", String.class );
-				add( module, "search_path", String.class, String.class );
-				add( module, "search", LuanState.class, String.class );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			LuanTable searchers = luan.searchers();
-			searchers.add(preloadSearcher);
-			searchers.add(fileSearcher);
-			searchers.add(javaSearcher);
-			module.put("searchers",searchers);
-			return module;
-		}
-	};
-
-	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
-		t.put( method, new LuanJavaFunction(PackageLib.class.getMethod(method,parameterTypes),null) );
-	}
-
-	public static Object require(LuanState luan,String modName) throws LuanException {
-		LuanTable loaded = luan.loaded();
-		Object mod = loaded.get(modName);
-		if( mod == null ) {
-			Object[] a = search(luan,modName);
-			if( a == null )
-				throw luan.exception( "module '"+modName+"' not found" );
-			LuanFunction loader = (LuanFunction)a[0];
-			a[0] = modName;
-			mod = Luan.first(luan.call(loader,"<require \""+modName+"\">",a));
-			if( mod != null ) {
-				loaded.put(modName,mod);
-			} else {
-				mod = loaded.get(modName);
-				if( mod==null )
-					loaded.put(modName,true);
-			}
-		}
-		return mod;
-	}
-
-	public static Object[] search(LuanState luan,String modName) throws LuanException {
-		List<Object> list = null;
-		LuanTable searchers = (LuanTable)luan.get("Package.searchers");
-		if( searchers == null ) {
-			list = Collections.<Object>singletonList(javaSearcher);
-		} else {
-			list = searchers.asList();
-		}
-		for( Object s : list ) {
-			LuanFunction searcher = (LuanFunction)s;
-			Object[] a = Luan.array(luan.call(searcher,"<searcher>",new Object[]{modName}));
-			if( a.length >= 1 && a[0] instanceof LuanFunction )
-				return a;
-		}
-		return null;
-	}
-
-	public static final LuanFunction preloadSearcher = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			String modName = (String)args[0];
-			return luan.preload().get(modName);
-		}
-	};
-
-	public static String search_path(String name,String path) {
-		name = name.replace('.','/');
-		for( String s : path.split(";") ) {
-			String file = s.replaceAll("\\?",name);
-			if( Utils.exists(file) )
-				return file;
-		}
-		return null;
-	}
-
-	public static final LuanFunction fileLoader = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) throws LuanException {
-			String fileName = (String)args[1];
-			LuanFunction fn = BasicLib.load_file(luan,fileName);
-			return fn.call(luan,args);
-		}
-	};
-
-	public static final LuanFunction fileSearcher = new LuanFunction() {
-		@Override public Object[] call(LuanState luan,Object[] args) {
-			String modName = (String)args[0];
-			String path = (String)luan.get("Package.path");
-			if( path==null )
-				return LuanFunction.NOTHING;
-			String file = search_path(modName,path);
-			return file==null ? LuanFunction.NOTHING : new Object[]{fileLoader,file};
-		}
-	};
-
-
-	public static final LuanFunction javaLoader = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) throws LuanException {
-			try {
-				String objName = (String)args[1];
-				LuanFunction fn = load_lib(objName);
-				return fn.call(luan,args);
-			} catch(ClassNotFoundException e) {
-				throw new RuntimeException(e);
-			} catch(NoSuchFieldException e) {
-				throw new RuntimeException(e);
-			} catch(IllegalAccessException e) {
-				throw new RuntimeException(e);
-			}
-		}
-	};
-
-	public static final LuanFunction javaSearcher = new LuanFunction() {
-		@Override public Object[] call(LuanState luan,Object[] args) {
-			String modName = (String)args[0];
-			String path = (String)luan.get("Package.jpath");
-			if( path==null )
-				path = jpath;
-			for( String s : path.split(";") ) {
-				String objName = s.replaceAll("\\?",modName);
-				try {
-					load_lib(objName);  // throws exception if not found
-					return new Object[]{javaLoader,objName};
-				} catch(ClassNotFoundException e) {
-				} catch(NoSuchFieldException e) {
-				} catch(IllegalAccessException e) {
-				}
-			}
-			return LuanFunction.NOTHING;
-		}
-	};
-
-
-	public static LuanFunction load_lib(String path)
-		throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException
-	{
-		int i = path.lastIndexOf('.');
-		String clsPath = path.substring(0,i);
-		String fld = path.substring(i+1);
-		Class cls = Class.forName(clsPath);
-		return (LuanFunction)cls.getField(fld).get(null);
-	}
-
-}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/lib/PickleClient.java
--- a/src/luan/lib/PickleClient.java	Sun Jun 22 04:17:38 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,106 +0,0 @@
-package luan.lib;
-
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import luan.Luan;
-import luan.LuanState;
-import luan.LuanException;
-import luan.LuanTable;
-import luan.LuanJavaFunction;
-import luan.LuanFunction;
-
-
-public final class PickleClient {
-
-	private final PickleCon con;
-	private final LuanFunction _reversed_pickle;
-
-	PickleClient(LuanState luan,DataInputStream in,DataOutputStream out) {
-		this(new PickleCon(luan,in,out));
-	}
-
-	PickleClient(PickleCon con) {
-		this.con = con;
-		try {
-			this._reversed_pickle = new LuanJavaFunction(
-				PickleClient.class.getMethod( "_reversed_pickle" ), this
-			);
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	public Object _reversed_pickle() throws LuanException, IOException {
-		new PickleServer(con).run();
-		return con.read();
-	}
-
-	public Object call(Object... args) throws LuanException, IOException {
-		con.write(args);
-		Object[] result;
-		con.ioModule.put("_reversed_pickle",_reversed_pickle);
-		try {
-			result = Luan.array(con.read());
-		} finally {
-			con.ioModule.put("_reversed_pickle",null);
-		}
-		boolean ok = (boolean)result[0];
-		if( ok ) {
-			Object[] rtn = new Object[result.length-1];
-			System.arraycopy(result,1,rtn,0,rtn.length);
-			return rtn;
-		} else {
-			String msg = (String)result[1];
-			String src = (String)result[2];
-			throw con.luan.exception(
-				msg + "\n"
-				+ "in:\n"
-				+ "------------------\n"
-				+ formatCode(src) + "\n"
-				+ "------------------\n"
-			);
-		}
-	}
-
-	LuanTable table() {
-		LuanTable tbl = new LuanTable();
-		try {
-			tbl.put( "pickle", new LuanJavaFunction(
-				PickleCon.class.getMethod( "pickle", Object.class ), con
-			) );
-			tbl.put( "call", new LuanJavaFunction(
-				PickleClient.class.getMethod( "call", new Object[0].getClass() ), this
-			) );
-			tbl.put( "close", new LuanJavaFunction(
-				PickleCon.class.getMethod( "close" ), con
-			) );
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-		return tbl;
-	}
-
-
-	public static String formatCode(String s) {
-		StringBuilder buf = new StringBuilder();
-		int line = 1;
-		int i = 0;
-		int i2 = 0;
-		while( i2 != -1 ) {
-			buf.append( line++ );
-			buf.append( '\t' );
-			i2 = s.indexOf('\n',i);
-			String lineStr = i2 == -1 ? s.substring(i) : s.substring(i,i2+1);
-			int j;
-			for( j=0; j<lineStr.length() && lineStr.charAt(j)=='\t'; j++ ) {
-				buf.append( "    " );
-			}
-			buf.append( lineStr.substring(j) );
-			i = i2 + 1;
-		}
-		return buf.toString();
-	}
-
-
-}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/lib/PickleCon.java
--- a/src/luan/lib/PickleCon.java	Sun Jun 22 04:17:38 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,134 +0,0 @@
-package luan.lib;
-
-import java.io.OutputStream;
-import java.io.BufferedOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.util.Set;
-import java.util.IdentityHashMap;
-import java.util.Collections;
-import java.util.Map;
-import java.util.List;
-import java.util.ArrayList;
-import luan.Luan;
-import luan.LuanTable;
-import luan.LuanState;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanException;
-
-
-public final class PickleCon {
-	final LuanState luan;
-	private final DataInputStream in;
-	private final LuanFunction _read_binary;
-	final LuanTable ioModule;
-	private final DataOutputStream out;
-	private final List<byte[]> binaries = new ArrayList<byte[]>();
-	String src;
-	private final LuanTable env = new LuanTable();
-
-	PickleCon(LuanState luan,DataInputStream in,DataOutputStream out) {
-		this.in = in;
-		this.luan = luan;
-		try {
-			this._read_binary = new LuanJavaFunction(
-				PickleCon.class.getMethod( "_read_binary", Integer.TYPE ), this
-			);
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-		this.ioModule = (LuanTable)luan.loaded().get("Io");
-
-		this.out = out;
-	}
-
-	public byte[] _read_binary(int size) throws IOException, LuanException {
-		byte[] a = new byte[size];
-		int i = 0;
-		while( i < size ) {
-			int n = in.read(a,i,size-i);
-			if( n == -1 )
-				throw luan.exception( "end of stream" );
-			i += n;
-		}
-		return a;
-	}
-
-	public Object read() throws IOException, LuanException {
-		ioModule.put("_read_binary",_read_binary);
-		try {
-			src = in.readUTF();
-			LuanFunction fn = BasicLib.load(luan,src,"pickle-reader",env,false);
-			return luan.call(fn);
-		} finally {
-			ioModule.put("_binaries",null);
-			ioModule.put("_read_binary",null);
-		}
-	}
-
-	public String pickle(Object obj) throws LuanException {
-		if( obj == null )
-			return "nil";
-		if( obj instanceof Boolean )
-			return Luan.toString((Boolean)obj);
-		if( obj instanceof Number )
-			return Luan.toString((Number)obj);
-		if( obj instanceof String )
-			return "\"" + Luan.stringEncode((String)obj) + "\"";
-		if( obj instanceof LuanTable )
-			return pickle( (LuanTable)obj, Collections.newSetFromMap(new IdentityHashMap<LuanTable,Boolean>()) );
-		if( obj instanceof byte[] ) {
-			byte[] a = (byte[])obj;
-			binaries.add(a);
-			return "Io._binaries[" + binaries.size() + "]";
-		}
-		throw luan.exception( "invalid type: " + obj.getClass() );
-	}
-
-	private String pickle(Object obj,Set<LuanTable> set) throws LuanException {
-		return obj instanceof LuanTable ? pickle((LuanTable)obj,set) : pickle(obj);
-	}
-
-	private String pickle(LuanTable tbl,Set<LuanTable> set) throws LuanException {
-		if( !set.add(tbl) ) {
-			throw luan.exception( "circular reference in table" );
-		}
-		StringBuilder sb = new StringBuilder();
-		sb.append( "{" );
-		for( Map.Entry<Object,Object> entry : tbl ) {
-			sb.append( "[" );
-			sb.append( pickle(entry.getKey(),set) );
-			sb.append( "]=" );
-			sb.append( pickle(entry.getValue(),set) );
-			sb.append( ", " );
-		}
-		sb.append( "}" );
-		return sb.toString();
-	}
-
-	public void write(Object... args) throws LuanException, IOException {
-		StringBuilder sb = new StringBuilder();
-		if( !binaries.isEmpty() ) {
-			sb.append( "Io._binaries = {}\n" );
-			for( byte[] a : binaries ) {
-				sb.append( "Io._binaries[#Io._binaries+1] = Io._read_binary(" + a.length + ")\n" );
-			}
-		}
-		for( Object obj : args ) {
-			sb.append( luan.toString(obj) );
-		}
-		out.writeUTF( sb.toString() );
-		for( byte[] a : binaries ) {
-			out.write(a);
-		}
-		out.flush();
-		binaries.clear();
-	}
-
-	public void close() throws IOException {
-		in.close();
-		out.close();
-	}
-}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/lib/PickleServer.java
--- a/src/luan/lib/PickleServer.java	Sun Jun 22 04:17:38 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,115 +0,0 @@
-package luan.lib;
-
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.io.EOFException;
-import java.util.List;
-import java.util.ArrayList;
-import luan.Luan;
-import luan.LuanState;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanException;
-
-
-public final class PickleServer {
-
-	private final PickleCon con;
-	private boolean isRunning;
-
-	PickleServer(LuanState luan,DataInputStream in,DataOutputStream out) {
-		this(new PickleCon(luan,in,out));
-	}
-
-	PickleServer(PickleCon con) {
-		this.con = con;
-	}
-
-	void next() throws IOException {
-		try {
-			List<String> list = new ArrayList<String>();
-			try {
-				Object[] result = Luan.array(con.read());
-				list.add( "return true" );
-				for( Object obj : result ) {
-					list.add( ", " );
-					list.add( con.pickle(obj) );
-				}
-			} catch(LuanException e) {
-//				System.out.println(e);
-//e.printStackTrace();
-				list.add( "return false, " );
-				list.add( con.pickle(e.getMessage()) );
-				list.add( ", " );
-				list.add( con.pickle(con.src) );
-			}
-			list.add( "\n" );
-			con.write( list.toArray() );
-		} catch(LuanException e2) {
-			throw new RuntimeException(e2);
-		}
-	}
-
-	public void run() {
-		LuanTable ioModule = con.ioModule;
-		Object old_reverse_pickle = ioModule.get("reverse_pickle");
-		Object old_unreverse_pickle = ioModule.get("_unreverse_pickle");
-		try {
-			try {
-				ioModule.put("reverse_pickle", new LuanJavaFunction(
-					PickleServer.class.getMethod( "reverse_pickle", LuanFunction.class ), this
-				) );
-				ioModule.put("_unreverse_pickle", new LuanJavaFunction(
-					PickleServer.class.getMethod( "_unreverse_pickle" ), this
-				) );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			isRunning = true;
-			try {
-				while( isRunning ) {
-					next();
-				}
-			} catch(EOFException e) {
-				// done
-			} catch(IOException e) {
-				e.printStackTrace();
-			}
-			if( isRunning ) {
-				try {
-					con.close();
-				} catch(IOException e) {
-					throw new RuntimeException(e);
-				}
-			}
-		} finally {
-			ioModule.put("reverse_pickle",old_reverse_pickle);
-			ioModule.put("_unreverse_pickle",old_unreverse_pickle);
-		}
-	}
-
-	public void reverse_pickle(LuanFunction fn) throws IOException, LuanException {
-		try {
-			con.write( "return Io._reversed_pickle()\n" );
-		} catch(LuanException e) {
-			throw new RuntimeException(e);
-		}
-		PickleClient pc = new PickleClient(con);
-		try {
-			con.luan.call(fn,new Object[]{pc.table()});
-		} finally {
-			try {
-				pc.call( "Io._unreverse_pickle()\n" );
-			} catch(LuanException e) {
-				throw new RuntimeException(e);
-			}
-		}
-	}
-
-	public void _unreverse_pickle() {
-		isRunning = false;
-	}
-
-}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/lib/StringLib.java
--- a/src/luan/lib/StringLib.java	Sun Jun 22 04:17:38 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,264 +0,0 @@
-package luan.lib;
-
-import java.util.regex.Pattern;
-import java.util.regex.Matcher;
-import luan.Luan;
-import luan.LuanState;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanElement;
-import luan.LuanException;
-import luan.MetatableGetter;
-
-
-public final class StringLib {
-
-	public static final LuanFunction LOADER = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			luan.addMetatableGetter(mg);
-			LuanTable module = new LuanTable();
-			try {
-				add( module, "to_binary", String.class );
-				add( module, "to_integers", String.class );
-				add( module, "from_integers", new int[0].getClass() );
-				add( module, "find", String.class, String.class, Integer.class, Boolean.class );
-				add( module, "format", String.class, new Object[0].getClass() );
-				add( module, "gmatch", String.class, String.class );
-				add( module, "gsub", LuanState.class, String.class, String.class, Object.class, Integer.class );
-				add( module, "len", String.class );
-				add( module, "lower", String.class );
-				add( module, "match", String.class, String.class, Integer.class );
-				add( module, "rep", String.class, Integer.TYPE, String.class );
-				add( module, "reverse", String.class );
-				add( module, "sub", String.class, Integer.TYPE, Integer.class );
-				add( module, "upper", String.class );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			return module;
-		}
-	};
-
-	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
-		t.put( method, new LuanJavaFunction(StringLib.class.getMethod(method,parameterTypes),null) );
-	}
-
-	private static final LuanTable mt = new LuanTable();
-	static {
-		try {
-			add( mt, "__index", LuanState.class, String.class, Object.class );
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	private static final MetatableGetter mg = new MetatableGetter() {
-		public LuanTable getMetatable(Object obj) {
-			return obj instanceof String ? mt : null;
-		}
-	};
-
-	public static Object __index(LuanState luan,final String s,Object key) throws LuanException {
-		LuanTable mod = (LuanTable)luan.loaded().get("String");
-		if( mod!=null ) {
-			Object obj = mod.get(key);
-			if( obj instanceof LuanFunction ) {
-				final LuanFunction fn = (LuanFunction)obj;
-				return new LuanFunction() {
-					@Override public Object call(LuanState luan,Object[] args) throws LuanException {
-						Object[] a = new Object[args.length+1];
-						a[0] = s;
-						System.arraycopy(args,0,a,1,args.length);
-						return fn.call(luan,a);
-					}
-				};
-			}
-		}
-		if( luan.loaded().get("Java") != null )
-			return JavaLib.__index(luan,s,key);
-		return null;
-	}
-
-	static int start(String s,int i) {
-		return i==0 ? 0 : i > 0 ? i - 1 : s.length() + i;
-	}
-
-	static int start(String s,Integer i,int dflt) {
-		return i==null ? dflt : start(s,i);
-	}
-
-	static int end(String s,int i) {
-		return i==0 ? 0 : i > 0 ? i : s.length() + i + 1;
-	}
-
-	static int end(String s,Integer i,int dflt) {
-		return i==null ? dflt : end(s,i);
-	}
-
-	public static byte[] to_binary(String s) {
-		return s.getBytes();
-	}
-
-	public static int[] to_integers(String s) {
-		char[] a = s.toCharArray();
-		int[] chars = new int[a.length];
-		for( int i=0; i<a.length; i++ ) {
-			chars[i] = a[i];
-		}
-		return chars;
-	}
-
-	public static String from_integers(int... chars) {
-		char[] a = new char[chars.length];
-		for( int i=0; i<chars.length; i++ ) {
-			a[i] = (char)chars[i];
-		}
-		return new String(a);
-	}
-
-	public static int len(String s) {
-		return s.length();
-	}
-
-	public static String lower(String s) {
-		return s.toLowerCase();
-	}
-
-	public static String upper(String s) {
-		return s.toUpperCase();
-	}
-
-	public static String reverse(String s) {
-		return new StringBuilder(s).reverse().toString();
-	}
-
-	public static String rep(String s,int n,String sep) {
-		if( n < 1 )
-			return "";
-		StringBuilder buf = new StringBuilder(s);
-		while( --n > 0 ) {
-			if( sep != null )
-				buf.append(sep);
-			buf.append(s);
-		}
-		return buf.toString();
-	}
-
-	public static String sub(String s,int i,Integer j) {
-		int start = start(s,i);
-		int end = end(s,j,s.length());
-		return s.substring(start,end);
-	}
-
-	public static int[] find(String s,String pattern,Integer init,Boolean plain) {
-		int start = start(s,init,0);
-		if( Boolean.TRUE.equals(plain) ) {
-			int i = s.indexOf(pattern,start);
-			return i == -1 ? null : new int[]{i+1,i+pattern.length()};
-		}
-		Matcher m = Pattern.compile(pattern).matcher(s);
-		return m.find(start) ? new int[]{m.start()+1,m.end()} : null;
-	}
-
-	public static String[] match(String s,String pattern,Integer init) {
-		int start = start(s,init,0);
-		Matcher m = Pattern.compile(pattern).matcher(s);
-		if( !m.find(start) )
-			return null;
-		final int n = m.groupCount();
-		if( n == 0 )
-			return new String[]{m.group()};
-		String[] rtn = new String[n];
-		for( int i=0; i<n; i++ ) {
-			rtn[i] = m.group(i+1);
-		}
-		return rtn;
-	}
-
-	public static LuanFunction gmatch(String s,String pattern) {
-		final Matcher m = Pattern.compile(pattern).matcher(s);
-		return new LuanFunction() {
-			@Override public Object call(LuanState luan,Object[] args) {
-				if( !m.find() )
-					return null;
-				final int n = m.groupCount();
-				if( n == 0 )
-					return m.group();
-				String[] rtn = new String[n];
-				for( int i=0; i<n; i++ ) {
-					rtn[i] = m.group(i+1);
-				}
-				return rtn;
-			}
-		};
-	}
-
-	public static Object[] gsub(LuanState luan,String s,String pattern,Object repl,Integer n) throws LuanException {
-		int max = n==null ? Integer.MAX_VALUE : n;
-		final Matcher m = Pattern.compile(pattern).matcher(s);
-		if( repl instanceof String ) {
-			String replacement = (String)repl;
-			int i = 0;
-			StringBuffer sb = new StringBuffer();
-			while( i<max && m.find() ) {
-				m.appendReplacement(sb,replacement);
-				i++;
-			}
-			m.appendTail(sb);
-			return new Object[]{ sb.toString(), i };
-		}
-		if( repl instanceof LuanTable ) {
-			LuanTable t = (LuanTable)repl;
-			int i = 0;
-			StringBuffer sb = new StringBuffer();
-			while( i<max && m.find() ) {
-				String match = m.groupCount()==0 ? m.group() : m.group(1);
-				Object val = t.get(match);
-				if( Luan.toBoolean(val) ) {
-					String replacement = Luan.asString(val);
-					if( replacement==null )
-						throw luan.exception( "invalid replacement value (a "+Luan.type(val)+")" );
-					m.appendReplacement(sb,replacement);
-				}
-				i++;
-			}
-			m.appendTail(sb);
-			return new Object[]{ sb.toString(), i };
-		}
-		if( repl instanceof LuanFunction ) {
-			LuanFunction fn = (LuanFunction)repl;
-			int i = 0;
-			StringBuffer sb = new StringBuffer();
-			while( i<max && m.find() ) {
-				Object[] args;
-				final int count = m.groupCount();
-				if( count == 0 ) {
-					args = new Object[]{m.group()};
-				} else {
-					args = new Object[count];
-					for( int j=0; j<count; j++ ) {
-						args[j] = m.group(j);
-					}
-				}
-				Object val = Luan.first( luan.call(fn,"repl-arg",args) );
-				if( Luan.toBoolean(val) ) {
-					String replacement = Luan.asString(val);
-					if( replacement==null )
-						throw luan.exception( "invalid replacement value (a "+Luan.type(val)+")" );
-					m.appendReplacement(sb,replacement);
-				}
-				i++;
-			}
-			m.appendTail(sb);
-			return new Object[]{ sb.toString(), i };
-		}
-		throw luan.exception( "bad argument #3 to 'gsub' (string/function/table expected)" );
-	}
-
-	// note - String.format() is too stupid to convert between ints and floats.
-	public static String format(String format,Object... args) {
-		return String.format(format,args);
-	}
-
-}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/lib/TableLib.java
--- a/src/luan/lib/TableLib.java	Sun Jun 22 04:17:38 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-package luan.lib;
-
-import java.util.Comparator;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Arrays;
-import luan.Luan;
-import luan.LuanState;
-import luan.LuanTable;
-import luan.LuanFunction;
-import luan.LuanJavaFunction;
-import luan.LuanElement;
-import luan.LuanException;
-import luan.LuanRuntimeException;
-
-
-public final class TableLib {
-
-	public static final LuanFunction LOADER = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
-			try {
-				add( module, "concat", LuanState.class, LuanTable.class, String.class, Integer.class, Integer.class );
-				add( module, "insert", LuanState.class, LuanTable.class, Integer.TYPE, Object.class );
-				add( module, "pack", new Object[0].getClass() );
-				add( module, "remove", LuanState.class, LuanTable.class, Integer.TYPE );
-				add( module, "sort", LuanState.class, LuanTable.class, LuanFunction.class );
-				add( module, "sub_list", LuanTable.class, Integer.TYPE, Integer.TYPE );
-				add( module, "unpack", LuanTable.class, Integer.class, Integer.class );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			return module;
-		}
-	};
-
-	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
-		t.put( method, new LuanJavaFunction(TableLib.class.getMethod(method,parameterTypes),null) );
-	}
-
-	public static String concat(LuanState luan,LuanTable list,String sep,Integer i,Integer j) throws LuanException {
-		int first = i==null ? 1 : i;
-		int last = j==null ? list.length() : j;
-		StringBuilder buf = new StringBuilder();
-		for( int k=first; k<=last; k++ ) {
-			Object val = list.get(k);
-			if( val==null )
-				break;
-			if( sep!=null && k > first )
-				buf.append(sep);
-			String s = Luan.asString(val);
-			if( s==null )
-				throw luan.exception( "invalid value ("+Luan.type(val)+") at index "+k+" in table for 'concat'" );
-			buf.append(val);
-		}
-		return buf.toString();
-	}
-
-	public static void insert(LuanState luan,LuanTable list,int pos,Object value) throws LuanException {
-		try {
-			list.insert(pos,value);
-		} catch(IndexOutOfBoundsException e) {
-			throw luan.exception(e);
-		}
-	}
-
-	public static Object remove(LuanState luan,LuanTable list,int pos) throws LuanException {
-		try {
-			return list.remove(pos);
-		} catch(IndexOutOfBoundsException e) {
-			throw luan.exception(e);
-		}
-	}
-
-	private static interface LessThan {
-		public boolean isLessThan(Object o1,Object o2);
-	}
-
-	public static void sort(final LuanState luan,LuanTable list,final LuanFunction comp) throws LuanException {
-		final LessThan lt;
-		if( comp==null ) {
-			lt = new LessThan() {
-				public boolean isLessThan(Object o1,Object o2) {
-					try {
-						return luan.isLessThan(o1,o2);
-					} catch(LuanException e) {
-						throw new LuanRuntimeException(e);
-					}
-				}
-			};
-		} else {
-			lt = new LessThan() {
-				public boolean isLessThan(Object o1,Object o2) {
-					try {
-						return Luan.toBoolean(Luan.first(luan.call(comp,"comp-arg",new Object[]{o1,o2})));
-					} catch(LuanException e) {
-						throw new LuanRuntimeException(e);
-					}
-				}
-			};
-		}
-		try {
-			list.sort( new Comparator<Object>() {
-				public int compare(Object o1,Object o2) {
-					return lt.isLessThan(o1,o2) ? -1 : lt.isLessThan(o2,o1) ? 1 : 0;
-				}
-			} );
-		} catch(LuanRuntimeException e) {
-			throw (LuanException)e.getCause();
-		}
-	}
-
-	public static LuanTable pack(Object... args) {
-		return new LuanTable(new ArrayList<Object>(Arrays.asList(args)));
-	}
-
-	public static Object[] unpack(LuanTable tbl,Integer iFrom,Integer iTo) {
-		int from = iFrom!=null ? iFrom : 1;
-		int to = iTo!=null ? iTo : tbl.length();
-		List<Object> list = new ArrayList<Object>();
-		for( int i=from; i<=to; i++ ) {
-			list.add( tbl.get(i) );
-		}
-		return list.toArray();
-	}
-
-	public static LuanTable sub_list(LuanTable list,int from,int to) {
-		return list.subList(from,to);
-	}
-
-}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/lib/ThreadLib.java
--- a/src/luan/lib/ThreadLib.java	Sun Jun 22 04:17:38 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-package luan.lib;
-
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-import luan.LuanState;
-import luan.LuanFunction;
-import luan.LuanTable;
-import luan.LuanJavaFunction;
-import luan.LuanException;
-import luan.DeepCloner;
-
-
-public final class ThreadLib {
-
-	public static final LuanFunction LOADER = new LuanFunction() {
-		@Override public Object call(LuanState luan,Object[] args) {
-			LuanTable module = new LuanTable();
-			try {
-				add( module, "fork", LuanState.class, LuanFunction.class, new Object[0].getClass() );
-			} catch(NoSuchMethodException e) {
-				throw new RuntimeException(e);
-			}
-			return module;
-		}
-	};
-
-	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
-		t.put( method, new LuanJavaFunction(ThreadLib.class.getMethod(method,parameterTypes),null) );
-	}
-
-	private static final Executor exec = Executors.newCachedThreadPool();
-
-	public static void fork(LuanState luan,LuanFunction fn,Object... args) {
-		DeepCloner cloner = new DeepCloner();
-		final LuanState newLuan = cloner.deepClone(luan);
-		final LuanFunction newFn = cloner.get(fn);
-		final Object[] newArgs = cloner.deepClone(args);
-		exec.execute(new Runnable(){public void run() {
-			try {
-				newLuan.call(newFn,"<forked>",newArgs);
-			} catch(LuanException e) {
-				e.printStackTrace();
-			}
-		}});
-	}
-
-}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/lib/Utils.java
--- a/src/luan/lib/Utils.java	Sun Jun 22 04:17:38 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-package luan.lib;
-
-import java.io.Reader;
-import java.io.IOException;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.File;
-import java.net.URL;
-import java.net.MalformedURLException;
-import luan.LuanState;
-import luan.LuanException;
-
-
-public final class Utils {
-	private Utils() {}  // never
-
-	static final int bufSize = 8192;
-
-	public static void checkNotNull(LuanState luan,Object v,String expected) throws LuanException {
-		if( v == null )
-			throw luan.exception("bad argument #1 ("+expected+" expected, got nil)");
-	}
-
-	public static String readAll(Reader in)
-		throws IOException
-	{
-		char[] a = new char[bufSize];
-		StringBuilder buf = new StringBuilder();
-		int n;
-		while( (n=in.read(a)) != -1 ) {
-			buf.append(a,0,n);
-		}
-		return buf.toString();
-	}
-
-	public static void copyAll(InputStream in,OutputStream out)
-		throws IOException
-	{
-		byte[] a = new byte[bufSize];
-		int n;
-		while( (n=in.read(a)) != -1 ) {
-			out.write(a,0,n);
-		}
-	}
-
-	public static byte[] readAll(InputStream in)
-		throws IOException
-	{
-		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		copyAll(in,out);
-		return out.toByteArray();
-	}
-
-	public static boolean exists(File file) {
-		try {
-			return file.exists() && file.getName().equals(file.getCanonicalFile().getName());
-		} catch(IOException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	public static boolean isFile(String path) {
-		return exists(new File(path));
-	}
-
-	public static String toUrl(String path) {
-		if( path.indexOf(':') == -1 )
-			return null;
-		if( path.startsWith("java:") ) {
-			path = path.substring(5);
-			URL url = ClassLoader.getSystemResource(path);
-			return url==null ? null : url.toString();
-		}
-		try {
-			new URL(path);
-			return path;
-		} catch(MalformedURLException e) {}
-		return null;
-	}
-
-	public static boolean exists(String path) {
-		return isFile(path) || toUrl(path)!=null;
-	}
-}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/lib/init.luan
--- a/src/luan/lib/init.luan	Sun Jun 22 04:17:38 2014 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,107 +0,0 @@
-function Package.global(module,fn_name)
-	local function fn(...)
-		return module[fn_name](...)
-	end
-	_G[fn_name] = fn
-	return fn
-end
-
-local require = Package.global(Package,"require")
-
-function Package.global_import(name)
-	local mod = require(name)
-	_G[name] = mod
-	return mod
-end
-
-local Basic = Package.global_import("Basic","luan.lib.BasicLib.LOADER")
-Package.global(Basic,"assert")
-Package.global(Basic,"assert_boolean")
-Package.global(Basic,"assert_nil")
-Package.global(Basic,"assert_number")
-Package.global(Basic,"assert_string")
-Package.global(Basic,"assert_table")
-Package.global(Basic,"do_file")
-Package.global(Basic,"error")
-Package.global(Basic,"get_metatable")
-Package.global(Basic,"ipairs")
-local load = Package.global(Basic,"load")
-Package.global(Basic,"load_file")
-Package.global(Basic,"pairs")
-Package.global(Basic,"range")
-Package.global(Basic,"raw_equal")
-Package.global(Basic,"raw_get")
-Package.global(Basic,"raw_len")
-Package.global(Basic,"raw_set")
-Package.global(Basic,"repr")
-Package.global(Basic,"set_metatable")
-Package.global(Basic,"to_number")
-local to_string = Package.global(Basic,"to_string")
-Package.global(Basic,"type")
-
-local String = Package.global_import("String","luan.lib.StringLib.LOADER")
-
--- improved global_import
-function Package.global_import(name)
-	local short = name.match("\.([^.]+)$") or name
-	local mod = require(name)
-	_G[short] = mod
-	return mod
-end
-
-local Table = Package.global_import("Table","luan.lib.TableLib.LOADER")
-local Io = Package.global_import("Io","luan.lib.IoLib.LOADER")
-Package.global_import("Math","luan.lib.MathLib.LOADER")
-Package.global_import("Html","luan.lib.HtmlLib.LOADER")
-Package.global_import("Thread","luan.lib.ThreadLib.LOADER")
-Package.global_import("Binary","luan.lib.BinaryLib.LOADER")
-
-
-function Io.print_to(out,...)
-	local list = {}
-	for _,v in Basic.values(...) do
-		list[#list+1] = to_string(v)
-		list[#list+1] = '\t'
-	end
-	if #list == 0 then
-		out.write( '\n' )
-	else
-		list[#list] = '\n'
-		out.write( Table.unpack(list) )
-	end
-end
-
-function Basic.print(...)
-	Io.print_to(Io.stdout,...)
-end
-local print = Package.global(Basic,"print")
-
-local Debug = {}
-Package.loaded.Debug = Debug
-_G.Debug = Debug
-
-function Debug.print_if_something(...)
-	if Table.pack(...).n > 0 then
-		print(...)
-	end
-end
-
-function Debug.debug(prompt)
-	prompt = prompt or "luan_debug> "
-	local function console()
-		return Io.read_console_line(prompt)
-	end
-	local env = {}
-	for line in console do
-		try
-			local fn = load(line,"stdin",env,true)
-			Debug.print_if_something( fn() )
-		catch e do
-			print(e)
-		end
-	end
-end
-
-
--- import modules
-Package.global_import("Reactionary")
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/modules/BasicLib.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/BasicLib.java	Sun Jun 22 04:28:32 2014 +0000
@@ -0,0 +1,222 @@
+package luan.modules;
+
+import java.io.File;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
+import luan.Luan;
+import luan.LuanState;
+import luan.LuanTable;
+import luan.LuanFunction;
+import luan.LuanJavaFunction;
+import luan.LuanException;
+import luan.LuanSource;
+import luan.LuanElement;
+import luan.impl.LuanCompiler;
+
+
+public final class BasicLib {
+
+	public static final LuanFunction LOADER = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			LuanTable module = new LuanTable();
+			try {
+				module.put( "assert", new LuanJavaFunction(BasicLib.class.getMethod("assert_",LuanState.class,Object.class,String.class),null) );
+				add( module, "assert_boolean", LuanState.class, Boolean.TYPE );
+				add( module, "assert_nil", LuanState.class, Object.class );
+				add( module, "assert_number", LuanState.class, Number.class );
+				add( module, "assert_string", LuanState.class, String.class );
+				add( module, "assert_table", LuanState.class, LuanTable.class );
+				add( module, "do_file", LuanState.class, String.class );
+				add( module, "error", LuanState.class, Object.class );
+				add( module, "get_metatable", LuanState.class, Object.class );
+				add( module, "ipairs", LuanState.class, LuanTable.class );
+				add( module, "load", LuanState.class, String.class, String.class, LuanTable.class, Boolean.class );
+				add( module, "load_file", LuanState.class, String.class );
+				add( module, "pairs", LuanState.class, LuanTable.class );
+				add( module, "range", LuanState.class, Double.TYPE, Double.TYPE, Double.class );
+				add( module, "raw_equal", Object.class, Object.class );
+				add( module, "raw_get", LuanTable.class, Object.class );
+				add( module, "raw_len", LuanState.class, Object.class );
+				add( module, "raw_set", LuanTable.class, Object.class, Object.class );
+				add( module, "repr", LuanState.class, Object.class );
+				add( module, "set_metatable", LuanTable.class, LuanTable.class );
+				add( module, "to_number", Object.class, Integer.class );
+				add( module, "to_string", LuanState.class, Object.class );
+				add( module, "type", Object.class );
+				add( module, "values", new Object[0].getClass() );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			return module;
+		}
+	};
+
+	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
+		t.put( method, new LuanJavaFunction(BasicLib.class.getMethod(method,parameterTypes),null) );
+	}
+
+	public static String type(Object obj) {
+		return Luan.type(obj);
+	}
+
+	public static LuanFunction load(LuanState luan,String text,String sourceName,LuanTable env,Boolean allowExpr)
+		throws LuanException
+	{
+		if( allowExpr==null )
+			allowExpr = false;
+		return LuanCompiler.compile(luan,new LuanSource(sourceName,text),env,allowExpr);
+	}
+
+	public static LuanFunction load_file(LuanState luan,String fileName) throws LuanException {
+		try {
+			String src = fileName==null ? Utils.readAll(new InputStreamReader(System.in)) : IoLib.luanIo(luan,fileName).read_text();
+			return load(luan,src,fileName,null,false);
+		} catch(IOException e) {
+			throw luan.exception(e);
+		}
+	}
+
+	public static Object do_file(LuanState luan,String fileName) throws LuanException {
+		LuanFunction fn = load_file(luan,fileName);
+		return luan.call(fn);
+	}
+
+	private static LuanFunction pairs(final Iterator<Map.Entry<Object,Object>> iter) {
+		return new LuanFunction() {
+			@Override public Object[] call(LuanState luan,Object[] args) {
+				if( !iter.hasNext() )
+					return LuanFunction.NOTHING;
+				Map.Entry<Object,Object> entry = iter.next();
+				return new Object[]{entry.getKey(),entry.getValue()};
+			}
+		};
+	}
+
+	public static LuanFunction pairs(LuanState luan,LuanTable t) throws LuanException {
+		Utils.checkNotNull(luan,t,"table");
+		return pairs( t.iterator() );
+	}
+
+	public static LuanFunction ipairs(LuanState luan,LuanTable t) throws LuanException {
+		Utils.checkNotNull(luan,t,"table");
+		return pairs( t.listIterator() );
+	}
+
+	public static LuanTable get_metatable(LuanState luan,Object obj) {
+		return luan.getMetatable(obj);
+	}
+
+	public static LuanTable set_metatable(LuanTable table,LuanTable metatable) {
+		table.setMetatable(metatable);
+		return table;
+	}
+
+	public static boolean raw_equal(Object v1,Object v2) {
+		return v1 == v2 || v1 != null && v1.equals(v2);
+	}
+
+	public static Object raw_get(LuanTable table,Object index) {
+		return table.get(index);
+	}
+
+	public static LuanTable raw_set(LuanTable table,Object index,Object value) {
+		table.put(index,value);
+		return table;
+	}
+
+	public static int raw_len(LuanState luan,Object v) throws LuanException {
+		if( v instanceof String ) {
+			String s = (String)v;
+			return s.length();
+		}
+		if( v instanceof LuanTable ) {
+			LuanTable t = (LuanTable)v;
+			return t.length();
+		}
+		throw luan.exception( "bad argument #1 to 'raw_len' (table or string expected)" );
+	}
+
+	public static Number to_number(Object e,Integer base) {
+		return Luan.toNumber(e,base);
+	}
+
+	public static String to_string(LuanState luan,Object v) throws LuanException {
+		return luan.toString(v);
+	}
+
+	public static void error(LuanState luan,Object msg) throws LuanException {
+		throw luan.exception(msg);
+	}
+
+	public static Object assert_(LuanState luan,Object v,String msg) throws LuanException {
+		if( Luan.toBoolean(v) )
+			return v;
+		if( msg == null )
+			msg = "assertion failed!";
+		throw luan.exception( msg );
+	}
+
+	public static String assert_string(LuanState luan,String v) throws LuanException {
+		Utils.checkNotNull(luan,v,"string");
+		return v;
+	}
+
+	public static Number assert_number(LuanState luan,Number v) throws LuanException {
+		Utils.checkNotNull(luan,v,"number");
+		return v;
+	}
+
+	public static LuanTable assert_table(LuanState luan,LuanTable v) throws LuanException {
+		Utils.checkNotNull(luan,v,"table");
+		return v;
+	}
+
+	public static boolean assert_boolean(LuanState luan,boolean v) throws LuanException {
+		return v;
+	}
+
+	public static Object assert_nil(LuanState luan,Object v) throws LuanException {
+		if( v != null )
+			throw luan.exception("bad argument #1 (nil expected, got "+Luan.type(v)+")");
+		return v;
+	}
+
+	public static String repr(LuanState luan,Object v) throws LuanException {
+		return luan.repr(v);
+	}
+
+	public static LuanFunction range(LuanState luan,final double from,final double to,Double stepV) throws LuanException {
+		final double step = stepV==null ? 1.0 : stepV;
+		if( step == 0.0 )
+			throw luan.exception("bad argument #3 (step may not be zero)");
+		return new LuanFunction() {
+			double v = from;
+
+			@Override public Object call(LuanState luan,Object[] args) {
+				if( step > 0.0 && v > to || step < 0.0 && v < to )
+					return LuanFunction.NOTHING;
+				double rtn = v;
+				v += step;
+				return rtn;
+			}
+		};
+	}
+
+	public static LuanFunction values(final Object... args) throws LuanException {
+		return new LuanFunction() {
+			int i = 0;
+
+			@Override public Object call(LuanState luan,Object[] unused) {
+				if( ++i > args.length )
+					return LuanFunction.NOTHING;
+				return new Object[]{i,args[i-1]};
+			}
+		};
+	}
+
+}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/modules/BinaryLib.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/BinaryLib.java	Sun Jun 22 04:28:32 2014 +0000
@@ -0,0 +1,32 @@
+package luan.modules;
+
+import luan.LuanState;
+import luan.LuanTable;
+import luan.LuanFunction;
+import luan.LuanJavaFunction;
+import luan.LuanException;
+
+
+public final class BinaryLib {
+
+	public static final LuanFunction LOADER = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			LuanTable module = new LuanTable();
+			try {
+				add( module, "to_string", new byte[0].getClass() );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			return module;
+		}
+	};
+
+	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
+		t.put( method, new LuanJavaFunction(BinaryLib.class.getMethod(method,parameterTypes),null) );
+	}
+
+	public static String to_string(byte[] bytes) {
+		return new String(bytes);
+	}
+
+}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/modules/HtmlLib.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/HtmlLib.java	Sun Jun 22 04:28:32 2014 +0000
@@ -0,0 +1,53 @@
+package luan.modules;
+
+import luan.LuanState;
+import luan.LuanTable;
+import luan.LuanFunction;
+import luan.LuanJavaFunction;
+import luan.LuanException;
+
+
+public final class HtmlLib {
+
+	public static final LuanFunction LOADER = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			LuanTable module = new LuanTable();
+			try {
+				add( module, "encode", String.class );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			return module;
+		}
+	};
+
+	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
+		t.put( method, new LuanJavaFunction(HtmlLib.class.getMethod(method,parameterTypes),null) );
+	}
+
+	public static String encode(String s) {
+		char[] a = s.toCharArray();
+		StringBuilder buf = new StringBuilder();
+		for( int i=0; i<a.length; i++ ) {
+			char c = a[i];
+			switch(c) {
+			case '&':
+				buf.append("&amp;");
+				break;
+			case '<':
+				buf.append("&lt;");
+				break;
+			case '>':
+				buf.append("&gt;");
+				break;
+			case '"':
+				buf.append("&quot;");
+				break;
+			default:
+				buf.append(c);
+			}
+		}
+		return buf.toString();
+	}
+
+}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/modules/HttpLib.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/HttpLib.java	Sun Jun 22 04:28:32 2014 +0000
@@ -0,0 +1,285 @@
+package luan.modules;
+
+import java.io.PrintWriter;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Set;
+import java.util.Arrays;
+import java.util.Enumeration;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import luan.Luan;
+import luan.LuanState;
+import luan.LuanFunction;
+import luan.LuanElement;
+import luan.LuanException;
+import luan.LuanTable;
+import luan.LuanJavaFunction;
+import luan.LuanExitException;
+import luan.DeepCloner;
+
+
+public final class HttpLib {
+
+	public static final LuanFunction LOADER = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			return new LuanTable();  // starts empty
+		}
+	};
+
+	public static void service(LuanState luan,HttpServletRequest request,HttpServletResponse response,String modName)
+		throws LuanException
+	{
+		LuanFunction fn;
+		synchronized(luan) {
+			Object mod = PackageLib.require(luan,modName);
+			if( !(mod instanceof LuanTable) )
+				throw luan.exception( "module '"+modName+"' must return a table" );
+			LuanTable tbl = (LuanTable)mod;
+			if( Luan.toBoolean( tbl.get("per_session") ) ) {
+				HttpSession session = request.getSession();
+				LuanState sessionLuan  = (LuanState)session.getValue("luan");
+				if( sessionLuan!=null ) {
+					luan = sessionLuan;
+				} else {
+					DeepCloner cloner = new DeepCloner();
+					luan = cloner.deepClone(luan);
+					session.putValue("luan",luan);
+				}
+				tbl = (LuanTable)PackageLib.require(luan,modName);
+				fn = (LuanFunction)tbl.get("page");
+			} else {
+				fn = (LuanFunction)tbl.get("page");
+				if( fn == null )
+					throw luan.exception( "function 'page' is not defined" );
+				DeepCloner cloner = new DeepCloner();
+				luan = cloner.deepClone(luan);
+				fn = cloner.get(fn);
+			}
+		}
+
+		LuanTable module = (LuanTable)luan.loaded().get("Http");
+		if( module == null )
+			throw luan.exception( "module 'Http' not defined" );
+		HttpLib lib = new HttpLib(request,response);
+		try {
+			module.put( "request", lib.requestTable() );
+			module.put( "response", lib.responseTable() );
+/*
+			module.put( "write", new LuanJavaFunction(
+				HttpLib.class.getMethod( "text_write", LuanState.class, new Object[0].getClass() ), lib
+			) );
+*/
+		} catch(NoSuchMethodException e) {
+			throw new RuntimeException(e);
+		}
+
+		luan.call(fn,"<http>");
+	}
+
+
+
+	private final HttpServletRequest request;
+	private final HttpServletResponse response;
+//	private PrintWriter writer = null;
+//	private ServletOutputStream sos = null;
+
+	private HttpLib(HttpServletRequest request,HttpServletResponse response) {
+		this.request = request;
+		this.response = response;
+	}
+
+	private LuanTable requestTable() throws NoSuchMethodException {
+		LuanTable req = new LuanTable();
+		req.put("java",request);
+		req.put( "get_attribute", new LuanJavaFunction(HttpServletRequest.class.getMethod("getAttribute",String.class),request) );
+		req.put( "set_attribute", new LuanJavaFunction(HttpServletRequest.class.getMethod("setAttribute",String.class,Object.class),request) );
+		req.put( "get_parameter", new LuanJavaFunction(HttpServletRequest.class.getMethod("getParameter",String.class),request) );
+		req.put( "get_parameter_values", new LuanJavaFunction(HttpServletRequest.class.getMethod("getParameterValues",String.class),request) );
+		req.put( "get_header", new LuanJavaFunction(HttpServletRequest.class.getMethod("getHeader",String.class),request) );
+		add( req, "get_cookie_value", String.class );
+		req.put( "method", new LuanJavaFunction(HttpServletRequest.class.getMethod("getMethod"),request) );
+		req.put( "servlet_path", new LuanJavaFunction(HttpServletRequest.class.getMethod("getServletPath"),request) );
+		req.put( "server_name", new LuanJavaFunction(HttpServletRequest.class.getMethod("getServerName"),request) );
+		add( req, "current_url" );
+		req.put( "remote_address", new LuanJavaFunction(HttpServletRequest.class.getMethod("getRemoteAddr"),request) );
+		add( req, "get_session_attribute", String.class );
+		add( req, "set_session_attribute", String.class, Object.class );
+		return req;
+	}
+
+	private LuanTable responseTable() throws NoSuchMethodException {
+		LuanTable resp = new LuanTable();
+		resp.put("java",response);
+		add( resp, "send_redirect", String.class );
+		add( resp, "send_error", Integer.TYPE, String.class );
+		resp.put( "contains_header", new LuanJavaFunction(HttpServletResponse.class.getMethod("containsHeader",String.class),response) );
+		resp.put( "set_header", new LuanJavaFunction(HttpServletResponse.class.getMethod("setHeader",String.class,String.class),response) );
+		add( resp, "set_cookie", String.class, String.class, Boolean.TYPE, String.class );
+		add( resp, "remove_cookie", String.class, String.class );
+		resp.put( "set_content_type", new LuanJavaFunction(HttpServletResponse.class.getMethod("setContentType",String.class),response) );
+		add( resp, "text_writer" );
+		return resp;
+	}
+
+	private void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
+		t.put( method, new LuanJavaFunction(HttpLib.class.getMethod(method,parameterTypes),this) );
+	}
+/*
+	public void text_write(LuanState luan,Object... args) throws LuanException, IOException {
+		if( writer == null )
+			writer = response.getWriter();
+		for( Object obj : args ) {
+			writer.print( luan.toString(obj) );
+		}
+	}
+*/
+	public LuanTable text_writer() throws IOException {
+		return IoLib.textWriter(response.getWriter());
+	}
+
+	public String get_cookie_value(String name) {
+		return getCookieValue(request, name);
+	}
+
+	public String current_url() {
+		return getCurrentURL(request);
+	}
+
+	public void send_redirect(String redirectUrl)
+		throws IOException
+	{
+		response.sendRedirect(redirectUrl);
+		throw new LuanExitException();
+	}
+
+	public void send_error(int code,String text)
+		throws IOException
+	{
+		response.sendError(code, text);
+		throw new LuanExitException();
+	}
+
+	public void set_cookie(String name,String value,boolean isPersistent, String domain) {
+		setCookie(request,response,name,value,isPersistent,domain);
+	}
+
+	public void remove_cookie(String name, String domain) {
+		removeCookie(request,response,name,domain);
+	}
+
+	public Object get_session_attribute(String name) {
+		return request.getSession().getAttribute(name);
+	}
+
+	public void set_session_attribute(String name,Object value) {
+		request.getSession().setAttribute(name,value);
+	}
+
+
+	// static utils
+
+	public static String getQueryString(HttpServletRequest request) {
+		return getQueryString(request,0);
+	}
+
+	public static String getQueryString(HttpServletRequest request,int maxValueLen) {
+		String method = request.getMethod();
+		if( method.equals("GET") )
+			return request.getQueryString();
+		if( !method.equals("POST") && !method.equals("HEAD") )
+			throw new RuntimeException(method);
+		Enumeration en = request.getParameterNames();
+		StringBuilder queryBuf = new StringBuilder();
+		if( !en.hasMoreElements() )
+			return null;
+		do {
+			String param = (String)en.nextElement();
+			String value = request.getParameter(param);
+			if( maxValueLen > 0 ) {
+				int len = value.length();
+				if( len > maxValueLen )
+					value = value.substring(0,maxValueLen) + "..." + (len-maxValueLen);
+			}
+			queryBuf.append(param);
+			queryBuf.append('=');
+			queryBuf.append(value);
+			queryBuf.append('&');
+		} while( en.hasMoreElements() );
+		queryBuf.deleteCharAt(queryBuf.length() - 1);
+		return queryBuf.toString();
+	}
+
+	public static String getCurrentURL(HttpServletRequest request) {
+		return getCurrentURL(request,0);
+	}
+
+	public static String getCurrentURL(HttpServletRequest request,int maxValueLen) {
+//		StringBuffer buf = HttpUtils.getRequestURL(request);
+		StringBuffer buf = request.getRequestURL();
+		String qStr = getQueryString(request,maxValueLen);
+		if(qStr != null && qStr.length() > 0) {
+			buf.append('?');
+			buf.append(qStr);
+		}
+		return buf.toString();
+	}
+
+	private static String escape(String value) {
+		return value.replaceAll(";", "%3B");
+	}
+
+	private static String unescape(String value) {
+		return value.replaceAll("%3B", ";");
+	}
+
+	private static Cookie getCookie(HttpServletRequest request,String name) {
+		Cookie[] cookies = request.getCookies();
+		if( cookies == null )
+			return null;
+		for (Cookie cookie : cookies) {
+			if (cookie.getName().equals(name))
+				return cookie;
+		}
+		return null;
+	}
+
+	public static String getCookieValue(HttpServletRequest request,String name) {
+		Cookie cookie = getCookie(request,name);
+		return cookie==null ? null : unescape(cookie.getValue());
+	}
+
+	public static void setCookie(HttpServletRequest request,HttpServletResponse response,String name,String value,boolean isPersistent, String domain) {
+		Cookie cookie = getCookie(request,name);
+		if( cookie==null || !cookie.getValue().equals(value) ) {
+			cookie = new Cookie(name, escape(value));
+			cookie.setPath("/");
+			if (domain != null && domain.length() > 0)
+				cookie.setDomain(domain);
+			if( isPersistent )
+				cookie.setMaxAge(10000000);
+			response.addCookie(cookie);
+		}
+	}
+
+	public static void removeCookie(HttpServletRequest request,
+									HttpServletResponse response,
+									String name,
+									String domain
+
+	) {
+		Cookie cookie = getCookie(request, name);
+		if(cookie != null) {
+			Cookie delCookie = new Cookie(name, "delete");
+			delCookie.setPath("/");
+			delCookie.setMaxAge(0);
+			if (domain != null && domain.length() > 0)
+				delCookie.setDomain(domain);
+			response.addCookie(delCookie);
+		}
+	}
+
+}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/modules/IoLib.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/IoLib.java	Sun Jun 22 04:28:32 2014 +0000
@@ -0,0 +1,504 @@
+package luan.modules;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.net.Socket;
+import java.net.ServerSocket;
+import java.net.MalformedURLException;
+import luan.LuanState;
+import luan.LuanTable;
+import luan.LuanFunction;
+import luan.LuanJavaFunction;
+import luan.LuanException;
+
+
+public final class IoLib {
+
+	public static final LuanFunction LOADER = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			LuanTable module = new LuanTable();
+			try {
+				add( module, "File", LuanState.class, String.class );
+				add( module, "read_console_line", String.class );
+
+				LuanTable stdin = new LuanTable();
+				stdin.put( "read_text", new LuanJavaFunction(
+					IoLib.class.getMethod( "stdin_read_text" ), null
+				) );
+				stdin.put( "read_binary", new LuanJavaFunction(
+					IoLib.class.getMethod( "stdin_read_binary" ), null
+				) );
+				stdin.put( "read_lines", new LuanJavaFunction(
+					IoLib.class.getMethod( "stdin_read_lines" ), null
+				) );
+				stdin.put( "read_blocks", new LuanJavaFunction(
+					IoLib.class.getMethod( "stdin_read_blocks", Integer.class ), null
+				) );
+				module.put( "stdin", stdin );
+
+				add( module, "Socket", String.class, Integer.TYPE );
+				add( module, "socket_server", Integer.TYPE );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			module.put( "stdout", textWriter(System.out) );
+			module.put( "stderr", textWriter(System.err) );
+			return module;
+		}
+	};
+
+	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
+		t.put( method, new LuanJavaFunction(IoLib.class.getMethod(method,parameterTypes),null) );
+	}
+
+
+	public static String stdin_read_text() throws IOException {
+		return Utils.readAll(new InputStreamReader(System.in));
+	}
+
+	public static byte[] stdin_read_binary() throws IOException {
+		return Utils.readAll(System.in);
+	}
+
+	public static LuanFunction stdin_read_lines() throws IOException {
+		return lines(new BufferedReader(new InputStreamReader(System.in)));
+	}
+
+	public static LuanFunction stdin_read_blocks(Integer blockSize) throws IOException {
+		int n = blockSize!=null ? blockSize : Utils.bufSize;
+		return blocks(System.in,n);
+	}
+
+	public static String read_console_line(String prompt) throws IOException {
+		if( prompt==null )
+			prompt = "> ";
+		return System.console().readLine(prompt);
+	}
+
+
+	public interface LuanWriter {
+		public void write(LuanState luan,Object... args) throws LuanException, IOException;
+		public void close() throws IOException;
+	}
+
+	public static LuanTable textWriter(final PrintStream out) {
+		LuanWriter luanWriter = new LuanWriter() {
+
+			public void write(LuanState luan,Object... args) throws LuanException {
+				for( Object obj : args ) {
+					out.print( luan.toString(obj) );
+				}
+			}
+
+			public void close() {
+				out.close();
+			}
+		};
+		return writer(luanWriter);
+	}
+
+	public static LuanTable textWriter(final Writer out) {
+		LuanWriter luanWriter = new LuanWriter() {
+
+			public void write(LuanState luan,Object... args) throws LuanException, IOException {
+				for( Object obj : args ) {
+					out.write( luan.toString(obj) );
+				}
+			}
+
+			public void close() throws IOException {
+				out.close();
+			}
+		};
+		return writer(luanWriter);
+	}
+
+	private static LuanTable writer(LuanWriter luanWriter) {
+		LuanTable writer = new LuanTable();
+		try {
+			writer.put( "write", new LuanJavaFunction(
+				LuanWriter.class.getMethod( "write", LuanState.class, new Object[0].getClass() ), luanWriter
+			) );
+			writer.put( "close", new LuanJavaFunction(
+				LuanWriter.class.getMethod( "close" ), luanWriter
+			) );
+		} catch(NoSuchMethodException e) {
+			throw new RuntimeException(e);
+		}
+		return writer;
+	}
+
+
+	public static LuanTable binaryWriter(final OutputStream out) {
+		LuanTable writer = new LuanTable();
+		try {
+			writer.put( "write", new LuanJavaFunction(
+				OutputStream.class.getMethod( "write", new byte[0].getClass() ), out
+			) );
+			writer.put( "close", new LuanJavaFunction(
+				OutputStream.class.getMethod( "close" ), out
+			) );
+		} catch(NoSuchMethodException e) {
+			throw new RuntimeException(e);
+		}
+		return writer;
+	}
+
+	static LuanFunction lines(final BufferedReader in) {
+		return new LuanFunction() {
+			@Override public Object call(LuanState luan,Object[] args) throws LuanException {
+				try {
+					if( args.length > 0 ) {
+						if( args.length > 1 || !"close".equals(args[0]) )
+							throw luan.exception( "the only argument allowed is 'close'" );
+						in.close();
+						return null;
+					}
+					String rtn = in.readLine();
+					if( rtn==null )
+						in.close();
+					return rtn;
+				} catch(IOException e) {
+					throw luan.exception(e);
+				}
+			}
+		};
+	}
+
+	static LuanFunction blocks(final InputStream in,final int blockSize) {
+		return new LuanFunction() {
+			final byte[] a = new byte[blockSize];
+
+			@Override public Object call(LuanState luan,Object[] args) throws LuanException {
+				try {
+					if( args.length > 0 ) {
+						if( args.length > 1 || !"close".equals(args[0]) )
+							throw luan.exception( "the only argument allowed is 'close'" );
+						in.close();
+						return null;
+					}
+					if( in.read(a) == -1 ) {
+						in.close();
+						return null;
+					}
+					return a;
+				} catch(IOException e) {
+					throw luan.exception(e);
+				}
+			}
+		};
+	}
+
+
+
+	public static abstract class LuanIn {
+		abstract InputStream inputStream() throws IOException;
+		public abstract String to_string();
+
+		public String read_text() throws IOException {
+			Reader in = new InputStreamReader(inputStream());
+			String s = Utils.readAll(in);
+			in.close();
+			return s;
+		}
+
+		public byte[] read_binary() throws IOException {
+			InputStream in = inputStream();
+			byte[] a = Utils.readAll(in);
+			in.close();
+			return a;
+		}
+
+		public LuanFunction read_lines() throws IOException {
+			return lines(new BufferedReader(new InputStreamReader(inputStream())));
+		}
+
+		public LuanFunction read_blocks(Integer blockSize) throws IOException {
+			int n = blockSize!=null ? blockSize : Utils.bufSize;
+			return blocks(inputStream(),n);
+		}
+
+		LuanTable table() {
+			LuanTable tbl = new LuanTable();
+			try {
+				tbl.put( "to_string", new LuanJavaFunction(
+					LuanIn.class.getMethod( "to_string" ), this
+				) );
+				tbl.put( "read_text", new LuanJavaFunction(
+					LuanIn.class.getMethod( "read_text" ), this
+				) );
+				tbl.put( "read_binary", new LuanJavaFunction(
+					LuanIn.class.getMethod( "read_binary" ), this
+				) );
+				tbl.put( "read_lines", new LuanJavaFunction(
+					LuanIn.class.getMethod( "read_lines" ), this
+				) );
+				tbl.put( "read_blocks", new LuanJavaFunction(
+					LuanIn.class.getMethod( "read_blocks", Integer.class ), this
+				) );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			return tbl;
+		}
+	}
+
+	public static abstract class LuanIO extends LuanIn {
+		abstract OutputStream outputStream() throws IOException;
+
+		public void write(LuanState luan,Object obj) throws LuanException, IOException {
+			if( obj instanceof String ) {
+				String s = (String)obj;
+				Writer out = new OutputStreamWriter(outputStream());
+				out.write(s);
+				out.close();
+				return;
+			}
+			if( obj instanceof byte[] ) {
+				byte[] a = (byte[])obj;
+				OutputStream out = outputStream();
+				Utils.copyAll(new ByteArrayInputStream(a),out);
+				out.close();
+				return;
+			}
+			throw luan.exception( "bad argument #1 to 'write' (string or binary expected)" );
+		}
+
+		public LuanTable text_writer() throws IOException {
+			return textWriter(new BufferedWriter(new OutputStreamWriter(outputStream())));
+		}
+
+		public LuanTable binary_writer() throws IOException {
+			return binaryWriter(new BufferedOutputStream(outputStream()));
+		}
+
+		@Override LuanTable table() {
+			LuanTable tbl = super.table();
+			try {
+				tbl.put( "write", new LuanJavaFunction(
+					LuanIO.class.getMethod( "write", LuanState.class, Object.class ), this
+				) );
+				tbl.put( "text_writer", new LuanJavaFunction(
+					LuanIO.class.getMethod( "text_writer" ), this
+				) );
+				tbl.put( "binary_writer", new LuanJavaFunction(
+					LuanIO.class.getMethod( "binary_writer" ), this
+				) );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			return tbl;
+		}
+	}
+
+	public static final class LuanUrl extends LuanIn {
+		private final URL url;
+
+		public LuanUrl(String s) throws MalformedURLException {
+			this.url = new URL(s);
+		}
+
+		@Override InputStream inputStream() throws IOException {
+			return url.openStream();
+		}
+
+		@Override public String to_string() {
+			return url.toString();
+		}
+	}
+
+	public static final class LuanFile extends LuanIO {
+		private final File file;
+
+		public LuanFile(String name) {
+			this(new File(name));
+		}
+
+		public LuanFile(File file) {
+			this.file = file;
+		}
+
+		@Override InputStream inputStream() throws IOException {
+			return new FileInputStream(file);
+		}
+
+		@Override OutputStream outputStream() throws IOException {
+			return new FileOutputStream(file);
+		}
+
+		@Override public String to_string() {
+			return file.toString();
+		}
+
+		public LuanTable child(String name) {
+			return new LuanFile(new File(file,name)).table();
+		}
+
+		public LuanTable children() {
+			File[] files = file.listFiles();
+			if( files==null )
+				return null;
+			LuanTable list = new LuanTable();
+			for( File f : files ) {
+				list.add(new LuanFile(f).table());
+			}
+			return list;
+		}
+
+		public boolean exists() {
+			return Utils.exists(file);
+		}
+
+		@Override LuanTable table() {
+			LuanTable tbl = super.table();
+			try {
+				tbl.put( "name", new LuanJavaFunction(
+					File.class.getMethod( "getName" ), file
+				) );
+				tbl.put( "exists", new LuanJavaFunction(
+					LuanFile.class.getMethod( "exists" ), this
+				) );
+				tbl.put( "is_directory", new LuanJavaFunction(
+					File.class.getMethod( "isDirectory" ), file
+				) );
+				tbl.put( "is_file", new LuanJavaFunction(
+					File.class.getMethod( "isFile" ), file
+				) );
+				tbl.put( "delete", new LuanJavaFunction(
+					File.class.getMethod( "delete" ), file
+				) );
+				tbl.put( "mkdir", new LuanJavaFunction(
+					File.class.getMethod( "mkdir" ), file
+				) );
+				tbl.put( "mkdirs", new LuanJavaFunction(
+					File.class.getMethod( "mkdirs" ), file
+				) );
+				tbl.put( "last_modified", new LuanJavaFunction(
+					File.class.getMethod( "lastModified" ), file
+				) );
+				tbl.put( "child", new LuanJavaFunction(
+					LuanFile.class.getMethod( "child", String.class ), this
+				) );
+				tbl.put( "children", new LuanJavaFunction(
+					LuanFile.class.getMethod( "children" ), this
+				) );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			return tbl;
+		}
+	}
+
+	public static LuanIn luanIo(LuanState luan,String name) throws LuanException {
+		if( Utils.isFile(name) )
+			return new LuanFile(name);
+		String url = Utils.toUrl(name);
+		if( url != null ) {
+			try {
+				return new LuanUrl(url);
+			} catch(MalformedURLException e) {
+				throw new RuntimeException(e);
+			}
+		}
+		throw luan.exception( "file '"+name+"' not found" );
+	}
+
+	public static LuanTable File(LuanState luan,String name) throws LuanException {
+		return luanIo(luan,name).table();
+	}
+
+	public static final class LuanSocket extends LuanIO {
+		private final Socket socket;
+
+		public LuanSocket(String host,int port) throws IOException {
+			this(new Socket(host,port));
+		}
+
+		public LuanSocket(Socket socket) throws IOException {
+			this.socket = socket;
+		}
+
+		@Override InputStream inputStream() throws IOException {
+			return socket.getInputStream();
+		}
+
+		@Override OutputStream outputStream() throws IOException {
+			return socket.getOutputStream();
+		}
+
+		@Override public String to_string() {
+			return socket.toString();
+		}
+
+		public LuanTable Pickle_client(LuanState luan) throws IOException {
+			DataInputStream in = new DataInputStream(new BufferedInputStream(inputStream()));
+			DataOutputStream out = new DataOutputStream(new BufferedOutputStream(outputStream()));
+			return new PickleClient(luan,in,out).table();
+		}
+
+		public void run_pickle_server(LuanState luan) throws IOException {
+			DataInputStream in = new DataInputStream(new BufferedInputStream(inputStream()));
+			DataOutputStream out = new DataOutputStream(new BufferedOutputStream(outputStream()));
+			new PickleServer(luan,in,out).run();
+		}
+
+		@Override LuanTable table() {
+			LuanTable tbl = super.table();
+			try {
+				tbl.put( "Pickle_client", new LuanJavaFunction(
+					LuanSocket.class.getMethod( "Pickle_client", LuanState.class ), this
+				) );
+				tbl.put( "run_pickle_server", new LuanJavaFunction(
+					LuanSocket.class.getMethod( "run_pickle_server", LuanState.class ), this
+				) );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			return tbl;
+		}
+	}
+
+	public static LuanTable Socket(String host,int port) throws IOException {
+		return new LuanSocket(host,port).table();
+	}
+
+	public static LuanFunction socket_server(int port) throws IOException {
+		final ServerSocket ss = new ServerSocket(port);
+		return new LuanFunction() {
+			@Override public Object call(LuanState luan,Object[] args) throws LuanException {
+				try {
+					if( args.length > 0 ) {
+						if( args.length > 1 || !"close".equals(args[0]) )
+							throw luan.exception( "the only argument allowed is 'close'" );
+						ss.close();
+						return null;
+					}
+					return new LuanSocket(ss.accept()).table();
+				} catch(IOException e) {
+					throw luan.exception(e);
+				}
+			}
+		};
+	}
+
+	private void IoLib() {}  // never
+}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/modules/JavaLib.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/JavaLib.java	Sun Jun 22 04:28:32 2014 +0000
@@ -0,0 +1,450 @@
+package luan.modules;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Member;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Proxy;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Collections;
+import java.util.Arrays;
+import luan.Luan;
+import luan.LuanState;
+import luan.LuanTable;
+import luan.MetatableGetter;
+import luan.LuanException;
+import luan.LuanFunction;
+import luan.LuanJavaFunction;
+import luan.LuanElement;
+
+
+public final class JavaLib {
+
+	public static final LuanFunction LOADER = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			luan.addMetatableGetter(mg);
+			LuanTable module = new LuanTable();
+			try {
+				module.put( "class", new LuanJavaFunction(JavaLib.class.getMethod("getClass",LuanState.class,String.class),null) );
+				add( module, "proxy", LuanState.class, Static.class, LuanTable.class, Object.class );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			luan.searchers().add(javaSearcher);
+			return module;
+		}
+	};
+
+	public static final LuanFunction javaSearcher = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) throws LuanException {
+			String modName = (String)args[0];
+			final Static s = JavaLib.getClass(luan,modName);
+			if( s==null )
+				return null;
+			LuanFunction loader = new LuanFunction() {
+				@Override public Object call(LuanState luan,Object[] args) {
+					return s;
+				}
+			};
+			return loader;
+		}
+	};
+
+	private static void add(LuanTable t,String method,Class<?>... parameterTypes) {
+		try {
+			t.put( method, new LuanJavaFunction(JavaLib.class.getMethod(method,parameterTypes),null) );
+		} catch(NoSuchMethodException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	private static final LuanTable mt = new LuanTable();
+	static {
+		add( mt, "__index", LuanState.class, Object.class, Object.class );
+		add( mt, "__newindex", LuanState.class, Object.class, Object.class, Object.class );
+	}
+
+	private static final MetatableGetter mg = new MetatableGetter() {
+		public LuanTable getMetatable(Object obj) {
+			if( obj==null )
+				return null;
+			return mt;
+		}
+	};
+
+	public static Object __index(LuanState luan,Object obj,Object key) throws LuanException {
+		if( obj instanceof Static ) {
+			if( key instanceof String ) {
+				String name = (String)key;
+				Static st = (Static)obj;
+				Class cls = st.cls;
+				if( "class".equals(name) ) {
+					return cls;
+				} else if( "new".equals(name) ) {
+					Constructor<?>[] constructors = cls.getConstructors();
+					if( constructors.length > 0 ) {
+						if( constructors.length==1 ) {
+							return new LuanJavaFunction(constructors[0],null);
+						} else {
+							List<LuanJavaFunction> fns = new ArrayList<LuanJavaFunction>();
+							for( Constructor constructor : constructors ) {
+								fns.add(new LuanJavaFunction(constructor,null));
+							}
+							return new AmbiguousJavaFunction(fns);
+						}
+					}
+				} else if( "assert".equals(name) ) {
+					return new LuanJavaFunction(assertClass,new AssertClass(cls));
+				} else {
+					List<Member> members = getStaticMembers(cls,name);
+					if( !members.isEmpty() ) {
+						return member(null,members);
+					}
+				}
+			}
+			throw luan.exception("invalid member '"+key+"' for: "+obj);
+		}
+		Class cls = obj.getClass();
+		if( cls.isArray() ) {
+			if( "length".equals(key) ) {
+				return Array.getLength(obj);
+			}
+			Integer i = Luan.asInteger(key);
+			if( i != null ) {
+				return Array.get(obj,i);
+			}
+			throw luan.exception("invalid member '"+key+"' for java array: "+obj);
+		}
+		if( key instanceof String ) {
+			String name = (String)key;
+			if( "instanceof".equals(name) ) {
+				return new LuanJavaFunction(instanceOf,new InstanceOf(obj));
+			} else {
+				List<Member> members = getMembers(cls,name);
+				if( !members.isEmpty() ) {
+					return member(obj,members);
+				}
+			}
+		}
+//		throw luan.exception("invalid member '"+key+"' for java object: "+obj);
+		return null;
+	}
+
+	private static Object member(Object obj,List<Member> members) throws LuanException {
+		try {
+			if( members.size()==1 ) {
+				Member member = members.get(0);
+				if( member instanceof Static ) {
+					return member;
+				} else if( member instanceof Field ) {
+					Field field = (Field)member;
+					Object rtn = field.get(obj);
+					return rtn instanceof Object[] ? Arrays.asList((Object[])rtn) : rtn;
+				} else {
+					Method method = (Method)member;
+					return new LuanJavaFunction(method,obj);
+				}
+			} else {
+				List<LuanJavaFunction> fns = new ArrayList<LuanJavaFunction>();
+				for( Member member : members ) {
+					Method method = (Method)member;
+					fns.add(new LuanJavaFunction(method,obj));
+				}
+				return new AmbiguousJavaFunction(fns);
+			}
+		} catch(IllegalAccessException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	public static void __newindex(LuanState luan,Object obj,Object key,Object value) throws LuanException {
+		if( obj instanceof Static ) {
+			if( key instanceof String ) {
+				String name = (String)key;
+				Static st = (Static)obj;
+				Class cls = st.cls;
+				List<Member> members = getStaticMembers(cls,name);
+				if( !members.isEmpty() ) {
+					if( members.size() != 1 )
+						throw new RuntimeException("not field '"+name+"' of "+obj);
+					setMember(obj,members,value);
+					return;
+				}
+			}
+			throw luan.exception("invalid member '"+key+"' for: "+obj);
+		}
+		Class cls = obj.getClass();
+		if( cls.isArray() ) {
+			Integer i = Luan.asInteger(key);
+			if( i != null ) {
+				Array.set(obj,i,value);
+				return;
+			}
+			throw luan.exception("invalid member '"+key+"' for java array: "+obj);
+		}
+		if( key instanceof String ) {
+			String name = (String)key;
+			List<Member> members = getMembers(cls,name);
+			if( !members.isEmpty() ) {
+				if( members.size() != 1 )
+					throw new RuntimeException("not field '"+name+"' of "+obj);
+				setMember(obj,members,value);
+				return;
+			}
+		}
+		throw luan.exception("invalid member '"+key+"' for java object: "+obj);
+	}
+
+	private static void setMember(Object obj,List<Member> members,Object value) {
+		Field field = (Field)members.get(0);
+		try {
+			try {
+				field.set(obj,value);
+			} catch(IllegalArgumentException e) {
+				Class cls = field.getType();
+				if( value instanceof Number ) {
+					Number n = (Number)value;
+					if( cls.equals(Integer.TYPE) || cls.equals(Integer.class) ) {
+						int r = n.intValue();
+						if( r==n.doubleValue() ) {
+							field.setInt(obj,r);
+							return;
+						}
+					}
+				}
+				throw e;
+			}
+		} catch(IllegalAccessException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	public static boolean privateAccess = false;
+	private static Map<Class,Map<String,List<Member>>> memberMap = new HashMap<Class,Map<String,List<Member>>>();
+
+	private static synchronized List<Member> getMembers(Class cls,String name) {
+		Map<String,List<Member>> clsMap = memberMap.get(cls);
+		if( clsMap == null ) {
+			clsMap = new HashMap<String,List<Member>>();
+			for( Class c : cls.getClasses() ) {
+				String s = c.getSimpleName();
+				List<Member> list = new ArrayList<Member>();
+				clsMap.put(s,list);
+				list.add(new Static(c));
+			}
+			for( Field field : cls.getFields() ) {
+				String s = field.getName();
+				try {
+					if( !cls.getField(s).equals(field) )
+						continue;  // not accessible
+				} catch(NoSuchFieldException e) {
+					throw new RuntimeException(e);
+				}
+				List<Member> list = new ArrayList<Member>();
+				clsMap.put(s,list);
+				list.add(field);
+			}
+			for( Method method : cls.getMethods() ) {
+				String s = method.getName();
+				List<Member> list = clsMap.get(s);
+				if( list == null || !(list.get(0) instanceof Method) ) {
+					list = new ArrayList<Member>();
+					clsMap.put(s,list);
+				}
+				list.add(method);
+			}
+			if( privateAccess ) {
+				for( Method method : cls.getDeclaredMethods() ) {
+					String s = method.getName();
+					List<Member> list = clsMap.get(s);
+					if( list == null ) {
+						list = new ArrayList<Member>();
+						clsMap.put(s,list);
+					} else if( !(list.get(0) instanceof Method) )
+						continue;
+					if( !list.contains(method) ) {
+						list.add(method);
+					}
+				}
+				for( Field field : cls.getDeclaredFields() ) {
+					String s = field.getName();
+					List<Member> list = clsMap.get(s);
+					if( list == null ) {
+						list = new ArrayList<Member>();
+						clsMap.put(s,list);
+						list.add(field);
+					}
+				}
+			}
+			for( List<Member> members : clsMap.values() ) {
+				for( Member m : members ) {
+					if( m instanceof AccessibleObject )
+						((AccessibleObject)m).setAccessible(true);
+				}
+			}
+			memberMap.put(cls,clsMap);
+		}
+		List<Member> rtn = clsMap.get(name);
+		if( rtn==null )
+			rtn = Collections.emptyList();
+		return rtn;
+	}
+
+	private static synchronized List<Member> getStaticMembers(Class cls,String name) {
+		List<Member> staticMembers = new ArrayList<Member>();
+		for( Member m : getMembers(cls,name) ) {
+			if( Modifier.isStatic(m.getModifiers()) )
+				staticMembers.add(m);
+		}
+		return staticMembers;
+	}
+
+	static final class Static implements Member {
+		final Class cls;
+
+		Static(Class cls) {
+			this.cls = cls;
+		}
+
+		@Override public String toString() {
+			return cls.toString();
+		}
+
+		@Override public Class<?> getDeclaringClass() {
+			return cls.getDeclaringClass();
+		}
+
+		@Override public String getName() {
+			return cls.getName();
+		}
+
+		@Override public int getModifiers() {
+			return cls.getModifiers();
+		}
+
+		@Override public boolean isSynthetic() {
+			return cls.isSynthetic();
+		}
+	}
+
+	public static Static getClass(LuanState luan,String name) throws LuanException {
+		Class cls;
+		try {
+			cls = Class.forName(name);
+		} catch(ClassNotFoundException e) {
+			try {
+				cls = Thread.currentThread().getContextClassLoader().loadClass(name);
+			} catch(ClassNotFoundException e2) {
+				return null;
+			}
+		}
+		return new Static(cls);
+	}
+/*
+	public static void importClass(LuanState luan,String name) throws LuanException {
+		luan.currentEnvironment().put( name.substring(name.lastIndexOf('.')+1), getClass(luan,name) );
+	}
+*/
+	static class AmbiguousJavaFunction extends LuanFunction {
+		private final Map<Integer,List<LuanJavaFunction>> fnMap = new HashMap<Integer,List<LuanJavaFunction>>();
+
+		AmbiguousJavaFunction(List<LuanJavaFunction> fns) {
+			for( LuanJavaFunction fn : fns ) {
+				Integer n = fn.getParameterTypes().length;
+				List<LuanJavaFunction> list = fnMap.get(n);
+				if( list==null ) {
+					list = new ArrayList<LuanJavaFunction>();
+					fnMap.put(n,list);
+				}
+				list.add(fn);
+			}
+		}
+
+		@Override public Object call(LuanState luan,Object[] args) throws LuanException {
+			for( LuanJavaFunction fn : fnMap.get(args.length) ) {
+				try {
+					return fn.rawCall(luan,args);
+				} catch(IllegalArgumentException e) {}
+			}
+			throw luan.exception("no method matched args");
+		}
+	}
+
+	private static class InstanceOf {
+		private final Object obj;
+
+		InstanceOf(Object obj) {
+			this.obj = obj;
+		}
+
+		public boolean instanceOf(Static st) {
+			return st.cls.isInstance(obj);
+		}
+	}
+	private static final Method instanceOf;
+	static {
+		try {
+			instanceOf = InstanceOf.class.getMethod("instanceOf",Static.class);
+			instanceOf.setAccessible(true);
+		} catch(NoSuchMethodException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+
+	private static class AssertClass {
+		private final Class cls;
+
+		AssertClass(Class cls) {
+			this.cls = cls;
+		}
+
+		public Object assertClass(LuanState luan,Object v) throws LuanException {
+			if( !cls.isInstance(v) ) {
+				String got = v.getClass().getSimpleName();
+				String expected = cls.getSimpleName();
+				throw luan.exception("bad argument #1 ("+expected+" expected, got "+got+")");
+			}
+			return v;
+		}
+	}
+	private static final Method assertClass;
+	static {
+		try {
+			assertClass = AssertClass.class.getMethod("assertClass",LuanState.class,Object.class);
+			assertClass.setAccessible(true);
+		} catch(NoSuchMethodException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+
+	public static Object proxy(final LuanState luan,Static st,final LuanTable t,final Object base) throws LuanException {
+		return Proxy.newProxyInstance(
+			st.cls.getClassLoader(),
+			new Class[]{st.cls},
+			new InvocationHandler() {
+				public Object invoke(Object proxy,Method method, Object[] args)
+					throws Throwable
+				{
+					if( args==null )
+						args = new Object[0];
+					String name = method.getName();
+					Object fnObj = t.get(name);
+					if( fnObj==null && base!=null )
+						return method.invoke(base,args);
+					LuanFunction fn = luan.checkFunction(fnObj);
+					return Luan.first(luan.call(fn,name,args));
+				}
+			}
+		);
+	}
+}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/modules/MathLib.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/MathLib.java	Sun Jun 22 04:28:32 2014 +0000
@@ -0,0 +1,152 @@
+package luan.modules;
+
+import luan.LuanState;
+import luan.LuanTable;
+import luan.LuanFunction;
+import luan.LuanJavaFunction;
+import luan.LuanException;
+
+
+public final class MathLib {
+
+	public static final LuanFunction LOADER = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			LuanTable module = new LuanTable();
+			try {
+				add( module, "abs", Double.TYPE );
+				add( module, "acos", Double.TYPE );
+				add( module, "asin", Double.TYPE );
+				add( module, "atan", Double.TYPE );
+				add( module, "atan2", Double.TYPE, Double.TYPE );
+				add( module, "ceil", Double.TYPE );
+				add( module, "cos", Double.TYPE );
+				add( module, "cosh", Double.TYPE );
+				add( module, "deg", Double.TYPE );
+				add( module, "exp", Double.TYPE );
+				add( module, "floor", Double.TYPE );
+				add( module, "log", Double.TYPE );
+				add( module, "min", Double.TYPE, new double[0].getClass() );
+				add( module, "max", Double.TYPE, new double[0].getClass() );
+				add( module, "modf", Double.TYPE );
+				module.put("pi",Math.PI);
+				add( module, "pow", Double.TYPE, Double.TYPE );
+				add( module, "rad", Double.TYPE );
+				add( module, "random" );
+				add( module, "sin", Double.TYPE );
+				add( module, "sinh", Double.TYPE );
+				add( module, "sqrt", Double.TYPE );
+				add( module, "tan", Double.TYPE );
+				add( module, "tanh", Double.TYPE );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			return module;
+		}
+	};
+
+	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
+		t.put( method, new LuanJavaFunction(MathLib.class.getMethod(method,parameterTypes),null) );
+	}
+
+	public static double abs(double x) {
+		return Math.abs(x);
+	}
+
+	public static double acos(double x) {
+		return Math.acos(x);
+	}
+
+	public static double asin(double x) {
+		return Math.asin(x);
+	}
+
+	public static double atan(double x) {
+		return Math.atan(x);
+	}
+
+	public static double atan2(double y,double x) {
+		return Math.atan2(y,x);
+	}
+
+	public static double ceil(double x) {
+		return Math.ceil(x);
+	}
+
+	public static double cos(double x) {
+		return Math.cos(x);
+	}
+
+	public static double cosh(double x) {
+		return Math.cosh(x);
+	}
+
+	public static double deg(double x) {
+		return Math.toDegrees(x);
+	}
+
+	public static double exp(double x) {
+		return Math.exp(x);
+	}
+
+	public static double floor(double x) {
+		return Math.floor(x);
+	}
+
+	public static double log(double x) {
+		return Math.log(x);
+	}
+
+	public static double min(double x,double... a) {
+		for( double d : a ) {
+			if( x > d )
+				x = d;
+		}
+		return x;
+	}
+
+	public static double max(double x,double... a) {
+		for( double d : a ) {
+			if( x < d )
+				x = d;
+		}
+		return x;
+	}
+
+	public static double[] modf(double x) {
+		double i = (int)x;
+		return new double[]{i,x-i};
+	}
+
+	public static double pow(double x,double y) {
+		return Math.pow(x,y);
+	}
+
+	public static double rad(double x) {
+		return Math.toRadians(x);
+	}
+
+	public static double random() {
+		return Math.random();
+	}
+
+	public static double sin(double x) {
+		return Math.sin(x);
+	}
+
+	public static double sinh(double x) {
+		return Math.sinh(x);
+	}
+
+	public static double sqrt(double x) {
+		return Math.sqrt(x);
+	}
+
+	public static double tan(double x) {
+		return Math.tan(x);
+	}
+
+	public static double tanh(double x) {
+		return Math.tanh(x);
+	}
+
+}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/modules/PackageLib.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/PackageLib.java	Sun Jun 22 04:28:32 2014 +0000
@@ -0,0 +1,170 @@
+package luan.modules;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import luan.Luan;
+import luan.LuanState;
+import luan.LuanTable;
+import luan.LuanFunction;
+import luan.LuanJavaFunction;
+import luan.LuanElement;
+import luan.LuanException;
+
+
+public final class PackageLib {
+
+	private static final String jpath = "luan.modules.?Lib.LOADER";
+
+	public static final LuanFunction LOADER = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			LuanTable module = new LuanTable();
+			module.put("loaded",luan.loaded());
+			module.put("preload",luan.preload());
+			module.put("path","?.luan;java:luan/modules/?.luan");
+			module.put("jpath",jpath);
+			try {
+				add( module, "require", LuanState.class, String.class );
+				add( module, "load_lib", String.class );
+				add( module, "search_path", String.class, String.class );
+				add( module, "search", LuanState.class, String.class );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			LuanTable searchers = luan.searchers();
+			searchers.add(preloadSearcher);
+			searchers.add(fileSearcher);
+			searchers.add(javaSearcher);
+			module.put("searchers",searchers);
+			return module;
+		}
+	};
+
+	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
+		t.put( method, new LuanJavaFunction(PackageLib.class.getMethod(method,parameterTypes),null) );
+	}
+
+	public static Object require(LuanState luan,String modName) throws LuanException {
+		LuanTable loaded = luan.loaded();
+		Object mod = loaded.get(modName);
+		if( mod == null ) {
+			Object[] a = search(luan,modName);
+			if( a == null )
+				throw luan.exception( "module '"+modName+"' not found" );
+			LuanFunction loader = (LuanFunction)a[0];
+			a[0] = modName;
+			mod = Luan.first(luan.call(loader,"<require \""+modName+"\">",a));
+			if( mod != null ) {
+				loaded.put(modName,mod);
+			} else {
+				mod = loaded.get(modName);
+				if( mod==null )
+					loaded.put(modName,true);
+			}
+		}
+		return mod;
+	}
+
+	public static Object[] search(LuanState luan,String modName) throws LuanException {
+		List<Object> list = null;
+		LuanTable searchers = (LuanTable)luan.get("Package.searchers");
+		if( searchers == null ) {
+			list = Collections.<Object>singletonList(javaSearcher);
+		} else {
+			list = searchers.asList();
+		}
+		for( Object s : list ) {
+			LuanFunction searcher = (LuanFunction)s;
+			Object[] a = Luan.array(luan.call(searcher,"<searcher>",new Object[]{modName}));
+			if( a.length >= 1 && a[0] instanceof LuanFunction )
+				return a;
+		}
+		return null;
+	}
+
+	public static final LuanFunction preloadSearcher = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			String modName = (String)args[0];
+			return luan.preload().get(modName);
+		}
+	};
+
+	public static String search_path(String name,String path) {
+		name = name.replace('.','/');
+		for( String s : path.split(";") ) {
+			String file = s.replaceAll("\\?",name);
+			if( Utils.exists(file) )
+				return file;
+		}
+		return null;
+	}
+
+	public static final LuanFunction fileLoader = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) throws LuanException {
+			String fileName = (String)args[1];
+			LuanFunction fn = BasicLib.load_file(luan,fileName);
+			return fn.call(luan,args);
+		}
+	};
+
+	public static final LuanFunction fileSearcher = new LuanFunction() {
+		@Override public Object[] call(LuanState luan,Object[] args) {
+			String modName = (String)args[0];
+			String path = (String)luan.get("Package.path");
+			if( path==null )
+				return LuanFunction.NOTHING;
+			String file = search_path(modName,path);
+			return file==null ? LuanFunction.NOTHING : new Object[]{fileLoader,file};
+		}
+	};
+
+
+	public static final LuanFunction javaLoader = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) throws LuanException {
+			try {
+				String objName = (String)args[1];
+				LuanFunction fn = load_lib(objName);
+				return fn.call(luan,args);
+			} catch(ClassNotFoundException e) {
+				throw new RuntimeException(e);
+			} catch(NoSuchFieldException e) {
+				throw new RuntimeException(e);
+			} catch(IllegalAccessException e) {
+				throw new RuntimeException(e);
+			}
+		}
+	};
+
+	public static final LuanFunction javaSearcher = new LuanFunction() {
+		@Override public Object[] call(LuanState luan,Object[] args) {
+			String modName = (String)args[0];
+			String path = (String)luan.get("Package.jpath");
+			if( path==null )
+				path = jpath;
+			for( String s : path.split(";") ) {
+				String objName = s.replaceAll("\\?",modName);
+				try {
+					load_lib(objName);  // throws exception if not found
+					return new Object[]{javaLoader,objName};
+				} catch(ClassNotFoundException e) {
+				} catch(NoSuchFieldException e) {
+				} catch(IllegalAccessException e) {
+				}
+			}
+			return LuanFunction.NOTHING;
+		}
+	};
+
+
+	public static LuanFunction load_lib(String path)
+		throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException
+	{
+		int i = path.lastIndexOf('.');
+		String clsPath = path.substring(0,i);
+		String fld = path.substring(i+1);
+		Class cls = Class.forName(clsPath);
+		return (LuanFunction)cls.getField(fld).get(null);
+	}
+
+}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/modules/PickleClient.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/PickleClient.java	Sun Jun 22 04:28:32 2014 +0000
@@ -0,0 +1,106 @@
+package luan.modules;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import luan.Luan;
+import luan.LuanState;
+import luan.LuanException;
+import luan.LuanTable;
+import luan.LuanJavaFunction;
+import luan.LuanFunction;
+
+
+public final class PickleClient {
+
+	private final PickleCon con;
+	private final LuanFunction _reversed_pickle;
+
+	PickleClient(LuanState luan,DataInputStream in,DataOutputStream out) {
+		this(new PickleCon(luan,in,out));
+	}
+
+	PickleClient(PickleCon con) {
+		this.con = con;
+		try {
+			this._reversed_pickle = new LuanJavaFunction(
+				PickleClient.class.getMethod( "_reversed_pickle" ), this
+			);
+		} catch(NoSuchMethodException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	public Object _reversed_pickle() throws LuanException, IOException {
+		new PickleServer(con).run();
+		return con.read();
+	}
+
+	public Object call(Object... args) throws LuanException, IOException {
+		con.write(args);
+		Object[] result;
+		con.ioModule.put("_reversed_pickle",_reversed_pickle);
+		try {
+			result = Luan.array(con.read());
+		} finally {
+			con.ioModule.put("_reversed_pickle",null);
+		}
+		boolean ok = (boolean)result[0];
+		if( ok ) {
+			Object[] rtn = new Object[result.length-1];
+			System.arraycopy(result,1,rtn,0,rtn.length);
+			return rtn;
+		} else {
+			String msg = (String)result[1];
+			String src = (String)result[2];
+			throw con.luan.exception(
+				msg + "\n"
+				+ "in:\n"
+				+ "------------------\n"
+				+ formatCode(src) + "\n"
+				+ "------------------\n"
+			);
+		}
+	}
+
+	LuanTable table() {
+		LuanTable tbl = new LuanTable();
+		try {
+			tbl.put( "pickle", new LuanJavaFunction(
+				PickleCon.class.getMethod( "pickle", Object.class ), con
+			) );
+			tbl.put( "call", new LuanJavaFunction(
+				PickleClient.class.getMethod( "call", new Object[0].getClass() ), this
+			) );
+			tbl.put( "close", new LuanJavaFunction(
+				PickleCon.class.getMethod( "close" ), con
+			) );
+		} catch(NoSuchMethodException e) {
+			throw new RuntimeException(e);
+		}
+		return tbl;
+	}
+
+
+	public static String formatCode(String s) {
+		StringBuilder buf = new StringBuilder();
+		int line = 1;
+		int i = 0;
+		int i2 = 0;
+		while( i2 != -1 ) {
+			buf.append( line++ );
+			buf.append( '\t' );
+			i2 = s.indexOf('\n',i);
+			String lineStr = i2 == -1 ? s.substring(i) : s.substring(i,i2+1);
+			int j;
+			for( j=0; j<lineStr.length() && lineStr.charAt(j)=='\t'; j++ ) {
+				buf.append( "    " );
+			}
+			buf.append( lineStr.substring(j) );
+			i = i2 + 1;
+		}
+		return buf.toString();
+	}
+
+
+}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/modules/PickleCon.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/PickleCon.java	Sun Jun 22 04:28:32 2014 +0000
@@ -0,0 +1,134 @@
+package luan.modules;
+
+import java.io.OutputStream;
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Set;
+import java.util.IdentityHashMap;
+import java.util.Collections;
+import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
+import luan.Luan;
+import luan.LuanTable;
+import luan.LuanState;
+import luan.LuanFunction;
+import luan.LuanJavaFunction;
+import luan.LuanException;
+
+
+public final class PickleCon {
+	final LuanState luan;
+	private final DataInputStream in;
+	private final LuanFunction _read_binary;
+	final LuanTable ioModule;
+	private final DataOutputStream out;
+	private final List<byte[]> binaries = new ArrayList<byte[]>();
+	String src;
+	private final LuanTable env = new LuanTable();
+
+	PickleCon(LuanState luan,DataInputStream in,DataOutputStream out) {
+		this.in = in;
+		this.luan = luan;
+		try {
+			this._read_binary = new LuanJavaFunction(
+				PickleCon.class.getMethod( "_read_binary", Integer.TYPE ), this
+			);
+		} catch(NoSuchMethodException e) {
+			throw new RuntimeException(e);
+		}
+		this.ioModule = (LuanTable)luan.loaded().get("Io");
+
+		this.out = out;
+	}
+
+	public byte[] _read_binary(int size) throws IOException, LuanException {
+		byte[] a = new byte[size];
+		int i = 0;
+		while( i < size ) {
+			int n = in.read(a,i,size-i);
+			if( n == -1 )
+				throw luan.exception( "end of stream" );
+			i += n;
+		}
+		return a;
+	}
+
+	public Object read() throws IOException, LuanException {
+		ioModule.put("_read_binary",_read_binary);
+		try {
+			src = in.readUTF();
+			LuanFunction fn = BasicLib.load(luan,src,"pickle-reader",env,false);
+			return luan.call(fn);
+		} finally {
+			ioModule.put("_binaries",null);
+			ioModule.put("_read_binary",null);
+		}
+	}
+
+	public String pickle(Object obj) throws LuanException {
+		if( obj == null )
+			return "nil";
+		if( obj instanceof Boolean )
+			return Luan.toString((Boolean)obj);
+		if( obj instanceof Number )
+			return Luan.toString((Number)obj);
+		if( obj instanceof String )
+			return "\"" + Luan.stringEncode((String)obj) + "\"";
+		if( obj instanceof LuanTable )
+			return pickle( (LuanTable)obj, Collections.newSetFromMap(new IdentityHashMap<LuanTable,Boolean>()) );
+		if( obj instanceof byte[] ) {
+			byte[] a = (byte[])obj;
+			binaries.add(a);
+			return "Io._binaries[" + binaries.size() + "]";
+		}
+		throw luan.exception( "invalid type: " + obj.getClass() );
+	}
+
+	private String pickle(Object obj,Set<LuanTable> set) throws LuanException {
+		return obj instanceof LuanTable ? pickle((LuanTable)obj,set) : pickle(obj);
+	}
+
+	private String pickle(LuanTable tbl,Set<LuanTable> set) throws LuanException {
+		if( !set.add(tbl) ) {
+			throw luan.exception( "circular reference in table" );
+		}
+		StringBuilder sb = new StringBuilder();
+		sb.append( "{" );
+		for( Map.Entry<Object,Object> entry : tbl ) {
+			sb.append( "[" );
+			sb.append( pickle(entry.getKey(),set) );
+			sb.append( "]=" );
+			sb.append( pickle(entry.getValue(),set) );
+			sb.append( ", " );
+		}
+		sb.append( "}" );
+		return sb.toString();
+	}
+
+	public void write(Object... args) throws LuanException, IOException {
+		StringBuilder sb = new StringBuilder();
+		if( !binaries.isEmpty() ) {
+			sb.append( "Io._binaries = {}\n" );
+			for( byte[] a : binaries ) {
+				sb.append( "Io._binaries[#Io._binaries+1] = Io._read_binary(" + a.length + ")\n" );
+			}
+		}
+		for( Object obj : args ) {
+			sb.append( luan.toString(obj) );
+		}
+		out.writeUTF( sb.toString() );
+		for( byte[] a : binaries ) {
+			out.write(a);
+		}
+		out.flush();
+		binaries.clear();
+	}
+
+	public void close() throws IOException {
+		in.close();
+		out.close();
+	}
+}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/modules/PickleServer.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/PickleServer.java	Sun Jun 22 04:28:32 2014 +0000
@@ -0,0 +1,115 @@
+package luan.modules;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.EOFException;
+import java.util.List;
+import java.util.ArrayList;
+import luan.Luan;
+import luan.LuanState;
+import luan.LuanTable;
+import luan.LuanFunction;
+import luan.LuanJavaFunction;
+import luan.LuanException;
+
+
+public final class PickleServer {
+
+	private final PickleCon con;
+	private boolean isRunning;
+
+	PickleServer(LuanState luan,DataInputStream in,DataOutputStream out) {
+		this(new PickleCon(luan,in,out));
+	}
+
+	PickleServer(PickleCon con) {
+		this.con = con;
+	}
+
+	void next() throws IOException {
+		try {
+			List<String> list = new ArrayList<String>();
+			try {
+				Object[] result = Luan.array(con.read());
+				list.add( "return true" );
+				for( Object obj : result ) {
+					list.add( ", " );
+					list.add( con.pickle(obj) );
+				}
+			} catch(LuanException e) {
+//				System.out.println(e);
+//e.printStackTrace();
+				list.add( "return false, " );
+				list.add( con.pickle(e.getMessage()) );
+				list.add( ", " );
+				list.add( con.pickle(con.src) );
+			}
+			list.add( "\n" );
+			con.write( list.toArray() );
+		} catch(LuanException e2) {
+			throw new RuntimeException(e2);
+		}
+	}
+
+	public void run() {
+		LuanTable ioModule = con.ioModule;
+		Object old_reverse_pickle = ioModule.get("reverse_pickle");
+		Object old_unreverse_pickle = ioModule.get("_unreverse_pickle");
+		try {
+			try {
+				ioModule.put("reverse_pickle", new LuanJavaFunction(
+					PickleServer.class.getMethod( "reverse_pickle", LuanFunction.class ), this
+				) );
+				ioModule.put("_unreverse_pickle", new LuanJavaFunction(
+					PickleServer.class.getMethod( "_unreverse_pickle" ), this
+				) );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			isRunning = true;
+			try {
+				while( isRunning ) {
+					next();
+				}
+			} catch(EOFException e) {
+				// done
+			} catch(IOException e) {
+				e.printStackTrace();
+			}
+			if( isRunning ) {
+				try {
+					con.close();
+				} catch(IOException e) {
+					throw new RuntimeException(e);
+				}
+			}
+		} finally {
+			ioModule.put("reverse_pickle",old_reverse_pickle);
+			ioModule.put("_unreverse_pickle",old_unreverse_pickle);
+		}
+	}
+
+	public void reverse_pickle(LuanFunction fn) throws IOException, LuanException {
+		try {
+			con.write( "return Io._reversed_pickle()\n" );
+		} catch(LuanException e) {
+			throw new RuntimeException(e);
+		}
+		PickleClient pc = new PickleClient(con);
+		try {
+			con.luan.call(fn,new Object[]{pc.table()});
+		} finally {
+			try {
+				pc.call( "Io._unreverse_pickle()\n" );
+			} catch(LuanException e) {
+				throw new RuntimeException(e);
+			}
+		}
+	}
+
+	public void _unreverse_pickle() {
+		isRunning = false;
+	}
+
+}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/modules/StringLib.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/StringLib.java	Sun Jun 22 04:28:32 2014 +0000
@@ -0,0 +1,264 @@
+package luan.modules;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import luan.Luan;
+import luan.LuanState;
+import luan.LuanTable;
+import luan.LuanFunction;
+import luan.LuanJavaFunction;
+import luan.LuanElement;
+import luan.LuanException;
+import luan.MetatableGetter;
+
+
+public final class StringLib {
+
+	public static final LuanFunction LOADER = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			luan.addMetatableGetter(mg);
+			LuanTable module = new LuanTable();
+			try {
+				add( module, "to_binary", String.class );
+				add( module, "to_integers", String.class );
+				add( module, "from_integers", new int[0].getClass() );
+				add( module, "find", String.class, String.class, Integer.class, Boolean.class );
+				add( module, "format", String.class, new Object[0].getClass() );
+				add( module, "gmatch", String.class, String.class );
+				add( module, "gsub", LuanState.class, String.class, String.class, Object.class, Integer.class );
+				add( module, "len", String.class );
+				add( module, "lower", String.class );
+				add( module, "match", String.class, String.class, Integer.class );
+				add( module, "rep", String.class, Integer.TYPE, String.class );
+				add( module, "reverse", String.class );
+				add( module, "sub", String.class, Integer.TYPE, Integer.class );
+				add( module, "upper", String.class );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			return module;
+		}
+	};
+
+	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
+		t.put( method, new LuanJavaFunction(StringLib.class.getMethod(method,parameterTypes),null) );
+	}
+
+	private static final LuanTable mt = new LuanTable();
+	static {
+		try {
+			add( mt, "__index", LuanState.class, String.class, Object.class );
+		} catch(NoSuchMethodException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	private static final MetatableGetter mg = new MetatableGetter() {
+		public LuanTable getMetatable(Object obj) {
+			return obj instanceof String ? mt : null;
+		}
+	};
+
+	public static Object __index(LuanState luan,final String s,Object key) throws LuanException {
+		LuanTable mod = (LuanTable)luan.loaded().get("String");
+		if( mod!=null ) {
+			Object obj = mod.get(key);
+			if( obj instanceof LuanFunction ) {
+				final LuanFunction fn = (LuanFunction)obj;
+				return new LuanFunction() {
+					@Override public Object call(LuanState luan,Object[] args) throws LuanException {
+						Object[] a = new Object[args.length+1];
+						a[0] = s;
+						System.arraycopy(args,0,a,1,args.length);
+						return fn.call(luan,a);
+					}
+				};
+			}
+		}
+		if( luan.loaded().get("Java") != null )
+			return JavaLib.__index(luan,s,key);
+		return null;
+	}
+
+	static int start(String s,int i) {
+		return i==0 ? 0 : i > 0 ? i - 1 : s.length() + i;
+	}
+
+	static int start(String s,Integer i,int dflt) {
+		return i==null ? dflt : start(s,i);
+	}
+
+	static int end(String s,int i) {
+		return i==0 ? 0 : i > 0 ? i : s.length() + i + 1;
+	}
+
+	static int end(String s,Integer i,int dflt) {
+		return i==null ? dflt : end(s,i);
+	}
+
+	public static byte[] to_binary(String s) {
+		return s.getBytes();
+	}
+
+	public static int[] to_integers(String s) {
+		char[] a = s.toCharArray();
+		int[] chars = new int[a.length];
+		for( int i=0; i<a.length; i++ ) {
+			chars[i] = a[i];
+		}
+		return chars;
+	}
+
+	public static String from_integers(int... chars) {
+		char[] a = new char[chars.length];
+		for( int i=0; i<chars.length; i++ ) {
+			a[i] = (char)chars[i];
+		}
+		return new String(a);
+	}
+
+	public static int len(String s) {
+		return s.length();
+	}
+
+	public static String lower(String s) {
+		return s.toLowerCase();
+	}
+
+	public static String upper(String s) {
+		return s.toUpperCase();
+	}
+
+	public static String reverse(String s) {
+		return new StringBuilder(s).reverse().toString();
+	}
+
+	public static String rep(String s,int n,String sep) {
+		if( n < 1 )
+			return "";
+		StringBuilder buf = new StringBuilder(s);
+		while( --n > 0 ) {
+			if( sep != null )
+				buf.append(sep);
+			buf.append(s);
+		}
+		return buf.toString();
+	}
+
+	public static String sub(String s,int i,Integer j) {
+		int start = start(s,i);
+		int end = end(s,j,s.length());
+		return s.substring(start,end);
+	}
+
+	public static int[] find(String s,String pattern,Integer init,Boolean plain) {
+		int start = start(s,init,0);
+		if( Boolean.TRUE.equals(plain) ) {
+			int i = s.indexOf(pattern,start);
+			return i == -1 ? null : new int[]{i+1,i+pattern.length()};
+		}
+		Matcher m = Pattern.compile(pattern).matcher(s);
+		return m.find(start) ? new int[]{m.start()+1,m.end()} : null;
+	}
+
+	public static String[] match(String s,String pattern,Integer init) {
+		int start = start(s,init,0);
+		Matcher m = Pattern.compile(pattern).matcher(s);
+		if( !m.find(start) )
+			return null;
+		final int n = m.groupCount();
+		if( n == 0 )
+			return new String[]{m.group()};
+		String[] rtn = new String[n];
+		for( int i=0; i<n; i++ ) {
+			rtn[i] = m.group(i+1);
+		}
+		return rtn;
+	}
+
+	public static LuanFunction gmatch(String s,String pattern) {
+		final Matcher m = Pattern.compile(pattern).matcher(s);
+		return new LuanFunction() {
+			@Override public Object call(LuanState luan,Object[] args) {
+				if( !m.find() )
+					return null;
+				final int n = m.groupCount();
+				if( n == 0 )
+					return m.group();
+				String[] rtn = new String[n];
+				for( int i=0; i<n; i++ ) {
+					rtn[i] = m.group(i+1);
+				}
+				return rtn;
+			}
+		};
+	}
+
+	public static Object[] gsub(LuanState luan,String s,String pattern,Object repl,Integer n) throws LuanException {
+		int max = n==null ? Integer.MAX_VALUE : n;
+		final Matcher m = Pattern.compile(pattern).matcher(s);
+		if( repl instanceof String ) {
+			String replacement = (String)repl;
+			int i = 0;
+			StringBuffer sb = new StringBuffer();
+			while( i<max && m.find() ) {
+				m.appendReplacement(sb,replacement);
+				i++;
+			}
+			m.appendTail(sb);
+			return new Object[]{ sb.toString(), i };
+		}
+		if( repl instanceof LuanTable ) {
+			LuanTable t = (LuanTable)repl;
+			int i = 0;
+			StringBuffer sb = new StringBuffer();
+			while( i<max && m.find() ) {
+				String match = m.groupCount()==0 ? m.group() : m.group(1);
+				Object val = t.get(match);
+				if( Luan.toBoolean(val) ) {
+					String replacement = Luan.asString(val);
+					if( replacement==null )
+						throw luan.exception( "invalid replacement value (a "+Luan.type(val)+")" );
+					m.appendReplacement(sb,replacement);
+				}
+				i++;
+			}
+			m.appendTail(sb);
+			return new Object[]{ sb.toString(), i };
+		}
+		if( repl instanceof LuanFunction ) {
+			LuanFunction fn = (LuanFunction)repl;
+			int i = 0;
+			StringBuffer sb = new StringBuffer();
+			while( i<max && m.find() ) {
+				Object[] args;
+				final int count = m.groupCount();
+				if( count == 0 ) {
+					args = new Object[]{m.group()};
+				} else {
+					args = new Object[count];
+					for( int j=0; j<count; j++ ) {
+						args[j] = m.group(j);
+					}
+				}
+				Object val = Luan.first( luan.call(fn,"repl-arg",args) );
+				if( Luan.toBoolean(val) ) {
+					String replacement = Luan.asString(val);
+					if( replacement==null )
+						throw luan.exception( "invalid replacement value (a "+Luan.type(val)+")" );
+					m.appendReplacement(sb,replacement);
+				}
+				i++;
+			}
+			m.appendTail(sb);
+			return new Object[]{ sb.toString(), i };
+		}
+		throw luan.exception( "bad argument #3 to 'gsub' (string/function/table expected)" );
+	}
+
+	// note - String.format() is too stupid to convert between ints and floats.
+	public static String format(String format,Object... args) {
+		return String.format(format,args);
+	}
+
+}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/modules/TableLib.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/TableLib.java	Sun Jun 22 04:28:32 2014 +0000
@@ -0,0 +1,131 @@
+package luan.modules;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+import luan.Luan;
+import luan.LuanState;
+import luan.LuanTable;
+import luan.LuanFunction;
+import luan.LuanJavaFunction;
+import luan.LuanElement;
+import luan.LuanException;
+import luan.LuanRuntimeException;
+
+
+public final class TableLib {
+
+	public static final LuanFunction LOADER = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			LuanTable module = new LuanTable();
+			try {
+				add( module, "concat", LuanState.class, LuanTable.class, String.class, Integer.class, Integer.class );
+				add( module, "insert", LuanState.class, LuanTable.class, Integer.TYPE, Object.class );
+				add( module, "pack", new Object[0].getClass() );
+				add( module, "remove", LuanState.class, LuanTable.class, Integer.TYPE );
+				add( module, "sort", LuanState.class, LuanTable.class, LuanFunction.class );
+				add( module, "sub_list", LuanTable.class, Integer.TYPE, Integer.TYPE );
+				add( module, "unpack", LuanTable.class, Integer.class, Integer.class );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			return module;
+		}
+	};
+
+	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
+		t.put( method, new LuanJavaFunction(TableLib.class.getMethod(method,parameterTypes),null) );
+	}
+
+	public static String concat(LuanState luan,LuanTable list,String sep,Integer i,Integer j) throws LuanException {
+		int first = i==null ? 1 : i;
+		int last = j==null ? list.length() : j;
+		StringBuilder buf = new StringBuilder();
+		for( int k=first; k<=last; k++ ) {
+			Object val = list.get(k);
+			if( val==null )
+				break;
+			if( sep!=null && k > first )
+				buf.append(sep);
+			String s = Luan.asString(val);
+			if( s==null )
+				throw luan.exception( "invalid value ("+Luan.type(val)+") at index "+k+" in table for 'concat'" );
+			buf.append(val);
+		}
+		return buf.toString();
+	}
+
+	public static void insert(LuanState luan,LuanTable list,int pos,Object value) throws LuanException {
+		try {
+			list.insert(pos,value);
+		} catch(IndexOutOfBoundsException e) {
+			throw luan.exception(e);
+		}
+	}
+
+	public static Object remove(LuanState luan,LuanTable list,int pos) throws LuanException {
+		try {
+			return list.remove(pos);
+		} catch(IndexOutOfBoundsException e) {
+			throw luan.exception(e);
+		}
+	}
+
+	private static interface LessThan {
+		public boolean isLessThan(Object o1,Object o2);
+	}
+
+	public static void sort(final LuanState luan,LuanTable list,final LuanFunction comp) throws LuanException {
+		final LessThan lt;
+		if( comp==null ) {
+			lt = new LessThan() {
+				public boolean isLessThan(Object o1,Object o2) {
+					try {
+						return luan.isLessThan(o1,o2);
+					} catch(LuanException e) {
+						throw new LuanRuntimeException(e);
+					}
+				}
+			};
+		} else {
+			lt = new LessThan() {
+				public boolean isLessThan(Object o1,Object o2) {
+					try {
+						return Luan.toBoolean(Luan.first(luan.call(comp,"comp-arg",new Object[]{o1,o2})));
+					} catch(LuanException e) {
+						throw new LuanRuntimeException(e);
+					}
+				}
+			};
+		}
+		try {
+			list.sort( new Comparator<Object>() {
+				public int compare(Object o1,Object o2) {
+					return lt.isLessThan(o1,o2) ? -1 : lt.isLessThan(o2,o1) ? 1 : 0;
+				}
+			} );
+		} catch(LuanRuntimeException e) {
+			throw (LuanException)e.getCause();
+		}
+	}
+
+	public static LuanTable pack(Object... args) {
+		return new LuanTable(new ArrayList<Object>(Arrays.asList(args)));
+	}
+
+	public static Object[] unpack(LuanTable tbl,Integer iFrom,Integer iTo) {
+		int from = iFrom!=null ? iFrom : 1;
+		int to = iTo!=null ? iTo : tbl.length();
+		List<Object> list = new ArrayList<Object>();
+		for( int i=from; i<=to; i++ ) {
+			list.add( tbl.get(i) );
+		}
+		return list.toArray();
+	}
+
+	public static LuanTable sub_list(LuanTable list,int from,int to) {
+		return list.subList(from,to);
+	}
+
+}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/modules/ThreadLib.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/ThreadLib.java	Sun Jun 22 04:28:32 2014 +0000
@@ -0,0 +1,47 @@
+package luan.modules;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import luan.LuanState;
+import luan.LuanFunction;
+import luan.LuanTable;
+import luan.LuanJavaFunction;
+import luan.LuanException;
+import luan.DeepCloner;
+
+
+public final class ThreadLib {
+
+	public static final LuanFunction LOADER = new LuanFunction() {
+		@Override public Object call(LuanState luan,Object[] args) {
+			LuanTable module = new LuanTable();
+			try {
+				add( module, "fork", LuanState.class, LuanFunction.class, new Object[0].getClass() );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			return module;
+		}
+	};
+
+	private static void add(LuanTable t,String method,Class<?>... parameterTypes) throws NoSuchMethodException {
+		t.put( method, new LuanJavaFunction(ThreadLib.class.getMethod(method,parameterTypes),null) );
+	}
+
+	private static final Executor exec = Executors.newCachedThreadPool();
+
+	public static void fork(LuanState luan,LuanFunction fn,Object... args) {
+		DeepCloner cloner = new DeepCloner();
+		final LuanState newLuan = cloner.deepClone(luan);
+		final LuanFunction newFn = cloner.get(fn);
+		final Object[] newArgs = cloner.deepClone(args);
+		exec.execute(new Runnable(){public void run() {
+			try {
+				newLuan.call(newFn,"<forked>",newArgs);
+			} catch(LuanException e) {
+				e.printStackTrace();
+			}
+		}});
+	}
+
+}
diff -r 4eaee12f6c65 -r 4c0131c2b650 src/luan/modules/Utils.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/Utils.java	Sun Jun 22 04:28:32 2014 +0000
@@ -0,0 +1,85 @@
+package luan.modules;
+
+import java.io.Reader;
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.File;
+import java.net.URL;
+import java.net.MalformedURLException;
+import luan.LuanState;
+import luan.LuanException;
+
+
+public final class Utils {
+	private Utils() {}  // never
+
+	static final int bufSize = 8192;
+
+	public static void checkNotNull(LuanState luan,Object v,String expected) throws LuanException {
+		if( v == null )
+			throw luan.exception("bad argument #1 ("+expected+" expected, got nil)");
+	}
+
+	public static String readAll(Reader in)
+		throws IOException
+	{
+		char[] a = new char[bufSize];
+		StringBuilder buf = new StringBuilder();
+		int n;
+		while( (n=in.read(a)) != -1 ) {
+			buf.append(a,0,n);
+		}
+		return buf.toString();
+	}
+
+	public static void copyAll(InputStream in,OutputStream out)
+		throws IOException
+	{
+		byte[] a = new byte[bufSize];
+		int n;
+		while( (n=in.read(a)) != -1 ) {
+			out.write(a,0,n);
+		}
+	}
+
+	public static byte[] readAll(InputStream in)
+		throws IOException
+	{
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		copyAll(in,out);
+		return out.toByteArray();
+	}
+
+	public static boolean exists(File file) {
+		try {
+			return file.exists() && file.getName().equals(file.getCanonicalFile().getName());
+		} catch(IOException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	public static boolean isFile(String path) {
+		return exists(new File(path));
+	}
+
+	public static String toUrl(String path) {
+		if( path.indexOf(':') == -1 )
+			return null;
+		if( path.startsWith("java:") ) {
+			path = path.substring(5);
+			URL url = ClassLoader.getSystemResource(path);
+			return url==null ? null : url.toString();
+		}
+		try {
+			new URL(path);
+			return path;
+		} catch(MalformedURLException e) {}
+		return null;
+	}
+
+	public static boolean exists(String path) {
+		return isFile(path) || toUrl(path)!=null;
+	}
+}