changeset 432:d9df6d6cb927

finish fixing LuanTable to use metatables
author Franklin Schmidt <fschmidt@gmail.com>
date Sat, 02 May 2015 23:41:59 -0600
parents 3ffe8ba5b297
children c6bcb8859b93
files core/src/luan/LuanJavaFunction.java core/src/luan/LuanMeta.java core/src/luan/LuanPropertyMeta.java core/src/luan/LuanTable.java core/src/luan/impl/LenExpr.java core/src/luan/modules/BasicLuan.java core/src/luan/modules/HtmlLuan.java core/src/luan/modules/IoLuan.java core/src/luan/modules/PickleCon.java core/src/luan/modules/TableLuan.java lucene/src/luan/modules/lucene/LuceneDocument.java lucene/src/luan/modules/lucene/LuceneWriter.java mail/src/luan/modules/mail/SmtpCon.java
diffstat 13 files changed, 183 insertions(+), 127 deletions(-) [+]
line wrap: on
line diff
diff -r 3ffe8ba5b297 -r d9df6d6cb927 core/src/luan/LuanJavaFunction.java
--- a/core/src/luan/LuanJavaFunction.java	Sat May 02 21:12:48 2015 -0600
+++ b/core/src/luan/LuanJavaFunction.java	Sat May 02 23:41:59 2015 -0600
@@ -140,7 +140,7 @@
 		}
 	}
 
-	private Object[] fixArgs(LuanState luan,Object[] args) {
+	private Object[] fixArgs(LuanState luan,Object[] args) throws LuanException {
 		int n = argConverters.length;
 		Object[] rtn;
 		int start = 0;
@@ -163,7 +163,7 @@
 					Object varArgs = Array.newInstance(varArgCls,len);
 					ArgConverter ac = argConverters[n];
 					for( int i=0; i<len; i++ ) {
-						Array.set( varArgs, i, ac.convert(args[n+i]) );
+						Array.set( varArgs, i, ac.convert(luan,args[n+i]) );
 					}
 					rtn[rtn.length-1] = varArgs;
 				}
@@ -171,7 +171,7 @@
 			System.arraycopy(args,0,rtn,start,Math.min(args.length,n));
 		}
 		for( int i=0; i<n; i++ ) {
-			rtn[start+i] = argConverters[i].convert(rtn[start+i]);
+			rtn[start+i] = argConverters[i].convert(luan,rtn[start+i]);
 		}
 		return rtn;
 	}
@@ -226,11 +226,11 @@
 	}
 
 	private interface ArgConverter {
-		public Object convert(Object obj);
+		public Object convert(LuanState luan,Object obj) throws LuanException;
 	}
 
 	private static final ArgConverter ARG_SAME = new ArgConverter() {
-		public Object convert(Object obj) {
+		public Object convert(LuanState luan,Object obj) {
 			return obj;
 		}
 		@Override public String toString() {
@@ -239,7 +239,7 @@
 	};
 
 	private static final ArgConverter ARG_BOOLEAN = new ArgConverter() {
-		public Object convert(Object obj) {
+		public Object convert(LuanState luan,Object obj) {
 			return Luan.toBoolean(obj);
 		}
 		@Override public String toString() {
@@ -248,7 +248,7 @@
 	};
 
 	private static final ArgConverter ARG_BOOLEAN_OBJ = new ArgConverter() {
-		public Object convert(Object obj) {
+		public Object convert(LuanState luan,Object obj) {
 			return obj==null ? null : Luan.toBoolean(obj);
 		}
 		@Override public String toString() {
@@ -257,7 +257,7 @@
 	};
 
 	private static final ArgConverter ARG_DOUBLE = new ArgConverter() {
-		public Object convert(Object obj) {
+		public Object convert(LuanState luan,Object obj) {
 			if( obj instanceof Double )
 				return obj;
 			if( obj instanceof Number ) {
@@ -278,7 +278,7 @@
 	};
 
 	private static final ArgConverter ARG_FLOAT = new ArgConverter() {
-		public Object convert(Object obj) {
+		public Object convert(LuanState luan,Object obj) {
 			if( obj instanceof Float )
 				return obj;
 			if( obj instanceof Number ) {
@@ -299,7 +299,7 @@
 	};
 
 	private static final ArgConverter ARG_LONG = new ArgConverter() {
-		public Object convert(Object obj) {
+		public Object convert(LuanState luan,Object obj) {
 			if( obj instanceof Long )
 				return obj;
 			if( obj instanceof Number ) {
@@ -322,7 +322,7 @@
 	};
 
 	private static final ArgConverter ARG_INTEGER = new ArgConverter() {
-		public Object convert(Object obj) {
+		public Object convert(LuanState luan,Object obj) {
 			if( obj instanceof Integer )
 				return obj;
 			if( obj instanceof Number ) {
@@ -345,7 +345,7 @@
 	};
 
 	private static final ArgConverter ARG_SHORT = new ArgConverter() {
-		public Object convert(Object obj) {
+		public Object convert(LuanState luan,Object obj) {
 			if( obj instanceof Short )
 				return obj;
 			if( obj instanceof Number ) {
@@ -368,7 +368,7 @@
 	};
 
 	private static final ArgConverter ARG_BYTE = new ArgConverter() {
-		public Object convert(Object obj) {
+		public Object convert(LuanState luan,Object obj) {
 			if( obj instanceof Byte )
 				return obj;
 			if( obj instanceof Number ) {
@@ -391,7 +391,7 @@
 	};
 
 	private static final ArgConverter ARG_TABLE = new ArgConverter() {
-		public Object convert(Object obj) {
+		public Object convert(LuanState luan,Object obj) {
 			if( obj == null )
 				return null;
 			if( obj instanceof List ) {
@@ -402,24 +402,12 @@
 			if( obj instanceof Map ) {
 				@SuppressWarnings("unchecked")
 				Map<Object,Object> map = (Map<Object,Object>)obj;
-				LuanTable tbl = new LuanTable();
-				for( Map.Entry<Object,Object> entry : map.entrySet() ) {
-					Object key = entry.getKey();
-					Object value = entry.getValue();
-					if( key != null && value != null )
-						tbl.rawPut(key,value);
-				}
-				return tbl;
+				return new LuanTable(map);
 			}
 			if( obj instanceof Set ) {
 				@SuppressWarnings("unchecked")
 				Set<Object> set = (Set<Object>)obj;
-				LuanTable tbl = new LuanTable();
-				for( Object el : set ) {
-					if( el != null )
-						tbl.rawPut(el,Boolean.TRUE);
-				}
-				return tbl;
+				return new LuanTable(set);
 			}
 			Class cls = obj.getClass();
 			if( cls.isArray() && !cls.getComponentType().isPrimitive() ) {
@@ -434,10 +422,10 @@
 	};
 
 	private static final ArgConverter ARG_MAP = new ArgConverter() {
-		public Object convert(Object obj) {
+		public Object convert(LuanState luan,Object obj) throws LuanException {
 			if( obj instanceof LuanTable ) {
 				LuanTable t = (LuanTable)obj;
-				return t.asMap();
+				return t.asMap(luan);
 			}
 			return obj;
 		}
@@ -447,7 +435,7 @@
 	};
 
 	private static final ArgConverter ARG_LIST = new ArgConverter() {
-		public Object convert(Object obj) {
+		public Object convert(LuanState luan,Object obj) {
 			if( obj instanceof LuanTable ) {
 				LuanTable t = (LuanTable)obj;
 				if( t.isList() )
@@ -461,11 +449,11 @@
 	};
 
 	private static final ArgConverter ARG_SET = new ArgConverter() {
-		public Object convert(Object obj) {
+		public Object convert(LuanState luan,Object obj) throws LuanException {
 			if( obj instanceof LuanTable ) {
 				LuanTable t = (LuanTable)obj;
-				if( t.isSet() )
-					return t.asSet();
+				if( t.isSet(luan) )
+					return t.asSet(luan);
 			}
 			return obj;
 		}
@@ -475,13 +463,13 @@
 	};
 
 	private static final ArgConverter ARG_COLLECTION = new ArgConverter() {
-		public Object convert(Object obj) {
+		public Object convert(LuanState luan,Object obj) throws LuanException {
 			if( obj instanceof LuanTable ) {
 				LuanTable t = (LuanTable)obj;
 				if( t.isList() )
 					return t.asList();
-				if( t.isSet() )
-					return t.asSet();
+				if( t.isSet(luan) )
+					return t.asSet(luan);
 			}
 			return obj;
 		}
@@ -497,7 +485,7 @@
 			a = (Object[])Array.newInstance(cls.getComponentType(),0);
 		}
 
-		public Object convert(Object obj) {
+		public Object convert(LuanState luan,Object obj) {
 			if( obj instanceof LuanTable ) {
 				LuanTable t = (LuanTable)obj;
 				if( t.isList() ) {
diff -r 3ffe8ba5b297 -r d9df6d6cb927 core/src/luan/LuanMeta.java
--- a/core/src/luan/LuanMeta.java	Sat May 02 21:12:48 2015 -0600
+++ b/core/src/luan/LuanMeta.java	Sat May 02 23:41:59 2015 -0600
@@ -14,7 +14,7 @@
 
 	public LuanFunction __pairs(final LuanState luan,final LuanTable tbl) {
 		return new LuanFunction() {
-			final Iterator<Map.Entry<Object,Object>> iter1 = tbl.iterator();
+			final Iterator<Map.Entry<Object,Object>> iter1 = tbl.rawIterator();
 			final Iterator<Object> iter2 = keys(tbl);
 			final Set<Object> set = new HashSet<Object>();
 
diff -r 3ffe8ba5b297 -r d9df6d6cb927 core/src/luan/LuanPropertyMeta.java
--- a/core/src/luan/LuanPropertyMeta.java	Sat May 02 21:12:48 2015 -0600
+++ b/core/src/luan/LuanPropertyMeta.java	Sat May 02 23:41:59 2015 -0600
@@ -33,7 +33,7 @@
 
 	@Override protected Iterator keys(final LuanTable tbl) {
 		return new Iterator() {
-			final Iterator<Map.Entry<Object,Object>> iter = getters(tbl).iterator();
+			final Iterator<Map.Entry<Object,Object>> iter = getters(tbl).rawIterator();
 
 			@Override public boolean hasNext() {
 				return iter.hasNext();
diff -r 3ffe8ba5b297 -r d9df6d6cb927 core/src/luan/LuanTable.java
--- a/core/src/luan/LuanTable.java	Sat May 02 21:12:48 2015 -0600
+++ b/core/src/luan/LuanTable.java	Sat May 02 23:41:59 2015 -0600
@@ -15,7 +15,7 @@
 import java.util.regex.Pattern;
 
 
-public final class LuanTable implements Iterable<Map.Entry<Object,Object>>, DeepCloneable<LuanTable> {
+public final class LuanTable implements DeepCloneable<LuanTable> {
 	private Map<Object,Object> map = null;
 	private List<Object> list = null;
 	private LuanTable metatable = null;
@@ -24,21 +24,34 @@
 	public LuanTable() {}
 
 	public LuanTable(List<Object> list) {
-		this.list = list;
-		this.map = newMap();
-		map.put("n",list.size());
-		for( int i=0; i<list.size(); i++ ) {
-			if( list.get(i) == null ) {
-				listToMap(i);
-				break;
-			}
+		int n = list.size();
+		for( int i=0; i<n; i++ ) {
+			Object val = list.get(i);
+			if( val != null )
+				rawPut(i+1,val);
+		}
+	}
+
+	public LuanTable(Map<Object,Object> map) {
+		for( Map.Entry<Object,Object> entry : map.entrySet() ) {
+			Object key = entry.getKey();
+			Object value = entry.getValue();
+			if( key != null && value != null )
+				rawPut(key,value);
+		}
+	}
+
+	public LuanTable(Set<Object> set) {
+		for( Object el : set ) {
+			if( el != null )
+				rawPut(el,Boolean.TRUE);
 		}
 	}
 
 	public LuanTable(LuanTable tbl) {
 		if( tbl.map != null && !tbl.map.isEmpty() )
 			this.map = new LinkedHashMap<Object,Object>(tbl.map);
-		if( tbl.length() > 0 )
+		if( tbl.rawLength() > 0 )
 			this.list = new ArrayList<Object>(tbl.list);
 		this.metatable = tbl.metatable;
 	}
@@ -223,13 +236,6 @@
 		mapToList();
 	}
 
-	public void rawAdd(Object value) {
-		if( value==null )
-			throw new IllegalArgumentException("can't add a nil value");
-		list().add(value);
-		mapToList();
-	}
-
 	public Object rawRemove(int pos) {
 		return list().remove(pos-1);
 	}
@@ -238,11 +244,97 @@
 		Collections.sort(list(),cmp);
 	}
 
-	public int length() {
+	public int length(LuanState luan) throws LuanException {
+		Object h = getHandler("__len");
+		if( h != null ) {
+			LuanFunction fn = luan.checkFunction(h);
+			return (Integer)Luan.first(luan.call(fn,"__len",new Object[]{this}));
+		}
+		return rawLength();
+	}
+
+	public int rawLength() {
 		return list==null ? 0 : list.size();
 	}
 
-	@Override public Iterator<Map.Entry<Object,Object>> iterator() {
+	public Iterable<Map.Entry<Object,Object>> iterable(LuanState luan) throws LuanException {
+		final Iterator<Map.Entry<Object,Object>> iter = iterator(luan);
+		return new Iterable<Map.Entry<Object,Object>>() {
+			public Iterator<Map.Entry<Object,Object>> iterator() {
+				return iter;
+			}
+		};
+	}
+
+	public Iterator<Map.Entry<Object,Object>> iterator(final LuanState luan) throws LuanException {
+		if( getHandler("__pairs") == null )
+			return rawIterator();
+		final LuanFunction fn = pairs(luan);
+		return new Iterator<Map.Entry<Object,Object>>() {
+			private Map.Entry<Object,Object> next = getNext();
+
+			private Map.Entry<Object,Object> getNext() {
+				try {
+					Object obj = luan.call(fn);
+					if( obj==null )
+						return null;
+					Object[] a = (Object[])obj;
+					if( a.length == 0 )
+						return null;
+					return new AbstractMap.SimpleEntry<Object,Object>(a[0],a[1]);
+				} catch(LuanException e) {
+					throw new LuanRuntimeException(e);
+				}
+			}
+
+			public boolean hasNext() {
+				return next != null;
+			}
+
+			public Map.Entry<Object,Object> next() {
+				Map.Entry<Object,Object> rtn = next;
+				next = getNext();
+				return rtn;
+			}
+
+			public void remove() {
+				throw new UnsupportedOperationException();
+			}
+		};
+	}
+
+	public LuanFunction pairs(final LuanState luan) throws LuanException {
+		Object h = getHandler("__pairs");
+		if( h != null ) {
+			if( h instanceof LuanFunction ) {
+				Object obj = Luan.first(luan.call((LuanFunction)h,"__pairs",new Object[]{this}));
+				if( !(obj instanceof LuanFunction) )
+					throw luan.exception( "metamethod __pairs should return function but returned " + Luan.type(obj) );
+				return (LuanFunction)obj;
+			}
+			if( h instanceof LuanMeta ) {
+				LuanMeta meta = (LuanMeta)h;
+				return meta.__pairs(luan,this);
+			}
+			throw luan.exception( "invalid type of metamethod __pairs: " + Luan.type(h) );
+		}
+		return rawPairs();
+	}
+
+	private LuanFunction rawPairs() {
+		return new LuanFunction() {
+			final Iterator<Map.Entry<Object,Object>> iter = rawIterator();
+
+			@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 Iterator<Map.Entry<Object,Object>> rawIterator() {
 		if( list == null ) {
 			if( map == null )
 				return Collections.<Map.Entry<Object,Object>>emptyList().iterator();
@@ -253,6 +345,7 @@
 		return new Iterator<Map.Entry<Object,Object>>() {
 			Iterator<Map.Entry<Object,Object>> iter = listIterator();
 			boolean isList = true;
+
 			public boolean hasNext() {
 				boolean b = iter.hasNext();
 				if( !b && isList ) {
@@ -262,9 +355,11 @@
 				}
 				return b;
 			}
+
 			public Map.Entry<Object,Object> next() {
 				return iter.next();
 			}
+
 			public void remove() {
 				throw new UnsupportedOperationException();
 			}
@@ -276,13 +371,16 @@
 			return Collections.<Map.Entry<Object,Object>>emptyList().iterator();
 		final ListIterator iter = list.listIterator();
 		return new Iterator<Map.Entry<Object,Object>>() {
+
 			public boolean hasNext() {
 				return iter.hasNext();
 			}
+
 			public Map.Entry<Object,Object> next() {
-				Double key = Double.valueOf(iter.nextIndex()+1);
+				Integer key = iter.nextIndex()+1;
 				return new AbstractMap.SimpleEntry<Object,Object>(key,iter.next());
 			}
+
 			public void remove() {
 				throw new UnsupportedOperationException();
 			}
@@ -304,8 +402,7 @@
 	}
 
 	public Object getHandler(String op) {
-		LuanTable t = getMetatable();
-		return t==null ? null : t.rawGet(op);
+		return metatable==null ? null : metatable.rawGet(op);
 	}
 
 	public boolean hasJava() {
@@ -323,25 +420,25 @@
 		return new LinkedHashMap<Object,Object>();
 	}
 
-	public boolean isSet() {
-		for( Map.Entry<Object,Object> entry : this ) {
+	public boolean isSet(LuanState luan) throws LuanException {
+		for( Map.Entry<Object,Object> entry : iterable(luan) ) {
 			if( !entry.getValue().equals(Boolean.TRUE) )
 				return false;
 		}
 		return true;
 	}
 
-	public Set<Object> asSet() {
+	public Set<Object> asSet(LuanState luan) throws LuanException {
 		Set<Object> set = new HashSet<Object>();
-		for( Map.Entry<Object,Object> entry : this ) {
+		for( Map.Entry<Object,Object> entry : iterable(luan) ) {
 			set.add(entry.getKey());
 		}
 		return set;
 	}
 
-	public Map<Object,Object> asMap() {
+	public Map<Object,Object> asMap(LuanState luan) throws LuanException {
 		Map<Object,Object> map = newMap();
-		for( Map.Entry<Object,Object> entry : this ) {
+		for( Map.Entry<Object,Object> entry : iterable(luan) ) {
 			map.put(entry.getKey(),entry.getValue());
 		}
 		return map;
diff -r 3ffe8ba5b297 -r d9df6d6cb927 core/src/luan/impl/LenExpr.java
--- a/core/src/luan/impl/LenExpr.java	Sat May 02 21:12:48 2015 -0600
+++ b/core/src/luan/impl/LenExpr.java	Sat May 02 23:41:59 2015 -0600
@@ -28,9 +28,6 @@
 		if( !(o instanceof LuanTable) )
 			throw bit.exception( "attempt to get length of a " + Luan.type(o) + " value" );
 		LuanTable t = (LuanTable)o;
-		LuanFunction fn = bit.getHandlerFunction("__len",t);
-		if( fn != null )
-			return Luan.first(bit.call(fn,"__len",new Object[]{o}));
-		return t.length();
+		return t.length(luan);
 	}
 }
diff -r 3ffe8ba5b297 -r d9df6d6cb927 core/src/luan/modules/BasicLuan.java
--- a/core/src/luan/modules/BasicLuan.java	Sat May 02 21:12:48 2015 -0600
+++ b/core/src/luan/modules/BasicLuan.java	Sat May 02 23:41:59 2015 -0600
@@ -48,32 +48,9 @@
 		return luan.call(fn);
 	}
 
-	public static LuanFunction pairs(LuanState luan,final LuanTable t) throws LuanException {
+	public static LuanFunction pairs(final LuanState luan,final LuanTable t) throws LuanException {
 		Utils.checkNotNull(luan,t);
-		Object obj = t.getHandler("__pairs");
-		if( obj != null ) {
-			if( obj instanceof LuanFunction ) {
-				obj = Luan.first(luan.call((LuanFunction)obj,"__pairs",new Object[]{t}));
-				if( !(obj instanceof LuanFunction) )
-					throw luan.exception( "metamethod __pairs should return function but returned " + Luan.type(obj) );
-				return (LuanFunction)obj;
-			}
-			if( obj instanceof LuanMeta ) {
-				LuanMeta meta = (LuanMeta)obj;
-				return meta.__pairs(luan,t);
-			}
-			throw luan.exception( "invalid type of metamethod __pairs: " + Luan.type(obj) );
-		}
-		return new LuanFunction() {
-			final Iterator<Map.Entry<Object,Object>> iter = t.iterator();
-
-			@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()};
-			}
-		};
+		return t.pairs(luan);
 	}
 
 	public static LuanFunction ipairs(LuanState luan,final LuanTable t) throws LuanException {
@@ -121,7 +98,7 @@
 		}
 		if( v instanceof LuanTable ) {
 			LuanTable t = (LuanTable)v;
-			return t.length();
+			return t.rawLength();
 		}
 		throw luan.exception( "bad argument #1 to 'raw_len' (table or string expected)" );
 	}
diff -r 3ffe8ba5b297 -r d9df6d6cb927 core/src/luan/modules/HtmlLuan.java
--- a/core/src/luan/modules/HtmlLuan.java	Sat May 02 21:12:48 2015 -0600
+++ b/core/src/luan/modules/HtmlLuan.java	Sat May 02 23:41:59 2015 -0600
@@ -310,7 +310,7 @@
 		buf.append('<');
 		buf.append(tbl.get(luan,"name"));
 		LuanTable attributes = (LuanTable)tbl.get(luan,"attributes");
-		for( Map.Entry<Object,Object> attr : attributes ) {
+		for( Map.Entry<Object,Object> attr : attributes.iterable(luan) ) {
 			buf.append( ' ' );
 			buf.append( attr.getKey() );
 			Object val = attr.getValue();
diff -r 3ffe8ba5b297 -r d9df6d6cb927 core/src/luan/modules/IoLuan.java
--- a/core/src/luan/modules/IoLuan.java	Sat May 02 21:12:48 2015 -0600
+++ b/core/src/luan/modules/IoLuan.java	Sat May 02 23:41:59 2015 -0600
@@ -417,7 +417,7 @@
 				return null;
 			LuanTable list = new LuanTable();
 			for( File f : files ) {
-				list.rawAdd(new LuanFile(luan,f).table());
+				list.rawPut(list.rawLength()+1,new LuanFile(luan,f).table());
 			}
 			return list;
 		}
diff -r 3ffe8ba5b297 -r d9df6d6cb927 core/src/luan/modules/PickleCon.java
--- a/core/src/luan/modules/PickleCon.java	Sat May 02 21:12:48 2015 -0600
+++ b/core/src/luan/modules/PickleCon.java	Sat May 02 23:41:59 2015 -0600
@@ -96,7 +96,7 @@
 		}
 		StringBuilder sb = new StringBuilder();
 		sb.append( "{" );
-		for( Map.Entry<Object,Object> entry : tbl ) {
+		for( Map.Entry<Object,Object> entry : tbl.iterable(luan) ) {
 			sb.append( "[" );
 			sb.append( pickle(entry.getKey(),set) );
 			sb.append( "]=" );
diff -r 3ffe8ba5b297 -r d9df6d6cb927 core/src/luan/modules/TableLuan.java
--- a/core/src/luan/modules/TableLuan.java	Sat May 02 21:12:48 2015 -0600
+++ b/core/src/luan/modules/TableLuan.java	Sat May 02 23:41:59 2015 -0600
@@ -18,10 +18,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() : j;
+		int last = j==null ? list.length(luan) : j;
 		StringBuilder buf = new StringBuilder();
 		for( int k=first; k<=last; k++ ) {
-			Object val = list.rawGet(k);
+			Object val = list.get(luan,k);
 			if( val==null )
 				break;
 			if( sep!=null && k > first )
@@ -34,11 +34,15 @@
 		return buf.toString();
 	}
 
-	public static void insert(LuanTable list,int pos,Object value){
+	public static void insert(LuanState luan,LuanTable list,int pos,Object value) throws LuanException {
+		if( list.getMetatable() != null )
+			throw luan.exception("can't insert into a table with a metatable");
 		list.rawInsert(pos,value);
 	}
 
-	public static Object remove(LuanTable list,int pos) {
+	public static Object remove(LuanState luan,LuanTable list,int pos) throws LuanException {
+		if( list.getMetatable() != null )
+			throw luan.exception("can't remove from a table with a metatable");
 		return list.rawRemove(pos);
 	}
 
@@ -47,6 +51,8 @@
 	}
 
 	public static void sort(final LuanState luan,LuanTable list,final LuanFunction comp) throws LuanException {
+		if( list.getMetatable() != null )
+			throw luan.exception("can't sort a table with a metatable");
 		final LessThan lt;
 		if( comp==null ) {
 			lt = new LessThan() {
@@ -81,33 +87,24 @@
 	}
 
 	public static LuanTable pack(Object... args) {
-		LuanTable tbl = new LuanTable();
-		boolean hasNull = false;
-		for( int i=0; i<args.length; i++ ) {
-			Object v = args[i];
-			if( v==null ) {
-				hasNull = true;
-			} else if( !hasNull ) {
-				tbl.rawAdd(v);
-			} else {
-				tbl.rawPut(i+1,v);
-			}
-		}
+		LuanTable tbl = new LuanTable(Arrays.asList(args));
 		tbl.rawPut( "n", args.length );
 		return tbl;
 	}
 
-	@LuanMethod public static Object[] unpack(LuanTable tbl,Integer iFrom,Integer iTo) {
+	@LuanMethod public static Object[] unpack(LuanState luan,LuanTable tbl,Integer iFrom,Integer iTo) throws LuanException {
 		int from = iFrom!=null ? iFrom : 1;
-		int to = iTo!=null ? iTo : tbl.length();
+		int to = iTo!=null ? iTo : tbl.length(luan);
 		List<Object> list = new ArrayList<Object>();
 		for( int i=from; i<=to; i++ ) {
-			list.add( tbl.rawGet(i) );
+			list.add( tbl.get(luan,i) );
 		}
 		return list.toArray();
 	}
 
-	public static LuanTable sub_list(LuanTable list,int from,int to) {
+	public static LuanTable sub_list(LuanState luan,LuanTable list,int from,int to) throws LuanException {
+		if( list.getMetatable() != null )
+			throw luan.exception("can't sub_list a table with a metatable");
 		return list.rawSubList(from,to);
 	}
 
diff -r 3ffe8ba5b297 -r d9df6d6cb927 lucene/src/luan/modules/lucene/LuceneDocument.java
--- a/lucene/src/luan/modules/lucene/LuceneDocument.java	Sat May 02 21:12:48 2015 -0600
+++ b/lucene/src/luan/modules/lucene/LuceneDocument.java	Sat May 02 23:41:59 2015 -0600
@@ -28,7 +28,7 @@
 
 	static Document toLucene(LuanState luan,LuanTable table,Map<String,String> nameMap) throws LuanException {
 		Document doc = new Document();
-		for( Map.Entry<Object,Object> entry : table ) {
+		for( Map.Entry<Object,Object> entry : table.iterable(luan) ) {
 			Object key = entry.getKey();
 			if( !(key instanceof String) )
 				throw luan.exception("key must be string");
diff -r 3ffe8ba5b297 -r d9df6d6cb927 lucene/src/luan/modules/lucene/LuceneWriter.java
--- a/lucene/src/luan/modules/lucene/LuceneWriter.java	Sat May 02 21:12:48 2015 -0600
+++ b/lucene/src/luan/modules/lucene/LuceneWriter.java	Sat May 02 23:41:59 2015 -0600
@@ -41,7 +41,7 @@
 
 	public void delete_documents(LuanState luan,LuanTable tblTerms) throws LuanException, IOException {
 		List<Term> list = new ArrayList<Term>();
-		for( Map.Entry<Object,Object> entry : tblTerms ) {
+		for( Map.Entry<Object,Object> entry : tblTerms.iterable(luan) ) {
 			Object key = entry.getKey();
 			Object value = entry.getValue();
 			if( !(key instanceof String) )
diff -r 3ffe8ba5b297 -r d9df6d6cb927 mail/src/luan/modules/mail/SmtpCon.java
--- a/mail/src/luan/modules/mail/SmtpCon.java	Sat May 02 21:12:48 2015 -0600
+++ b/mail/src/luan/modules/mail/SmtpCon.java	Sat May 02 23:41:59 2015 -0600
@@ -25,7 +25,7 @@
 	private final Session session;
 
 	public SmtpCon(LuanState luan,LuanTable paramsTbl) throws LuanException {
-		Map<Object,Object> params = new HashMap<Object,Object>(paramsTbl.asMap());
+		Map<Object,Object> params = new HashMap<Object,Object>(paramsTbl.asMap(luan));
 		Properties props = new Properties(System.getProperties());
 
 		String host = getString(luan,params,"host");
@@ -81,7 +81,7 @@
 
 	public void send(LuanState luan,LuanTable mailTbl) throws LuanException {
 		try {
-			Map<Object,Object> mailParams = new HashMap<Object,Object>(mailTbl.asMap());
+			Map<Object,Object> mailParams = new HashMap<Object,Object>(mailTbl.asMap(luan));
 			MimeMessage msg = new MimeMessage(session);
 
 			String from = getString(luan,mailParams,"from");
@@ -109,7 +109,7 @@
 					bodyPart.setText((String)body);
 				} else if( body instanceof LuanTable ) {
 					LuanTable bodyTbl = (LuanTable)body;
-					Map<Object,Object> map = new HashMap<Object,Object>(bodyTbl.asMap());
+					Map<Object,Object> map = new HashMap<Object,Object>(bodyTbl.asMap(luan));
 					MimeMultipart mp = new MimeMultipart("alternative");
 					String text = (String)map.remove("text");
 					if( text != null ) {
@@ -142,7 +142,7 @@
 				for( Object attachment : attachmentsTbl.asList() ) {
 					if( !(attachment instanceof LuanTable) )
 						throw luan.exception( "each attachment must be a table" );
-					Map<Object,Object> attachmentMap = new HashMap<Object,Object>(((LuanTable)attachment).asMap());
+					Map<Object,Object> attachmentMap = new HashMap<Object,Object>(((LuanTable)attachment).asMap(luan));
 					Object obj;
 
 					obj = attachmentMap.remove("filename");