changeset 1267:9fa8b8389578

add LuanTable.luan; support metatable __gc(); add luan.sql;
author Franklin Schmidt <fschmidt@gmail.com>
date Mon, 12 Nov 2018 02:10:41 -0700
parents 05934fbf635a
children 725e52076f03
files src/luan/Luan.java src/luan/LuanException.java src/luan/LuanJavaFunction.java src/luan/LuanState.java src/luan/LuanTable.java src/luan/impl/LuanImpl.java src/luan/impl/LuanParser.java src/luan/modules/BasicLuan.java src/luan/modules/IoLuan.java src/luan/modules/JavaLuan.java src/luan/modules/PackageLuan.java src/luan/modules/StringLuan.java src/luan/modules/Table.luan src/luan/modules/TableLuan.java src/luan/modules/ThreadLuan.java src/luan/modules/http/HttpServicer.java src/luan/modules/http/LuanHandler.java src/luan/modules/lucene/LuceneIndex.java src/luan/modules/mail/SmtpCon.java src/luan/modules/parsers/Csv.java src/luan/modules/parsers/Html.java src/luan/modules/sql/Database.java src/luan/modules/sql/Sql.luan src/luan/modules/url/LuanUrl.java
diffstat 24 files changed, 495 insertions(+), 273 deletions(-) [+]
line wrap: on
line diff
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/Luan.java
--- a/src/luan/Luan.java	Sun Sep 30 19:10:48 2018 -0600
+++ b/src/luan/Luan.java	Mon Nov 12 02:10:41 2018 -0700
@@ -1,6 +1,5 @@
 package luan;
 
-import java.lang.reflect.Array;
 import java.util.List;
 import java.util.ArrayList;
 import java.util.Map;
@@ -202,36 +201,6 @@
 		return v;
 	}
 */
-	public static LuanTable toTable(Object obj) {
-		if( obj == null )
-			return null;
-		if( obj instanceof LuanTable )
-			return (LuanTable)obj;
-		if( obj instanceof List ) {
-			return new LuanTable((List)obj);
-		}
-		if( obj instanceof Map ) {
-			return new LuanTable((Map)obj);
-		}
-		if( obj instanceof Set ) {
-			return new LuanTable((Set)obj);
-		}
-		Class cls = obj.getClass();
-		if( cls.isArray() ) {
-			if( cls.getComponentType().isPrimitive() ) {
-				int len = Array.getLength(obj);
-				List list = new ArrayList();
-				for( int i=0; i<len; i++ ) {
-					list.add(Array.get(obj,i));
-				}
-				return new LuanTable(list);
-			} else {
-				Object[] a = (Object[])obj;
-				return new LuanTable(Arrays.asList(a));
-			}
-		}
-		return null;
-	}
 
 	private Luan() {}  // never
 }
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/LuanException.java
--- a/src/luan/LuanException.java	Sun Sep 30 19:10:48 2018 -0600
+++ b/src/luan/LuanException.java	Mon Nov 12 02:10:41 2018 -0700
@@ -35,11 +35,11 @@
 		clone.table = (LuanTable)cloner.clone(table);
 	}
 
-	public LuanTable table() {
+	public LuanTable table(LuanState luan) {
 		if( table==null ) {
-			table = new LuanTable();
+			table = new LuanTable(luan);
 			table.rawPut( "java", this );
-			LuanTable metatable = new LuanTable();
+			LuanTable metatable = new LuanTable(luan);
 			table.setMetatable(metatable);
 			try {
 				table.rawPut( "get_message", new LuanJavaFunction(
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/LuanJavaFunction.java
--- a/src/luan/LuanJavaFunction.java	Sun Sep 30 19:10:48 2018 -0600
+++ b/src/luan/LuanJavaFunction.java	Mon Nov 12 02:10:41 2018 -0700
@@ -58,7 +58,7 @@
 	@Override public Object call(LuanState luan,Object[] args) throws LuanException {
 		try {
 			args = fixArgs(luan,args);
-			return doCall(args);
+			return doCall(luan,args);
 		} catch(IllegalArgumentException e) {
 			checkArgs(args);
 			throw e;
@@ -67,10 +67,10 @@
 
 	public Object rawCall(LuanState luan,Object[] args) throws LuanException {
 		args = fixArgs(luan,args);
-		return doCall(args);
+		return doCall(luan,args);
 	}
 
-	private Object doCall(Object[] args) throws LuanException {
+	private Object doCall(LuanState luan,Object[] args) throws LuanException {
 		Object rtn;
 		try {
 			rtn = method.invoke(obj,args);
@@ -86,7 +86,7 @@
 		} catch(InstantiationException e) {
 			throw new RuntimeException(e);
 		}
-		return rtnConverter.convert(rtn);
+		return rtnConverter.convert(luan,rtn);
 	}
 
 	private static final Map primitiveMap = new HashMap();
@@ -189,30 +189,30 @@
 
 
 	private interface RtnConverter {
-		public Object convert(Object obj);
+		public Object convert(LuanState luan,Object obj);
 	}
 
 	private static final RtnConverter RTN_NOTHING = new RtnConverter() {
-		@Override public Object[] convert(Object obj) {
+		@Override public Object[] convert(LuanState luan,Object obj) {
 			return NOTHING;
 		}
 	};
 
 	private static final RtnConverter RTN_SAME = new RtnConverter() {
-		@Override public Object convert(Object obj) {
+		@Override public Object convert(LuanState luan,Object obj) {
 			return obj;
 		}
 	};
 
 	private static final RtnConverter RTN_ARRAY = new RtnConverter() {
-		@Override public Object convert(Object obj) {
+		@Override public Object convert(LuanState luan,Object obj) {
 			if( obj == null )
 				return null;
 			Object[] a = new Object[Array.getLength(obj)];
 			for( int i=0; i<a.length; i++ ) {
 				a[i] = Array.get(obj,i);
 			}
-			return new LuanTable(new ArrayList<Object>(Arrays.asList(a)));
+			return new LuanTable(luan,new ArrayList<Object>(Arrays.asList(a)));
 		}
 	};
 
@@ -339,7 +339,7 @@
 
 	private static final ArgConverter ARG_TABLE = new ArgConverter() {
 		public Object convert(LuanState luan,Object obj) {
-			LuanTable tbl = Luan.toTable(obj);
+			LuanTable tbl = luan.toTable(obj);
 			return tbl!=null ? tbl : obj;
 		}
 		@Override public String toString() {
@@ -351,7 +351,7 @@
 		public Object convert(LuanState luan,Object obj) throws LuanException {
 			if( obj instanceof LuanTable ) {
 				LuanTable t = (LuanTable)obj;
-				return t.asMap(luan);
+				return t.asMap();
 			}
 			return obj;
 		}
@@ -378,8 +378,8 @@
 		public Object convert(LuanState luan,Object obj) throws LuanException {
 			if( obj instanceof LuanTable ) {
 				LuanTable t = (LuanTable)obj;
-				if( t.isSet(luan) )
-					return t.asSet(luan);
+				if( t.isSet() )
+					return t.asSet();
 			}
 			return obj;
 		}
@@ -394,8 +394,8 @@
 				LuanTable t = (LuanTable)obj;
 				if( t.isList() )
 					return t.asList();
-				if( t.isSet(luan) )
-					return t.asSet(luan);
+				if( t.isSet() )
+					return t.asSet();
 			}
 			return obj;
 		}
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/LuanState.java
--- a/src/luan/LuanState.java	Sun Sep 30 19:10:48 2018 -0600
+++ b/src/luan/LuanState.java	Mon Nov 12 02:10:41 2018 -0700
@@ -1,9 +1,14 @@
 package luan;
 
+import java.lang.reflect.Array;
 import java.io.Closeable;
 import java.io.IOException;
 import java.util.Map;
 import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.Arrays;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import luan.impl.LuanCompiler;
@@ -64,7 +69,7 @@
 	public String toString(Object obj) throws LuanException {
 		if( obj instanceof LuanTable ) {
 			LuanTable tbl = (LuanTable)obj;
-			return tbl.toString(this);
+			return tbl.toStringLuan();
 		}
 		if( obj == null )
 			return "nil";
@@ -78,7 +83,7 @@
 	public Object index(Object obj,Object key) throws LuanException {
 		if( obj instanceof LuanTable ) {
 			LuanTable tbl = (LuanTable)obj;
-			return tbl.get(this,key);
+			return tbl.get(key);
 		}
 		if( obj != null && javaOk.ok )
 			return JavaLuan.__index(this,obj,key);
@@ -121,9 +126,41 @@
 	}
 
 	public LuanFunction getHandlerFunction(String op,LuanTable t) throws LuanException {
-		Object f = t.getHandler(this,op);
+		Object f = t.getHandler(op);
 		if( f == null )
 			return null;
 		return Luan.checkFunction(f);
 	}
+
+	public LuanTable toTable(Object obj) {
+		if( obj == null )
+			return null;
+		if( obj instanceof LuanTable )
+			return (LuanTable)obj;
+		if( obj instanceof List ) {
+			return new LuanTable(this,(List)obj);
+		}
+		if( obj instanceof Map ) {
+			return new LuanTable(this,(Map)obj);
+		}
+		if( obj instanceof Set ) {
+			return new LuanTable(this,(Set)obj);
+		}
+		Class cls = obj.getClass();
+		if( cls.isArray() ) {
+			if( cls.getComponentType().isPrimitive() ) {
+				int len = Array.getLength(obj);
+				List list = new ArrayList();
+				for( int i=0; i<len; i++ ) {
+					list.add(Array.get(obj,i));
+				}
+				return new LuanTable(this,list);
+			} else {
+				Object[] a = (Object[])obj;
+				return new LuanTable(this,Arrays.asList(a));
+			}
+		}
+		return null;
+	}
+
 }
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/LuanTable.java
--- a/src/luan/LuanTable.java	Sun Sep 30 19:10:48 2018 -0600
+++ b/src/luan/LuanTable.java	Mon Nov 12 02:10:41 2018 -0700
@@ -14,15 +14,18 @@
 
 
 public final class LuanTable implements LuanCloneable {
+	private LuanState luan;
 	private Map map = null;
 	private List list = null;
 	private LuanTable metatable = null;
 	public LuanJavaOk javaOk;
 	private LuanCloner cloner;
 
-	public LuanTable() {}
+	public LuanTable(LuanState luan) {
+		this.luan = luan;
+	}
 
-	public LuanTable(List list) {
+	public LuanTable(LuanState luan,List list) {
 		int n = list.size();
 		for( int i=0; i<n; i++ ) {
 			Object val = list.get(i);
@@ -31,7 +34,8 @@
 		}
 	}
 
-	public LuanTable(Map map) {
+	public LuanTable(LuanState luan,Map map) {
+		this.luan = luan;
 		for( Object stupid : map.entrySet() ) {
 			Map.Entry entry = (Map.Entry)stupid;
 			Object key = entry.getKey();
@@ -41,7 +45,8 @@
 		}
 	}
 
-	public LuanTable(Set set) {
+	public LuanTable(LuanState luan,Set set) {
+		this.luan = luan;
 		for( Object el : set ) {
 			if( el != null )
 				rawPut(el,Boolean.TRUE);
@@ -49,6 +54,7 @@
 	}
 
 	public LuanTable(LuanTable tbl) {
+		this.luan = tbl.luan;
 		if( tbl.map != null && !tbl.map.isEmpty() )
 			this.map = new LinkedHashMap<Object,Object>(tbl.map);
 		if( tbl.rawLength() > 0 )
@@ -57,7 +63,7 @@
 	}
 
 	@Override public LuanTable shallowClone() {
-		return new LuanTable();
+		return new LuanTable(luan);
 	}
 
 	@Override public void deepenClone(LuanCloneable dc,LuanCloner cloner) {
@@ -85,6 +91,7 @@
 	}
 
 	private void deepenClone(LuanTable clone,LuanCloner cloner) {
+		clone.luan = (LuanState)cloner.clone(luan);
 		if( map != null ) {
 			Map newMap = newMap();
 			for( Object stupid : map.entrySet() ) {
@@ -118,8 +125,8 @@
 		return map!=null ? map : Collections.emptyMap();
 	}
 
-	public String toString(LuanState luan) throws LuanException {
-		Object h = getHandler(luan,"__to_string");
+	public String toStringLuan() throws LuanException {
+		Object h = getHandler("__to_string");
 		if( h == null )
 			return rawToString();
 		LuanFunction fn = Luan.checkFunction(h);
@@ -130,11 +137,11 @@
 		return "table: " + Integer.toHexString(hashCode());
 	}
 
-	public Object get(LuanState luan,Object key) throws LuanException {
+	public Object get(Object key) throws LuanException {
 		Object value = rawGet(key);
 		if( value != null )
 			return value;
-		Object h = getHandler(luan,"__index");
+		Object h = getHandler("__index");
 		if( h==null )
 			return null;
 		if( h instanceof LuanFunction ) {
@@ -163,8 +170,8 @@
 		return map.get(key);
 	}
 
-	public void put(LuanState luan,Object key,Object value) throws LuanException {
-		Object h = getHandler(luan,"__new_index");
+	public void put(Object key,Object value) throws LuanException {
+		Object h = getHandler("__new_index");
 		if( h==null || rawGet(key)!=null ) {
 			rawPut(key,value);
 			return;
@@ -176,7 +183,7 @@
 		}
 		if( h instanceof LuanTable ) {
 			LuanTable tbl = (LuanTable)h;
-			tbl.put(luan,key,value);
+			tbl.put(key,value);
 			return;
 		}
 		throw new LuanException("invalid type "+Luan.type(h)+" for metamethod __new_index");
@@ -267,8 +274,8 @@
 		Collections.sort(list(),cmp);
 	}
 
-	public int length(LuanState luan) throws LuanException {
-		Object h = getHandler(luan,"__len");
+	public int length() throws LuanException {
+		Object h = getHandler("__len");
 		if( h != null ) {
 			LuanFunction fn = Luan.checkFunction(h);
 			return (Integer)Luan.first(fn.call(luan,new Object[]{this}));
@@ -281,8 +288,8 @@
 		return list==null ? 0 : list.size();
 	}
 
-	public Iterable<Map.Entry> iterable(LuanState luan) throws LuanException {
-		final Iterator<Map.Entry> iter = iterator(luan);
+	public Iterable<Map.Entry> iterable() throws LuanException {
+		final Iterator<Map.Entry> iter = iterator();
 		return new Iterable<Map.Entry>() {
 			public Iterator<Map.Entry> iterator() {
 				return iter;
@@ -299,10 +306,10 @@
 		};
 	}
 
-	public Iterator<Map.Entry> iterator(final LuanState luan) throws LuanException {
-		if( getHandler(luan,"__pairs") == null )
+	public Iterator<Map.Entry> iterator() throws LuanException {
+		if( getHandler("__pairs") == null )
 			return rawIterator();
-		final LuanFunction fn = pairs(luan);
+		final LuanFunction fn = pairs();
 		return new Iterator<Map.Entry>() {
 			private Map.Entry<Object,Object> next = getNext();
 
@@ -336,8 +343,8 @@
 		};
 	}
 
-	public LuanFunction pairs(LuanState luan) throws LuanException {
-		Object h = getHandler(luan,"__pairs");
+	public LuanFunction pairs() throws LuanException {
+		Object h = getHandler("__pairs");
 		if( h != null ) {
 			if( h instanceof LuanFunction ) {
 				LuanFunction fn = (LuanFunction)h;
@@ -420,7 +427,7 @@
 
 	public LuanTable rawSubList(int from,int to) {
 		check();
-		LuanTable tbl = new LuanTable();
+		LuanTable tbl = new LuanTable(luan);
 		tbl.list = new ArrayList<Object>(list().subList(from-1,to-1));
 		return tbl;
 	}
@@ -435,34 +442,34 @@
 		this.metatable = metatable;
 	}
 
-	public Object getHandler(LuanState luan,String op) throws LuanException {
+	public Object getHandler(String op) throws LuanException {
 		check();
-		return metatable==null ? null : metatable.get(luan,op);
+		return metatable==null ? null : metatable.get(op);
 	}
 
 	private Map<Object,Object> newMap() {
 		return new LinkedHashMap<Object,Object>();
 	}
 
-	public boolean isSet(LuanState luan) throws LuanException {
-		for( Map.Entry<Object,Object> entry : iterable(luan) ) {
+	public boolean isSet() throws LuanException {
+		for( Map.Entry<Object,Object> entry : iterable() ) {
 			if( !entry.getValue().equals(Boolean.TRUE) )
 				return false;
 		}
 		return true;
 	}
 
-	public Set<Object> asSet(LuanState luan) throws LuanException {
+	public Set<Object> asSet() throws LuanException {
 		Set<Object> set = new HashSet<Object>();
-		for( Map.Entry<Object,Object> entry : iterable(luan) ) {
+		for( Map.Entry<Object,Object> entry : iterable() ) {
 			set.add(entry.getKey());
 		}
 		return set;
 	}
 
-	public Map<Object,Object> asMap(LuanState luan) throws LuanException {
+	public Map<Object,Object> asMap() throws LuanException {
 		Map<Object,Object> map = newMap();
-		for( Map.Entry<Object,Object> entry : iterable(luan) ) {
+		for( Map.Entry<Object,Object> entry : iterable() ) {
 			map.put(entry.getKey(),entry.getValue());
 		}
 		return map;
@@ -495,4 +502,14 @@
 			n += list.size();
 		return n;
 	}
+
+	protected void finalize() throws Throwable {
+		Object h = getHandler("__gc");
+		if( h != null ) {
+			LuanFunction fn = Luan.checkFunction(h);
+			fn.call(luan,new Object[]{this});
+		}
+		super.finalize();
+	}
+
 }
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/impl/LuanImpl.java
--- a/src/luan/impl/LuanImpl.java	Sun Sep 30 19:10:48 2018 -0600
+++ b/src/luan/impl/LuanImpl.java	Mon Nov 12 02:10:41 2018 -0700
@@ -14,7 +14,7 @@
 public final class LuanImpl {
 	private LuanImpl() {}  // never
 
-	public static int len(LuanState luan,Object o) throws LuanException {
+	public static int len(Object o) throws LuanException {
 		if( o instanceof String ) {
 			String s = (String)o;
 			return s.length();
@@ -25,7 +25,7 @@
 		}
 		if( o instanceof LuanTable ) {
 			LuanTable t = (LuanTable)o;
-			return t.length(luan);
+			return t.length();
 		}
 		throw new LuanException( "attempt to get length of a " + Luan.type(o) + " value" );
 	}
@@ -159,7 +159,7 @@
 	public static void put(LuanState luan,Object t,Object key,Object value) throws LuanException {
 		if( t instanceof LuanTable ) {
 			LuanTable tbl = (LuanTable)t;
-			tbl.put(luan,key,value);
+			tbl.put(key,value);
 			return;
 		}
 		if( t != null && luan.javaOk.ok )
@@ -229,8 +229,8 @@
 		}
 	}
 
-	public static LuanTable table(Object[] a) {
-		LuanTable table = new LuanTable();
+	public static LuanTable table(LuanState luan,Object[] a) {
+		LuanTable table = new LuanTable(luan);
 		int i = 0;
 		for( Object fld : a ) {
 			if( fld instanceof TableField ) {
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/impl/LuanParser.java
--- a/src/luan/impl/LuanParser.java	Sun Sep 30 19:10:48 2018 -0600
+++ b/src/luan/impl/LuanParser.java	Mon Nov 12 02:10:41 2018 -0700
@@ -1011,7 +1011,7 @@
 			Spaces();
 			Expr exp = required(UnaryExpr(in)).single();
 			Expr newExp = new Expr(Val.SINGLE,false);
-			newExp.add( "LuanImpl.len(luan," );
+			newExp.add( "LuanImpl.len(" );
 			newExp.addAll( exp );
 			newExp.add( ")" );
 			return parser.success(newExp);
@@ -1143,7 +1143,7 @@
 		if( !parser.match('{') )
 			return parser.failure(null);
 		Expr tblExp = new Expr(Val.SINGLE,false);
-		tblExp.add( "LuanImpl.table(" );
+		tblExp.add( "LuanImpl.table(luan," );
 		Expr lastExp = tblExp;
 		List<Expr> builder = new ArrayList<Expr>();
 /*
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/modules/BasicLuan.java
--- a/src/luan/modules/BasicLuan.java	Sun Sep 30 19:10:48 2018 -0600
+++ b/src/luan/modules/BasicLuan.java	Mon Nov 12 02:10:41 2018 -0700
@@ -43,9 +43,9 @@
 		return load(src,fileName,null);
 	}
 
-	public static LuanFunction pairs(final LuanState luan,final LuanTable t) throws LuanException {
+	public static LuanFunction pairs(final LuanTable t) throws LuanException {
 		Utils.checkNotNull(t);
-		return t.pairs(luan);
+		return t.pairs();
 	}
 
 	public static LuanFunction ipairs(final LuanTable t) throws LuanException {
@@ -73,9 +73,9 @@
 		return obj!=null ? obj : metatable;
 	}
 
-	public static void set_metatable(LuanState luan,LuanTable table,LuanTable metatable) throws LuanException {
+	public static void set_metatable(LuanTable table,LuanTable metatable) throws LuanException {
 		Utils.checkNotNull(table);
-		if( table.getHandler(luan,"__metatable") != null )
+		if( table.getHandler("__metatable") != null )
 			throw new LuanException("cannot change a protected metatable");
 		table.setMetatable(metatable);
 	}
@@ -110,7 +110,7 @@
 
 	public static LuanTable new_error(LuanState luan,Object msg) throws LuanException {
 		String s = luan.toString(msg);
-		LuanTable tbl = new LuanException(s).table();
+		LuanTable tbl = new LuanException(s).table(luan);
 		tbl.rawPut( "message", msg );
 		return tbl;
 	}
@@ -166,21 +166,21 @@
 
 	public static Object try_(LuanState luan,LuanTable blocks,Object... args) throws LuanException {
 		Utils.checkNotNull(blocks);
-		Object obj = blocks.get(luan,1);
+		Object obj = blocks.get(1);
 		if( obj == null )
 			throw new LuanException("missing 'try' value");
 		if( !(obj instanceof LuanFunction) )
 			throw new LuanException("bad 'try' value (function expected, got "+Luan.type(obj)+")");
 		LuanFunction tryFn = (LuanFunction)obj;
 		LuanFunction catchFn = null;
-		obj = blocks.get(luan,"catch");
+		obj = blocks.get("catch");
 		if( obj != null ) {
 			if( !(obj instanceof LuanFunction) )
 				throw new LuanException("bad 'catch' value (function expected, got "+Luan.type(obj)+")");
 			catchFn = (LuanFunction)obj;
 		}
 		LuanFunction finallyFn = null;
-		obj = blocks.get(luan,"finally");
+		obj = blocks.get("finally");
 		if( obj != null ) {
 			if( !(obj instanceof LuanFunction) )
 				throw new LuanException("bad 'finally' value (function expected, got "+Luan.type(obj)+")");
@@ -191,7 +191,7 @@
 		} catch(LuanException e) {
 			if( catchFn == null )
 				throw e;
-			return catchFn.call(luan,new Object[]{e.table()});
+			return catchFn.call(luan,new Object[]{e.table(luan)});
 		} finally {
 			if( finallyFn != null )
 				finallyFn.call(luan);
@@ -208,7 +208,7 @@
 			}
 			return rtn;
 		} catch(LuanException e) {
-			return new Object[]{false,e.table()};
+			return new Object[]{false,e.table(luan)};
 		}
 	}
 
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/modules/IoLuan.java
--- a/src/luan/modules/IoLuan.java	Sun Sep 30 19:10:48 2018 -0600
+++ b/src/luan/modules/IoLuan.java	Mon Nov 12 02:10:41 2018 -0700
@@ -79,8 +79,8 @@
 		};
 	}
 
-	public static LuanTable textWriter(final PrintStream out) {
-		return writer(luanWriter(out));
+	public static LuanTable textWriter(LuanState luan,final PrintStream out) {
+		return writer(luan,luanWriter(out));
 	}
 
 	private static LuanWriter luanWriter(final Writer out) {
@@ -102,12 +102,12 @@
 		};
 	}
 
-	public static LuanTable textWriter(final Writer out) {
-		return writer(luanWriter(out));
+	public static LuanTable textWriter(LuanState luan,final Writer out) {
+		return writer(luan,luanWriter(out));
 	}
 
-	private static LuanTable writer(LuanWriter luanWriter) {
-		LuanTable writer = new LuanTable();
+	private static LuanTable writer(LuanState luan,LuanWriter luanWriter) {
+		LuanTable writer = new LuanTable(luan);
 		writer.rawPut( "java", luanWriter.out() );
 		try {
 			writer.rawPut( "write", new LuanJavaFunction(
@@ -123,8 +123,8 @@
 	}
 
 
-	public static LuanTable binaryWriter(final OutputStream out) {
-		LuanTable writer = new LuanTable();
+	public static LuanTable binaryWriter(LuanState luan,final OutputStream out) {
+		LuanTable writer = new LuanTable(luan);
 		writer.rawPut( "java", out );
 		try {
 			writer.rawPut( "write", new LuanJavaFunction(
@@ -202,40 +202,40 @@
 
 
 	public static abstract class LuanIn {
-		public abstract InputStream inputStream() throws IOException, LuanException;
+		public abstract InputStream inputStream(LuanState luan) throws IOException, LuanException;
 		public abstract String to_string();
 		public abstract String to_uri_string();
 
-		public Reader reader() throws IOException, LuanException {
-			return new InputStreamReader(inputStream());
+		public Reader reader(LuanState luan) throws IOException, LuanException {
+			return new InputStreamReader(inputStream(luan));
 		}
 
-		public String read_text() throws IOException, LuanException {
-			Reader in = reader();
+		public String read_text(LuanState luan) throws IOException, LuanException {
+			Reader in = reader(luan);
 			String s = Utils.readAll(in);
 			in.close();
 			return s;
 		}
 
-		public byte[] read_binary() throws IOException, LuanException {
-			InputStream in = inputStream();
+		public byte[] read_binary(LuanState luan) throws IOException, LuanException {
+			InputStream in = inputStream(luan);
 			byte[] a = Utils.readAll(in);
 			in.close();
 			return a;
 		}
 
-		public LuanFunction read_lines() throws IOException, LuanException {
-			return lines(new BufferedReader(reader()));
+		public LuanFunction read_lines(LuanState luan) throws IOException, LuanException {
+			return lines(new BufferedReader(reader(luan)));
 		}
 
-		public LuanFunction read_blocks(Integer blockSize) throws IOException, LuanException {
+		public LuanFunction read_blocks(LuanState luan,Integer blockSize) throws IOException, LuanException {
 			int n = blockSize!=null ? blockSize : Utils.bufSize;
-			return blocks(inputStream(),n);
+			return blocks(inputStream(luan),n);
 		}
 
-		public boolean exists() throws IOException, LuanException {
+		public boolean exists(LuanState luan) throws IOException, LuanException {
 			try {
-				inputStream().close();
+				inputStream(luan).close();
 				return true;
 			} catch(FileNotFoundException e) {
 				return false;
@@ -244,9 +244,9 @@
 			}
 		}
 
-		public long checksum() throws IOException, LuanException {
+		public long checksum(LuanState luan) throws IOException, LuanException {
 			long cs = 0;
-			InputStream in = new BufferedInputStream(inputStream());
+			InputStream in = new BufferedInputStream(inputStream(luan));
 			int c;
 			while( (c=in.read()) != -1 ) {
 				cs = 31 * cs + c;
@@ -255,8 +255,8 @@
 			return cs;
 		}
 
-		public LuanTable table() {
-			LuanTable tbl = new LuanTable();
+		public LuanTable table(LuanState luan) {
+			LuanTable tbl = new LuanTable(luan);
 			try {
 				tbl.rawPut( "java", this );
 				tbl.rawPut( "to_string", new LuanJavaFunction(
@@ -266,22 +266,22 @@
 					LuanIn.class.getMethod( "to_uri_string" ), this
 				) );
 				tbl.rawPut( "read_text", new LuanJavaFunction(
-					LuanIn.class.getMethod( "read_text" ), this
+					LuanIn.class.getMethod( "read_text", LuanState.class ), this
 				) );
 				tbl.rawPut( "read_binary", new LuanJavaFunction(
-					LuanIn.class.getMethod( "read_binary" ), this
+					LuanIn.class.getMethod( "read_binary", LuanState.class ), this
 				) );
 				tbl.rawPut( "read_lines", new LuanJavaFunction(
-					LuanIn.class.getMethod( "read_lines" ), this
+					LuanIn.class.getMethod( "read_lines", LuanState.class ), this
 				) );
 				tbl.rawPut( "read_blocks", new LuanJavaFunction(
-					LuanIn.class.getMethod( "read_blocks", Integer.class ), this
+					LuanIn.class.getMethod( "read_blocks", LuanState.class, Integer.class ), this
 				) );
 				tbl.rawPut( "exists", new LuanJavaFunction(
-					LuanIn.class.getMethod( "exists" ), this
+					LuanIn.class.getMethod( "exists", LuanState.class ), this
 				) );
 				tbl.rawPut( "checksum", new LuanJavaFunction(
-					LuanIn.class.getMethod( "checksum" ), this
+					LuanIn.class.getMethod( "checksum", LuanState.class ), this
 				) );
 			} catch(NoSuchMethodException e) {
 				throw new RuntimeException(e);
@@ -292,7 +292,7 @@
 
 	public static final LuanIn defaultStdin = new LuanIn() {
 
-		@Override public InputStream inputStream() {
+		@Override public InputStream inputStream(LuanState luan) {
 			return System.in;
 		}
 
@@ -304,15 +304,15 @@
 			return "stdin:";
 		}
 
-		@Override public String read_text() throws IOException {
+		@Override public String read_text(LuanState luan) throws IOException {
 			return Utils.readAll(new InputStreamReader(System.in));
 		}
 
-		@Override public byte[] read_binary() throws IOException {
+		@Override public byte[] read_binary(LuanState luan) throws IOException {
 			return Utils.readAll(System.in);
 		}
 
-		@Override public boolean exists() {
+		@Override public boolean exists(LuanState luan) {
 			return true;
 		}
 	};
@@ -320,7 +320,7 @@
 	public static abstract class LuanIO extends LuanIn {
 		abstract OutputStream outputStream() throws IOException;
 
-		public void write(Object obj) throws LuanException, IOException {
+		public void write(LuanState luan,Object obj) throws LuanException, IOException {
 			if( obj instanceof String ) {
 				String s = (String)obj;
 				Writer out = new OutputStreamWriter(outputStream());
@@ -340,7 +340,7 @@
 				Object java = t.rawGet("java");
 				if( java instanceof LuanIn ) {
 					LuanIn luanIn = (LuanIn)java;
-					InputStream in = luanIn.inputStream();
+					InputStream in = luanIn.inputStream(luan);
 					OutputStream out = outputStream();
 					Utils.copyAll(in,out);
 					out.close();
@@ -351,12 +351,12 @@
 			throw new LuanException( "bad argument #1 to 'write' (string or binary or Io.uri expected)" );
 		}
 
-		public LuanTable text_writer() throws IOException {
-			return textWriter(new BufferedWriter(new OutputStreamWriter(outputStream())));
+		public LuanTable text_writer(LuanState luan) throws IOException {
+			return textWriter(luan,new BufferedWriter(new OutputStreamWriter(outputStream())));
 		}
 
-		public LuanTable binary_writer() throws IOException {
-			return binaryWriter(new BufferedOutputStream(outputStream()));
+		public LuanTable binary_writer(LuanState luan) throws IOException {
+			return binaryWriter(luan,new BufferedOutputStream(outputStream()));
 		}
 
 		public void write_text(LuanState luan,Object... args) throws LuanException, IOException {
@@ -365,17 +365,17 @@
 			luanWriter.close();
 		}
 
-		@Override public LuanTable table() {
-			LuanTable tbl = super.table();
+		@Override public LuanTable table(LuanState luan) {
+			LuanTable tbl = super.table(luan);
 			try {
 				tbl.rawPut( "write", new LuanJavaFunction(
-					LuanIO.class.getMethod( "write", Object.class ), this
+					LuanIO.class.getMethod( "write", LuanState.class, Object.class ), this
 				) );
 				tbl.rawPut( "text_writer", new LuanJavaFunction(
-					LuanIO.class.getMethod( "text_writer" ), this
+					LuanIO.class.getMethod( "text_writer", LuanState.class ), this
 				) );
 				tbl.rawPut( "binary_writer", new LuanJavaFunction(
-					LuanIO.class.getMethod( "binary_writer" ), this
+					LuanIO.class.getMethod( "binary_writer", LuanState.class ), this
 				) );
 				tbl.rawPut( "write_text", new LuanJavaFunction(
 					LuanIO.class.getMethod( "write_text", LuanState.class, new Object[0].getClass() ), this
@@ -397,7 +397,7 @@
 			@Override public void write(int b) {}
 		};
 
-		@Override public InputStream inputStream() {
+		@Override public InputStream inputStream(LuanState luan) {
 			return in;
 		}
 
@@ -422,7 +422,7 @@
 			this.s = s;
 		}
 
-		@Override public InputStream inputStream() {
+		@Override public InputStream inputStream(LuanState luan) {
 			throw new UnsupportedOperationException();
 		}
 
@@ -438,19 +438,19 @@
 			return "string:" + s;
 		}
 
-		@Override public Reader reader() {
+		@Override public Reader reader(LuanState luan) {
 			return new StringReader(s);
 		}
 
-		@Override public String read_text() {
+		@Override public String read_text(LuanState luan) {
 			return s;
 		}
 
-		@Override public boolean exists() {
+		@Override public boolean exists(LuanState luan) {
 			return true;
 		}
 
-		@Override public LuanTable text_writer() throws IOException {
+		@Override public LuanTable text_writer(LuanState luan) throws IOException {
 			LuanWriter luanWriter = new LuanWriter() {
 				private final Writer out = new StringWriter();
 
@@ -468,7 +468,7 @@
 					s = out.toString();
 				}
 			};
-			return writer(luanWriter);
+			return writer(luan,luanWriter);
 		}
 	}
 
@@ -484,7 +484,7 @@
 			this.file = file;
 		}
 
-		@Override public InputStream inputStream() throws IOException {
+		@Override public InputStream inputStream(LuanState luan) throws IOException {
 			return new FileInputStream(file);
 		}
 
@@ -501,16 +501,16 @@
 		}
 
 		public LuanTable child(LuanState luan,String name) throws LuanException {
-			return new LuanFile(luan,new File(file,name)).table();
+			return new LuanFile(luan,new File(file,name)).table(luan);
 		}
 
 		public LuanTable children(LuanState luan) throws LuanException {
 			File[] files = file.listFiles();
 			if( files==null )
 				return null;
-			LuanTable list = new LuanTable();
+			LuanTable list = new LuanTable(luan);
 			for( File f : files ) {
-				list.rawPut(list.rawLength()+1,new LuanFile(luan,f).table());
+				list.rawPut(list.rawLength()+1,new LuanFile(luan,f).table(luan));
 			}
 			return list;
 		}
@@ -519,10 +519,10 @@
 			File parent = file.getParentFile();
 			if( parent==null )
 				parent = file.getCanonicalFile().getParentFile();
-			return new LuanFile(luan,parent).table();
+			return new LuanFile(luan,parent).table(luan);
 		}
 
-		@Override public boolean exists() {
+		@Override public boolean exists(LuanState luan) {
 			return file.exists();
 		}
 
@@ -535,12 +535,12 @@
 		}
 
 		public LuanTable canonical(LuanState luan) throws LuanException, IOException {
-			return new LuanFile(luan,file.getCanonicalFile()).table();
+			return new LuanFile(luan,file.getCanonicalFile()).table(luan);
 		}
 
 		public LuanTable create_temp_file(LuanState luan,String prefix,String suffix) throws LuanException, IOException {
 			File tmp = File.createTempFile(prefix,suffix,file);
-			return new LuanFile(luan,tmp).table();
+			return new LuanFile(luan,tmp).table(luan);
 		}
 
 		public void delete() throws LuanException {
@@ -571,8 +571,8 @@
 				throw new LuanException("couldn't set_last_modified on "+file);
 		}
 
-		@Override public LuanTable table() {
-			LuanTable tbl = super.table();
+		@Override public LuanTable table(LuanState luan) {
+			LuanTable tbl = super.table(luan);
 			try {
 				tbl.rawPut( "name", new LuanJavaFunction(
 					File.class.getMethod( "getName" ), file
@@ -626,18 +626,18 @@
 		}
 	}
 
-	public static LuanTable null_(String ignore) {
-		return nullIO.table();
+	public static LuanTable null_(LuanState luan,String ignore) {
+		return nullIO.table(luan);
 	}
 
-	public static LuanTable string(String s) throws LuanException {
+	public static LuanTable string(LuanState luan,String s) throws LuanException {
 		Utils.checkNotNull(s);
-		return new LuanString(s).table();
+		return new LuanString(s).table(luan);
 	}
 
 	public static LuanTable file(LuanState luan,String name) throws LuanException {
 		File file = new File(name);
-		return new LuanFile(luan,file).table();
+		return new LuanFile(luan,file).table(luan);
 	}
 
 	public static LuanTable classpath(LuanState luan,String name) throws LuanException {
@@ -666,13 +666,13 @@
 			}
 		}
 		if( url != null )
-			return new LuanUrl(luan,url,null).table();
+			return new LuanUrl(url,null).table(luan);
 
 		return null;
 	}
 
 	private static LuanTable url(LuanState luan,String url,LuanTable options) throws IOException, LuanException {
-		return new LuanUrl(luan,new URL(url),options).table();
+		return new LuanUrl(new URL(url),options).table(luan);
 	}
 
 	public static LuanTable http(LuanState luan,String path,LuanTable options) throws IOException, LuanException {
@@ -689,14 +689,14 @@
 
 	public static LuanTable stdin(LuanState luan) throws LuanException {
 		LuanTable io = (LuanTable)PackageLuan.require(luan,"luan:Io.luan");
-		return (LuanTable)io.get(luan,"stdin");
+		return (LuanTable)io.get("stdin");
 	}
 
-	public static LuanTable newSchemes() {
-		LuanTable schemes = new LuanTable();
+	public static LuanTable newSchemes(LuanState luan) {
+		LuanTable schemes = new LuanTable(luan);
 		try {
-			schemes.rawPut( "null", new LuanJavaFunction(IoLuan.class.getMethod("null_",String.class),null) );
-			add( schemes, "string", String.class );
+			schemes.rawPut( "null", new LuanJavaFunction(IoLuan.class.getMethod("null_",LuanState.class,String.class),null) );
+			add( schemes, "string", LuanState.class, String.class );
 			add( schemes, "file", LuanState.class, String.class );
 			add( schemes, "classpath", LuanState.class, String.class );
 //			add( schemes, "socket", String.class );
@@ -715,10 +715,10 @@
 	private static LuanTable schemes(LuanState luan) throws LuanException {
 		LuanTable t = (LuanTable)PackageLuan.loaded(luan).rawGet("luan:Io.luan");
 		if( t == null )
-			return newSchemes();
-		t = (LuanTable)t.get(luan,"schemes");
+			return newSchemes(luan);
+		t = (LuanTable)t.get("schemes");
 		if( t == null )
-			return newSchemes();
+			return newSchemes(luan);
 		return t;
 	}
 
@@ -729,7 +729,7 @@
 		String scheme = name.substring(0,i);
 		String location = name.substring(i+1);
 		LuanTable schemes = schemes(luan);
-		LuanFunction opener = (LuanFunction)schemes.get(luan,scheme);
+		LuanFunction opener = (LuanFunction)schemes.get(scheme);
 		if( opener == null )
 			throw new LuanException( "invalid scheme '"+scheme+"' in '"+name+"'" );
 		return (LuanTable)Luan.first(opener.call(luan,new Object[]{location,options}));
@@ -802,11 +802,11 @@
 		final File dir;
 		Process proc;
 
-		private BaseOs(LuanState luan,String cmd,LuanTable options) throws IOException, LuanException {
+		private BaseOs(String cmd,LuanTable options) throws IOException, LuanException {
 			this.cmd = cmd;
 			File dir = null;
 			if( options != null ) {
-				Map map = options.asMap(luan);
+				Map map = options.asMap();
 				Object obj = map.remove("dir");
 				dir = objToFile(obj);
 				if( dir==null )
@@ -817,7 +817,7 @@
 			this.dir = dir;
 		}
 
-		@Override public InputStream inputStream() throws IOException {
+		@Override public InputStream inputStream(LuanState luan) throws IOException {
 			return proc.getInputStream();
 		}
 
@@ -833,7 +833,7 @@
 			throw new UnsupportedOperationException();
 		}
 
-		@Override public boolean exists() {
+		@Override public boolean exists(LuanState luan) {
 			return true;
 		}
 
@@ -854,14 +854,14 @@
 			}
 		}
 
-		@Override public String read_text() throws IOException, LuanException {
-			String s = super.read_text();
+		@Override public String read_text(LuanState luan) throws IOException, LuanException {
+			String s = super.read_text(luan);
 			wait_for();
 			return s;
 		}
 
-		@Override public LuanTable table() {
-			LuanTable tbl = super.table();
+		@Override public LuanTable table(LuanState luan) {
+			LuanTable tbl = super.table(luan);
 			try {
 				tbl.rawPut( "wait_for", new LuanJavaFunction(
 					BaseOs.class.getMethod( "wait_for" ), this
@@ -875,26 +875,26 @@
 
 	public static final class LuanOs extends BaseOs {
 		private LuanOs(LuanState luan,String cmd,LuanTable options) throws IOException, LuanException {
-			super(luan,cmd,options);
+			super(cmd,options);
 			check(luan,"os:"+cmd);
 			this.proc = Runtime.getRuntime().exec(cmd,null,dir);
 		}
 	}
 
 	public static LuanTable os(LuanState luan,String cmd,LuanTable options) throws IOException, LuanException {
-		return new LuanOs(luan,cmd,options).table();
+		return new LuanOs(luan,cmd,options).table(luan);
 	}
 
 	public static final class LuanBash extends BaseOs {
 		private LuanBash(LuanState luan,String cmd,LuanTable options) throws IOException, LuanException {
-			super(luan,cmd,options);
+			super(cmd,options);
 			check(luan,"bash:"+cmd);
 			this.proc = Runtime.getRuntime().exec(new String[]{"bash","-c",cmd},null,dir);
 		}
 	}
 
 	public static LuanTable bash(LuanState luan,String cmd,LuanTable options) throws IOException, LuanException {
-		return new LuanBash(luan,cmd,options).table();
+		return new LuanBash(luan,cmd,options).table(luan);
 	}
 
 
@@ -906,7 +906,7 @@
 			this.in = in;
 		}
 
-		@Override public InputStream inputStream() {
+		@Override public InputStream inputStream(LuanState luan) {
 			return in;
 		}
 
@@ -918,7 +918,7 @@
 			throw new UnsupportedOperationException();
 		}
 
-		@Override public boolean exists() {
+		@Override public boolean exists(LuanState luan) {
 			return true;
 		}
 	};
@@ -932,8 +932,8 @@
 		}
 	}
 
-	public static LuanTable my_ips() throws IOException {
-		LuanTable tbl = new LuanTable();
+	public static LuanTable my_ips(LuanState luan) throws IOException {
+		LuanTable tbl = new LuanTable(luan);
 		for( Enumeration<NetworkInterface> e1 = NetworkInterface.getNetworkInterfaces(); e1.hasMoreElements(); ) {
 			NetworkInterface ni = e1.nextElement();
 			for( Enumeration<InetAddress> e2 = ni.getInetAddresses(); e2.hasMoreElements(); ) {
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/modules/JavaLuan.java
--- a/src/luan/modules/JavaLuan.java	Sun Sep 30 19:10:48 2018 -0600
+++ b/src/luan/modules/JavaLuan.java	Mon Nov 12 02:10:41 2018 -0700
@@ -321,7 +321,7 @@
 						if( args==null )
 							args = new Object[0];
 						String name = method.getName();
-						Object fnObj = t.get(luan,name);
+						Object fnObj = t.get(name);
 						if( fnObj == null )
 							throw new NullPointerException("luan_proxy couldn't find method '"+name+"'");
 						LuanFunction fn = Luan.checkFunction(fnObj);
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/modules/PackageLuan.java
--- a/src/luan/modules/PackageLuan.java	Sun Sep 30 19:10:48 2018 -0600
+++ b/src/luan/modules/PackageLuan.java	Mon Nov 12 02:10:41 2018 -0700
@@ -26,7 +26,7 @@
 	public static LuanTable loaded(LuanState luan) {
 		LuanTable tbl = (LuanTable)luan.registry().get("Package.loaded");
 		if( tbl == null ) {
-			tbl = new LuanTable();
+			tbl = new LuanTable(luan);
 			luan.registry().put("Package.loaded",tbl);
 		}
 		return tbl;
@@ -83,9 +83,9 @@
 */
 		IoLuan.LuanIn in = (IoLuan.LuanIn)t.rawGet("java");
 		try {
-			if( !in.exists() )
+			if( !in.exists(luan) )
 				return null;
-			return in.read_text();
+			return in.read_text(luan);
 		} catch(IOException e) {
 			throw new LuanException(e);
 		}
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/modules/StringLuan.java
--- a/src/luan/modules/StringLuan.java	Sun Sep 30 19:10:48 2018 -0600
+++ b/src/luan/modules/StringLuan.java	Mon Nov 12 02:10:41 2018 -0700
@@ -167,7 +167,7 @@
 			StringBuffer sb = new StringBuffer();
 			while( i<max && m.find() ) {
 				String match = m.groupCount()==0 ? m.group() : m.group(1);
-				Object val = t.get(luan,match);
+				Object val = t.get(match);
 				if( val != null ) {
 					String replacement = luan.toString(val);
 					m.appendReplacement(sb,replacement);
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/modules/Table.luan
--- a/src/luan/modules/Table.luan	Sun Sep 30 19:10:48 2018 -0600
+++ b/src/luan/modules/Table.luan	Mon Nov 12 02:10:41 2018 -0700
@@ -20,8 +20,7 @@
 local error = Luan.error
 local type = Luan.type or error()
 local pairs = Luan.pairs or error()
-local LuanJava = require "java:luan.Luan"
-local toTable = LuanJava.toTable or error()
+local toTable = TableLuan.toTable or error()
 
 function Table.java_to_table_shallow(obj)
 	local rtn = toTable(obj)
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/modules/TableLuan.java
--- a/src/luan/modules/TableLuan.java	Sun Sep 30 19:10:48 2018 -0600
+++ b/src/luan/modules/TableLuan.java	Mon Nov 12 02:10:41 2018 -0700
@@ -17,10 +17,10 @@
 
 	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(luan) : j;
+		int last = j==null ? list.length() : j;
 		StringBuilder buf = new StringBuilder();
 		for( int k=first; k<=last; k++ ) {
-			Object val = list.get(luan,k);
+			Object val = list.get(k);
 			if( val==null )
 				break;
 			if( sep!=null && k > first )
@@ -84,23 +84,23 @@
 		}
 	}
 
-	public static LuanTable pack(Object... args) {
-		LuanTable tbl = new LuanTable(Arrays.asList(args));
+	public static LuanTable pack(LuanState luan,Object... args) {
+		LuanTable tbl = new LuanTable(luan,Arrays.asList(args));
 		tbl.rawPut( "n", args.length );
 		return tbl;
 	}
 
-	@LuanMethod public static Object[] unpack(LuanState luan,LuanTable tbl,Integer iFrom,Integer iTo) throws LuanException {
+	@LuanMethod public static Object[] unpack(LuanTable tbl,Integer iFrom,Integer iTo) throws LuanException {
 		int from = iFrom!=null ? iFrom : 1;
-		int to = iTo!=null ? iTo : tbl.length(luan);
+		int to = iTo!=null ? iTo : tbl.length();
 		List<Object> list = new ArrayList<Object>();
 		for( int i=from; i<=to; i++ ) {
-			list.add( tbl.get(luan,i) );
+			list.add( tbl.get(i) );
 		}
 		return list.toArray();
 	}
 
-	public static LuanTable copy(LuanTable list,Integer from,Integer to) {
+	public static LuanTable copy(LuanState luan,LuanTable list,Integer from,Integer to) {
 		if( from == null )
 			return new LuanTable(list);
 		if( to == null )
@@ -124,4 +124,8 @@
 		return tbl.rawSize();
 	}
 
+	public static LuanTable toTable(LuanState luan,Object obj) {
+		return luan.toTable(obj);
+	}
+
 }
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/modules/ThreadLuan.java
--- a/src/luan/modules/ThreadLuan.java	Sun Sep 30 19:10:48 2018 -0600
+++ b/src/luan/modules/ThreadLuan.java	Mon Nov 12 02:10:41 2018 -0700
@@ -125,17 +125,17 @@
 		}
 	}
 
-	private static Object makeSafe(Object v) {
+	private static Object makeSafe(LuanState luan,Object v) {
 		if( v instanceof LuanTable ) {
 			LuanTable tbl = (LuanTable)v;
 			if( tbl.getMetatable() != null )
 				return new Unsafe("table with metatable");
-			LuanTable rtn = new LuanTable();
+			LuanTable rtn = new LuanTable(luan);
 			for( Map.Entry entry : tbl.rawIterable() ) {
-				Object key = makeSafe( entry.getKey() );
+				Object key = makeSafe( luan, entry.getKey() );
 				if( key instanceof Unsafe )
 					return key;
-				Object value = makeSafe( entry.getValue() );
+				Object value = makeSafe( luan, entry.getValue() );
 				if( value instanceof Unsafe )
 					return value;
 				rtn.rawPut(key,value);
@@ -144,7 +144,7 @@
 		} else if( v instanceof Object[] ) {
 			Object[] a = (Object[])v;
 			for( int i=0; i<a.length; i++ ) {
-				Object obj = makeSafe(a[i]);
+				Object obj = makeSafe(luan,a[i]);
 				if( obj instanceof Unsafe )
 					return obj;
 				a[i] = obj;
@@ -167,19 +167,19 @@
 			this.fns = (LuanTable)cloner.get(fns);
 		}
 
-		public synchronized Object call(String fnName,Object... args) throws LuanException {
-			Object obj = makeSafe(args);
+		public synchronized Object call(LuanState callerLuan,String fnName,Object... args) throws LuanException {
+			Object obj = makeSafe(luan,args);
 			if( obj instanceof Unsafe )
 				throw new LuanException("can't pass "+((Unsafe)obj).reason+" to global_callable "+Arrays.asList(args));
 			args = (Object[])obj;
-			Object f = fns.get(luan,fnName);
+			Object f = fns.get(fnName);
 			if( f == null )
 				throw new LuanException("function '"+fnName+"' not found in global_callable");
 			if( !(f instanceof LuanFunction) )
 				throw new LuanException("value of '"+fnName+"' not a function in global_callable");
 			LuanFunction fn = (LuanFunction)f;
 			Object rtn = fn.call(luan,args);
-			rtn = makeSafe(rtn);
+			rtn = makeSafe(callerLuan,rtn);
 			if( rtn instanceof Unsafe )
 				throw new LuanException("can't return "+((Unsafe)rtn).reason+" from global_callable");
 			return rtn;
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/modules/http/HttpServicer.java
--- a/src/luan/modules/http/HttpServicer.java	Sun Sep 30 19:10:48 2018 -0600
+++ b/src/luan/modules/http/HttpServicer.java	Mon Nov 12 02:10:41 2018 -0700
@@ -35,7 +35,7 @@
 		}
 		LuanTable module = (LuanTable)PackageLuan.require(luan,"luan:http/Http.luan");
 		LuanFunction fn = (LuanFunction)module.rawGet("handle_error");
-		return (Response)fn.call( luan, new Object[]{request,e.table()} );
+		return (Response)fn.call( luan, new Object[]{request,e.table(luan)} );
 	}
 
 	private static Response serviceLuan(LuanState luan,Request request,String modName)
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/modules/http/LuanHandler.java
--- a/src/luan/modules/http/LuanHandler.java	Sun Sep 30 19:10:48 2018 -0600
+++ b/src/luan/modules/http/LuanHandler.java	Mon Nov 12 02:10:41 2018 -0700
@@ -137,8 +137,8 @@
 			synchronized(luan) {
 				PackageLuan.enableLoad(luan,"luan:Rpc.luan");
 				LuanTable rpc = (LuanTable)PackageLuan.require(luan,"luan:Rpc.luan");
-				LuanTable fns = (LuanTable)rpc.get(luan,"functions");
-				fn = (LuanFunction)fns.get(luan,fnName);
+				LuanTable fns = (LuanTable)rpc.get("functions");
+				fn = (LuanFunction)fns.get(fnName);
 				if( fn == null )
 					throw new LuanException( "function not found: " + fnName );
 				LuanCloner cloner = new LuanCloner(LuanCloner.Type.INCREMENTAL);
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/modules/lucene/LuceneIndex.java
--- a/src/luan/modules/lucene/LuceneIndex.java	Sun Sep 30 19:10:48 2018 -0600
+++ b/src/luan/modules/lucene/LuceneIndex.java	Mon Nov 12 02:10:41 2018 -0700
@@ -94,7 +94,7 @@
 	private final ThreadLocal<IndexSearcher> threadLocalSearcher = new ThreadLocal<IndexSearcher>();
 	private boolean isClosed = true;
 	private final MultiFieldParser mfp;
-	public final LuanTable indexed_only_fields = new LuanTable();
+	public final LuanTable indexed_only_fields;
 	private final Analyzer analyzer;
 	private final Exception created = new Exception("created");
 
@@ -102,7 +102,10 @@
 	private File fileDir;
 	private int writeCount;
 
-	public LuceneIndex(LuanState luan,String indexDirStr,FieldParser defaultFieldParser,String[] defaultFields) throws LuanException, IOException {
+	public LuceneIndex(LuanState luan,String indexDirStr,FieldParser defaultFieldParser,String[] defaultFields)
+		throws LuanException, IOException
+	{
+		indexed_only_fields = new LuanTable(luan);  // not good, not thread safe
 		mfp = defaultFieldParser==null ? new MultiFieldParser() : new MultiFieldParser(defaultFieldParser,defaultFields);
 		mfp.fields.put( "type", STRING_FIELD_PARSER );
 		mfp.fields.put( "id", NumberFieldParser.LONG );
@@ -177,18 +180,18 @@
 
 	public void save(LuanState luan,LuanTable doc) throws LuanException, IOException {
 		Set indexedOnlySet = new HashSet();
-		Object typeObj = doc.get(luan,"type");
+		Object typeObj = doc.get("type");
 		if( typeObj==null )
 			throw new LuanException("missing 'type' field");
 		if( !(typeObj instanceof String) )
 			throw new LuanException("type must be string");
 		String type = (String)typeObj;
-		Object indexedOnlyObj = indexed_only_fields.get(luan,type);
+		Object indexedOnlyObj = indexed_only_fields.get(type);
 		if( indexedOnlyObj != null ) {
 			if( !(indexedOnlyObj instanceof LuanTable) )
 				throw new LuanException("indexed_only_fields elements must be tables");
 			LuanTable indexedOnly = (LuanTable)indexedOnlyObj;
-			for( Map.Entry<Object,Object> entry : indexedOnly.iterable(luan) ) {
+			for( Map.Entry<Object,Object> entry : indexedOnly.iterable() ) {
 				Object key = entry.getKey();
 				if( !(key instanceof String) )
 					throw new LuanException("indexed_only_fields."+type+" entries must be strings");
@@ -198,11 +201,11 @@
 					throw new LuanException("indexed_only_fields."+type+" values must be functions");
 				LuanFunction fn = (LuanFunction)value;
 				value = Luan.first(fn.call(luan,new Object[]{doc}));
-				doc.put(luan, name, value );
+				doc.put( name, value );
 				indexedOnlySet.add(name);
 			}
 		}
-		Object obj = doc.get(luan,"id");
+		Object obj = doc.get("id");
 		Long id;
 		try {
 			id = (Long)obj;
@@ -215,10 +218,10 @@
 		try {
 			if( id == null ) {
 				id = nextId(luan);
-				doc.put(luan,"id",id);
-				writer.addDocument(toLucene(luan,doc,indexedOnlySet));
+				doc.put("id",id);
+				writer.addDocument(toLucene(doc,indexedOnlySet));
 			} else {
-				writer.updateDocument( term("id",id), toLucene(luan,doc,indexedOnlySet) );
+				writer.updateDocument( term("id",id), toLucene(doc,indexedOnlySet) );
 			}
 			if(commit) writer.commit();
 		} finally {
@@ -277,10 +280,10 @@
 	public synchronized long nextId(LuanState luan) throws LuanException, IOException {
 		if( ++id > idLim ) {
 			idLim += idBatch;
-			LuanTable doc = new LuanTable();
+			LuanTable doc = new LuanTable(luan);
 			doc.rawPut( "type", "next_id" );
 			doc.rawPut( FLD_NEXT_ID, idLim );
-			writer.updateDocument(new Term("type","next_id"),toLucene(luan,doc,Collections.EMPTY_SET));
+			writer.updateDocument(new Term("type","next_id"),toLucene(doc,Collections.EMPTY_SET));
 			wrote();
 		}
 		return id;
@@ -314,7 +317,7 @@
 		IndexCommit ic = snapshotDeletionPolicy.snapshot();
 		try {
 			String dir = fileDir.toString();
-			LuanTable fileNames = new LuanTable(new ArrayList(ic.getFileNames()));
+			LuanTable fileNames = new LuanTable(luan,new ArrayList(ic.getFileNames()));
 			return fn.call(luan,new Object[]{dir,fileNames});
 		} finally {
 			snapshotDeletionPolicy.release(ic);
@@ -355,7 +358,7 @@
 
 		@Override public Object call(LuanState luan,Object[] args) throws LuanException {
 			try {
-				return toTable(searcher.doc(docID));
+				return toTable(luan,searcher.doc(docID));
 			} catch(IOException e) {
 				throw new LuanException(e);
 			}
@@ -518,10 +521,10 @@
 			throw new LuanException("invalid value type "+value.getClass()+"' for '"+name+"'");
 	}
 
-	private Document toLucene(LuanState luan,LuanTable table,Set indexOnly) throws LuanException {
+	private Document toLucene(LuanTable table,Set indexOnly) throws LuanException {
 		Set<String> indexed = mfp.fields.keySet();
 		Document doc = new Document();
-		for( Map.Entry<Object,Object> entry : table.iterable(luan) ) {
+		for( Map.Entry<Object,Object> entry : table.iterable() ) {
 			Object key = entry.getKey();
 			if( !(key instanceof String) )
 				throw new LuanException("key must be string");
@@ -553,10 +556,10 @@
 		throw new LuanException("invalid field type for "+ifld);
 	}
 
-	private static LuanTable toTable(Document doc) throws LuanException {
+	private static LuanTable toTable(LuanState luan,Document doc) throws LuanException {
 		if( doc==null )
 			return null;
-		LuanTable table = new LuanTable();
+		LuanTable table = new LuanTable(luan);
 		for( IndexableField ifld : doc ) {
 			String name = ifld.name();
 			Object value = getValue(ifld);
@@ -568,7 +571,7 @@
 				if( old instanceof LuanTable ) {
 					list = (LuanTable)old;
 				} else {
-					list = new LuanTable();
+					list = new LuanTable(luan);
 					list.rawPut(1,old);
 					table.rawPut(name,list);
 				}
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/modules/mail/SmtpCon.java
--- a/src/luan/modules/mail/SmtpCon.java	Sun Sep 30 19:10:48 2018 -0600
+++ b/src/luan/modules/mail/SmtpCon.java	Mon Nov 12 02:10:41 2018 -0700
@@ -24,8 +24,8 @@
 public final class SmtpCon {
 	private final Session session;
 
-	public SmtpCon(LuanState luan,LuanTable paramsTbl) throws LuanException {
-		Map<Object,Object> params = new HashMap<Object,Object>(paramsTbl.asMap(luan));
+	public SmtpCon(LuanTable paramsTbl) throws LuanException {
+		Map<Object,Object> params = new HashMap<Object,Object>(paramsTbl.asMap());
 		Properties props = new Properties(System.getProperties());
 
 		String host = getString(params,"host");
@@ -71,7 +71,7 @@
 			throw new LuanException( "unrecognized parameters: "+params );
 	}
 
-	private String getString(Map<Object,Object> params,String key) throws LuanException {
+	private static String getString(Map<Object,Object> params,String key) throws LuanException {
 		Object val = params.remove(key);
 		if( val!=null && !(val instanceof String) )
 			throw new LuanException( "parameter '"+key+"' must be a string" );
@@ -79,9 +79,9 @@
 	}
 
 
-	public void send(LuanState luan,LuanTable mailTbl) throws LuanException {
+	public void send(LuanTable mailTbl) throws LuanException {
 		try {
-			Map<Object,Object> mailParams = new HashMap<Object,Object>(mailTbl.asMap(luan));
+			Map<Object,Object> mailParams = new HashMap<Object,Object>(mailTbl.asMap());
 			MimeMessage msg = new MimeMessage(session);
 
 			String from = getString(mailParams,"from");
@@ -113,7 +113,7 @@
 					bodyPart.setText((String)body);
 				} else if( body instanceof LuanTable ) {
 					LuanTable bodyTbl = (LuanTable)body;
-					Map<Object,Object> map = new HashMap<Object,Object>(bodyTbl.asMap(luan));
+					Map<Object,Object> map = new HashMap<Object,Object>(bodyTbl.asMap());
 					MimeMultipart mp = new MimeMultipart("alternative");
 					String text = (String)map.remove("text");
 					if( text != null ) {
@@ -146,7 +146,7 @@
 				for( Object attachment : attachmentsTbl.asList() ) {
 					if( !(attachment instanceof LuanTable) )
 						throw new LuanException( "each attachment must be a table" );
-					Map<Object,Object> attachmentMap = new HashMap<Object,Object>(((LuanTable)attachment).asMap(luan));
+					Map<Object,Object> attachmentMap = new HashMap<Object,Object>(((LuanTable)attachment).asMap());
 					Object obj;
 
 					obj = attachmentMap.remove("filename");
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/modules/parsers/Csv.java
--- a/src/luan/modules/parsers/Csv.java	Sun Sep 30 19:10:48 2018 -0600
+++ b/src/luan/modules/parsers/Csv.java	Mon Nov 12 02:10:41 2018 -0700
@@ -1,5 +1,6 @@
 package luan.modules.parsers;
 
+import luan.LuanState;
 import luan.LuanTable;
 import luan.lib.parser.Parser;
 import luan.lib.parser.ParseException;
@@ -7,8 +8,8 @@
 
 public final class Csv {
 
-	public static LuanTable toList(String line) throws ParseException {
-		return new Csv(line).parse();
+	public static LuanTable toList(LuanState luan,String line) throws ParseException {
+		return new Csv(line).parse(luan);
 	}
 
 	private final Parser parser;
@@ -21,8 +22,8 @@
 		return new ParseException(parser,msg);
 	}
 
-	private LuanTable parse() throws ParseException {
-		LuanTable list = new LuanTable();
+	private LuanTable parse(LuanState luan) throws ParseException {
+		LuanTable list = new LuanTable(luan);
 		while(true) {
 			Spaces();
 			String field = parseField();
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/modules/parsers/Html.java
--- a/src/luan/modules/parsers/Html.java	Sun Sep 30 19:10:48 2018 -0600
+++ b/src/luan/modules/parsers/Html.java	Mon Nov 12 02:10:41 2018 -0700
@@ -4,6 +4,7 @@
 import java.util.ArrayList;
 import java.util.Set;
 import java.util.HashSet;
+import luan.LuanState;
 import luan.LuanTable;
 import luan.lib.parser.Parser;
 import luan.lib.parser.ParseException;
@@ -11,14 +12,18 @@
 
 public final class Html {
 
-	public static LuanTable toList(String text,LuanTable containerTagsTbl) throws ParseException {
-		return new Html(text,containerTagsTbl).parse();
+	public static LuanTable toList(LuanState luan,String text,LuanTable containerTagsTbl)
+		throws ParseException
+	{
+		return new Html(luan,text,containerTagsTbl).parse();
 	}
 
+	private final LuanState luan;
 	private final Parser parser;
 	private final Set<String> containerTags = new HashSet<String>();
 
-	private Html(String text,LuanTable containerTagsTbl) {
+	private Html(LuanState luan,String text,LuanTable containerTagsTbl) {
+		this.luan = luan;
 		this.parser = new Parser(text);
 		for( Object v : containerTagsTbl.asList() ) {
 			containerTags.add((String)v);
@@ -56,7 +61,7 @@
 		}
 		if( sb.length() > 0 )
 			list.add(sb.toString());
-		return new LuanTable(list);
+		return new LuanTable(luan,list);
 	}
 
 	private LuanTable parseComment() {
@@ -69,7 +74,7 @@
 				return parser.failure(null);
 		}
 		String text = parser.textFrom(start);
-		LuanTable tbl = new LuanTable();
+		LuanTable tbl = new LuanTable(luan);
 		tbl.rawPut("type","comment");
 		tbl.rawPut("text",text);
 		return parser.success(tbl);
@@ -85,7 +90,7 @@
 				return parser.failure(null);
 		}
 		String text = parser.textFrom(start);
-		LuanTable tbl = new LuanTable();
+		LuanTable tbl = new LuanTable(luan);
 		tbl.rawPut("type","cdata");
 		tbl.rawPut("text",text);
 		return parser.success(tbl);
@@ -107,7 +112,7 @@
 				return parser.failure(null);
 		}
 		String text = parser.text.substring(start,end);
-		LuanTable tbl = new LuanTable();
+		LuanTable tbl = new LuanTable(luan);
 		tbl.rawPut("type","container");
 		tbl.rawPut("tag",tag);
 		tbl.rawPut("text",text);
@@ -124,7 +129,7 @@
 			return parser.failure(null);
 		while( matchNameChar() );
 		String name = parser.textFrom(start).toLowerCase();
-		LuanTable attributes = new LuanTable();
+		LuanTable attributes = new LuanTable(luan);
 		String attrName;
 		while( (attrName = parseAttrName()) != null ) {
 			String attrValue = parseAttrValue();
@@ -134,7 +139,7 @@
 		boolean isEmpty = parser.match('/');
 		if( !parser.match('>') )
 			return parser.failure(null);
-		LuanTable tbl = new LuanTable();
+		LuanTable tbl = new LuanTable(luan);
 		tbl.rawPut("type","tag");
 		tbl.rawPut("name",name);
 		tbl.rawPut("attributes",attributes);
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/modules/sql/Database.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/sql/Database.java	Mon Nov 12 02:10:41 2018 -0700
@@ -0,0 +1,120 @@
+package luan.modules.sql;
+
+import java.io.Closeable;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.Statement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Properties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import luan.LuanState;
+import luan.LuanTable;
+import luan.LuanException;
+
+
+public final class Database implements Closeable {
+	private static final Logger logger = LoggerFactory.getLogger(Database.class);
+
+	private static Map<Map,Database> pool = new HashMap<Map,Database>();
+
+	public static Database get(Connection con) {
+		return new Database(con);
+	}
+
+	public static synchronized Database get(LuanTable specTbl)
+		throws LuanException, ClassNotFoundException, SQLException
+	{
+		Map<Object,Object> spec = specTbl.asMap();
+		Database db = pool.get(spec);
+		if( db==null ) {
+			db = new Database(spec);
+			pool.put(spec,db);
+		}
+		return db;
+	}
+
+	public int uses = 0;
+	public final Connection con;
+	private final Map<String,PreparedStatement> pstmts = new HashMap<String,PreparedStatement>();
+
+	public Database(Connection con) {
+		this.con = con;
+	}
+
+	private Database(Map<Object,Object> spec)
+		throws LuanException, ClassNotFoundException, SQLException
+	{
+		spec = new HashMap<Object,Object>(spec);
+		String cls = getString(spec,"class");
+		Class.forName(cls);
+		String url = getString(spec,"url");
+		Properties props = new Properties();
+		props.putAll(spec);
+		this.con = DriverManager.getConnection(url,props);
+	}
+
+	private static String getString(Map spec,String key) throws LuanException {
+		Object val = spec.remove(key);
+		if( val==null )
+			throw new LuanException( "parameter '"+key+"' is required" );
+		if( !(val instanceof String) )
+			throw new LuanException( "parameter '"+key+"' must be a string" );
+		return (String)val;
+	}
+
+	private PreparedStatement prepareStatement(String sql,Object[] args) throws SQLException {
+		PreparedStatement pstmt = pstmts.get(sql);
+		if( pstmt==null ) {
+			pstmt = con.prepareStatement(sql);
+			pstmts.put(sql,pstmt);
+		}
+		for( int i=0; i<args.length; i++ ) {
+			pstmt.setObject(i+1,args[i]);
+		}
+		return pstmt;
+	}
+
+	public ResultSet query(String sql,Object... args) throws SQLException {
+		if( args.length == 0 ) {
+			Statement stmt = con.createStatement();
+			return stmt.executeQuery(sql);
+		} else {
+			PreparedStatement pstmt = prepareStatement(sql,args);
+			return pstmt.executeQuery();
+		}
+	}
+
+	public int update(String sql,Object... args) throws SQLException {
+		if( args.length == 0 ) {
+			Statement stmt = con.createStatement();
+			int n = stmt.executeUpdate(sql);
+			stmt.close();
+			return n;
+		} else {
+			PreparedStatement pstmt = prepareStatement(sql,args);
+			return pstmt.executeUpdate();
+		}
+	}
+
+	public void close() {
+		try {
+			con.close();
+		} catch(SQLException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	protected void finalize() throws Throwable {
+		if( !con.isClosed() ) {
+			logger.error("not closed");
+			close();
+		}
+		super.finalize();
+	}
+
+}
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/modules/sql/Sql.luan
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/sql/Sql.luan	Mon Nov 12 02:10:41 2018 -0700
@@ -0,0 +1,67 @@
+java()
+local Luan = require "luan:Luan.luan"
+local error = Luan.error
+local new_error = Luan.new_error or error()
+local set_metatable = Luan.set_metatable or error()
+local Database = require "java:luan.modules.sql.Database"
+local Logging = require "luan:logging/Logging.luan"
+local logger = Logging.logger "Sql"
+
+
+local Sql = {}
+
+local mt = {}
+
+function mt.__gc(database)
+	if not database.is_closed then
+		logger.error(database.created)
+		database.close()
+	end
+end
+
+function Sql.database(spec)
+	local database = {}
+	set_metatable(database,mt)
+	local java_database = Database.get(spec)
+	java_database.uses = java_database.uses + 1
+	database.is_closed = false
+	database.java = java_database
+	database.created = new_error "not closed, created:"
+	database.update = java_database.update
+
+	function database.close()
+		if not database.is_closed then
+			database.is_closed = true
+			java_database.uses > 0 or error "java_database.uses <= 0"
+			java_database.uses = java_database.uses - 1
+			if java_database.uses == 0 then
+				java_database.close()
+			end
+		end
+	end
+
+	function database.query(sql,...)
+		local rs = java_database.query(sql,...)
+		local mt = {}
+		function mt.__index(_,key)
+			local rtn = rs.getObject(key)
+			return not rs.wasNull() and rtn or nil
+		end
+		local result = {}
+		set_metatable(result,mt)
+		return function()
+			if rs.isClosed() then
+				return nil
+			end
+			if not rs.next() then
+				rs.close()
+				return nil
+			end
+			return result
+		end
+	end
+
+	return database
+end
+
+return Sql
diff -r 05934fbf635a -r 9fa8b8389578 src/luan/modules/url/LuanUrl.java
--- a/src/luan/modules/url/LuanUrl.java	Sun Sep 30 19:10:48 2018 -0600
+++ b/src/luan/modules/url/LuanUrl.java	Mon Nov 12 02:10:41 2018 -0700
@@ -34,10 +34,10 @@
 	private MultipartClient multipart = null;
 	private int timeout = 0;
 
-	public LuanUrl(LuanState luan,URL url,LuanTable options) throws LuanException {
+	public LuanUrl(URL url,LuanTable options) throws LuanException {
 		this.url = url;
 		if( options != null ) {
-			Map map = options.asMap(luan);
+			Map map = options.asMap();
 			String methodStr = getString(map,"method");
 			if( methodStr != null ) {
 				methodStr = methodStr.toUpperCase();
@@ -47,7 +47,7 @@
 					throw new LuanException( "invalid method: "+methodStr );
 				}
 			}
-			Map headerMap = getMap(luan,map,"headers");
+			Map headerMap = getMap(map,"headers");
 			if( headerMap != null ) {
 				headers = new HashMap();
 				for( Object hack : headerMap.entrySet() ) {
@@ -66,7 +66,7 @@
 					}
 				}
 			}
-			Map auth = getMap(luan,map,"authorization");
+			Map auth = getMap(map,"authorization");
 			if( auth != null ) {
 				if( headers!=null && headers.containsKey("authorization") )
 					throw new LuanException( "can't define authorization with header 'authorization' defined" );
@@ -85,7 +85,7 @@
 					headers = new HashMap();
 				headers.put("authorization",val);
 			}
-			Map params = getMap(luan,map,"parameters");
+			Map params = getMap(map,"parameters");
 			String enctype = getString(map,"enctype");
 			if( enctype != null ) {
 				if( !enctype.equals("multipart/form-data") )
@@ -184,12 +184,12 @@
 		return (LuanTable)val;
 	}
 
-	private static Map getMap(LuanState luan,Map map,String key) throws LuanException {
+	private static Map getMap(Map map,String key) throws LuanException {
 		LuanTable t = getTable(map,key);
-		return t==null ? null : t.asMap(luan);
+		return t==null ? null : t.asMap();
 	}
 
-	@Override public InputStream inputStream() throws IOException, LuanException {
+	@Override public InputStream inputStream(LuanState luan) throws IOException, LuanException {
 		URLConnection con = url.openConnection();
 		if( timeout != 0 ) {
 			con.setConnectTimeout(timeout);
@@ -219,12 +219,12 @@
 		HttpURLConnection httpCon = (HttpURLConnection)con;
 
 		if( method==Method.GET ) {
-			return getInputStream(httpCon);
+			return getInputStream(luan,httpCon);
 		}
 
 		if( method==Method.DELETE ) {
 			httpCon.setRequestMethod("DELETE");
-			return getInputStream(httpCon);
+			return getInputStream(luan,httpCon);
 		}
 
 		// POST
@@ -244,13 +244,13 @@
 		}
 		out.flush();
 		try {
-			return getInputStream(httpCon);
+			return getInputStream(luan,httpCon);
 		} finally {
 			out.close();
 		}
 	}
 
-	private static InputStream getInputStream(HttpURLConnection httpCon) throws IOException, LuanException {
+	private static InputStream getInputStream(LuanState luan,HttpURLConnection httpCon) throws IOException, LuanException {
 		try {
 			return httpCon.getInputStream();
 		} catch(IOException e) {
@@ -263,7 +263,7 @@
 			String msg = Utils.readAll(in);
 			in.close();
 			LuanException le = new LuanException(msg,e);
-			LuanTable tbl = le.table();
+			LuanTable tbl = le.table(luan);
 			tbl.rawPut("response_code",responseCode);
 			tbl.rawPut("response_message",responseMessage);
 			throw le;