changeset 195:24ede40ee0aa

make MetatableGetter DeepCloneable, scoped, and secure git-svn-id: https://luan-java.googlecode.com/svn/trunk@196 21e917c8-12df-6dd8-5cb6-c86387c605b9
author fschmidt@gmail.com <fschmidt@gmail.com@21e917c8-12df-6dd8-5cb6-c86387c605b9>
date Thu, 03 Jul 2014 08:19:48 +0000
parents 08df375e2e5f
children be0275bda373
files core/src/luan/DeepCloner.java core/src/luan/LuanState.java core/src/luan/LuanTable.java core/src/luan/MetatableGetter.java core/src/luan/impl/Closure.java core/src/luan/impl/ConstExpr.java core/src/luan/impl/FnDef.java core/src/luan/impl/LuanCompiler.java core/src/luan/impl/LuanParser.java core/src/luan/impl/LuanStateImpl.java core/src/luan/impl/MtGetterLink.java core/src/luan/modules/JavaLuan.java core/src/luan/modules/PackageLuan.java core/src/luan/modules/StringLuan.java web/src/luan/modules/web/Web_server.luan
diffstat 15 files changed, 207 insertions(+), 82 deletions(-) [+]
line wrap: on
line diff
--- a/core/src/luan/DeepCloner.java	Wed Jul 02 04:52:25 2014 +0000
+++ b/core/src/luan/DeepCloner.java	Thu Jul 03 08:19:48 2014 +0000
@@ -8,6 +8,8 @@
 	private final Map<Object,Object> cloned = new IdentityHashMap<Object,Object>();
 
 	public <T extends DeepCloneable<T>> T deepClone(T obj) {
+		if( obj==null )
+			return null;
 		@SuppressWarnings("unchecked")
 		T rtn = (T)cloned.get(obj);
 		if( rtn == null ) {
--- a/core/src/luan/LuanState.java	Wed Jul 02 04:52:25 2014 +0000
+++ b/core/src/luan/LuanState.java	Thu Jul 03 08:19:48 2014 +0000
@@ -57,7 +57,6 @@
 	private LuanTable preload;
 	private LuanTable searchers;
 
-	private final List<MetatableGetter> mtGetters;
 	final List<StackTraceElement> stackTrace = new ArrayList<StackTraceElement>();
 
 	protected LuanState() {
@@ -66,11 +65,6 @@
 		loaded = new LuanTable();
 		preload = new LuanTable();
 		searchers = new LuanTable();
-		mtGetters = new ArrayList<MetatableGetter>();
-	}
-
-	protected LuanState(LuanState luan) {
-		mtGetters = new ArrayList<MetatableGetter>(luan.mtGetters);
 	}
 
 	@Override public void deepenClone(LuanState clone,DeepCloner cloner) {
@@ -146,21 +140,12 @@
 	}
 
 	public final LuanTable getMetatable(Object obj) {
-		if( obj instanceof LuanTable ) {
-			LuanTable table = (LuanTable)obj;
-			return table.getMetatable();
-		}
-		for( MetatableGetter mg : mtGetters ) {
-			LuanTable table = mg.getMetatable(obj);
-			if( table != null )
-				return table;
-		}
-		return null;
+		return getMetatable(obj,null);
 	}
 
-	public final void addMetatableGetter(MetatableGetter mg) {
-		mtGetters.add(mg);
-	}
+	public abstract LuanTable getMetatable(Object obj,MetatableGetter beforeThis);
+
+	public abstract void addMetatableGetter(MetatableGetter mg);
 
 	public final LuanBit bit(LuanElement el) {
 		return new LuanBit(this,el);
--- a/core/src/luan/LuanTable.java	Wed Jul 02 04:52:25 2014 +0000
+++ b/core/src/luan/LuanTable.java	Thu Jul 03 08:19:48 2014 +0000
@@ -18,6 +18,7 @@
 	private Map<Object,Object> map = null;
 	private List<Object> list = null;
 	private LuanTable metatable = null;
+	public MetatableGetter metatableGetter = null;  // used for modules
 
 	public LuanTable() {}
 
@@ -62,6 +63,7 @@
 	}
 
 	@Override public void deepenClone(LuanTable clone,DeepCloner cloner) {
+		clone.metatableGetter = this.metatableGetter;
 		if( map != null ) {
 			clone.map = new HashMap<Object,Object>();
 			for( Map.Entry<Object,Object> entry : map.entrySet() ) {
--- a/core/src/luan/MetatableGetter.java	Wed Jul 02 04:52:25 2014 +0000
+++ b/core/src/luan/MetatableGetter.java	Thu Jul 03 08:19:48 2014 +0000
@@ -1,5 +1,5 @@
 package luan;
 
-public interface MetatableGetter {
+public interface MetatableGetter extends DeepCloneable<MetatableGetter> {
 	public LuanTable getMetatable(Object obj);
 }
--- a/core/src/luan/impl/Closure.java	Wed Jul 02 04:52:25 2014 +0000
+++ b/core/src/luan/impl/Closure.java	Thu Jul 03 08:19:48 2014 +0000
@@ -11,12 +11,14 @@
 
 final class Closure extends LuanFunction implements DeepCloneable<Closure> {
 	private final FnDef fnDef;
+	private MtGetterLink mtGetterLink;
 	private UpValue[] upValues;
 
-	Closure(LuanStateImpl luan,FnDef fnDef) throws LuanException {
+	Closure(LuanStateImpl luan,FnDef fnDef,MtGetterLink mtGetterLink) throws LuanException {
 		this.fnDef = fnDef;
+		this.mtGetterLink = mtGetterLink;
 		UpValue.Getter[] upValueGetters = fnDef.upValueGetters;
-		upValues = new UpValue[upValueGetters.length];
+		this.upValues = new UpValue[upValueGetters.length];
 		for( int i=0; i<upValues.length; i++ ) {
 			upValues[i] = upValueGetters[i].get(luan);
 		}
@@ -31,9 +33,14 @@
 	}
 
 	@Override public void deepenClone(Closure clone,DeepCloner cloner) {
+		clone.mtGetterLink = cloner.deepClone(mtGetterLink);
 		clone.upValues = cloner.deepClone(upValues);
 	}
 
+	MtGetterLink mtGetterLink() {
+		return mtGetterLink;
+	}
+
 	UpValue[] upValues() {
 		return upValues;
 	}
--- a/core/src/luan/impl/ConstExpr.java	Wed Jul 02 04:52:25 2014 +0000
+++ b/core/src/luan/impl/ConstExpr.java	Thu Jul 03 08:19:48 2014 +0000
@@ -6,10 +6,6 @@
 final class ConstExpr extends CodeImpl implements Expr {
 	private final Object obj;
 
-	ConstExpr(Object obj) {
-		this(null,obj);
-	}
-
 	ConstExpr(LuanSource.Element se,Object obj) {
 		super(se);
 		this.obj = obj;
--- a/core/src/luan/impl/FnDef.java	Wed Jul 02 04:52:25 2014 +0000
+++ b/core/src/luan/impl/FnDef.java	Thu Jul 03 08:19:48 2014 +0000
@@ -36,7 +36,7 @@
 	}
 
 	@Override public Object eval(LuanStateImpl luan) throws LuanException {
-		return new Closure(luan,this);
+		return new Closure(luan,this,luan.mtGetterLink());
 	}
 
 }
--- a/core/src/luan/impl/LuanCompiler.java	Wed Jul 02 04:52:25 2014 +0000
+++ b/core/src/luan/impl/LuanCompiler.java	Thu Jul 03 08:19:48 2014 +0000
@@ -24,17 +24,28 @@
 				parser.addVar( (String)key, entry.getValue() );
 		}
 		FnDef fnDef = parse(luan,parser,allowExpr);
-		if( passedEnv )
-			return new Closure((LuanStateImpl)luan,fnDef);
-		final Closure c = new Closure((LuanStateImpl)luan,fnDef);
-		return new LuanFunction() {
-			@Override public Object call(LuanState luan,Object[] args) throws LuanException {
-				Object rtn = c.call(luan,args);
-				if( rtn instanceof Object[] && ((Object[])rtn).length==0 )
-					rtn = c.upValues()[0].get();
-				return rtn;
-			}
-		};
+		final LuanStateImpl luanImpl = (LuanStateImpl)luan;
+		MtGetterLink mtGetterLink = (MtGetterLink)env.get("_MTG");
+		final Closure c = new Closure(luanImpl,fnDef,mtGetterLink);
+		final LuanTable ENV = env;
+		if( passedEnv ) {
+			return new LuanFunction() {
+				@Override public Object call(LuanState luan,Object[] args) throws LuanException {
+					Object rtn = c.call(luan,args);
+					ENV.put("_MTG",luanImpl.mtGetterLink);
+					return rtn;
+				}
+			};
+		} else {
+			return new LuanFunction() {
+				@Override public Object call(LuanState luan,Object[] args) throws LuanException {
+					Object rtn = c.call(luan,args);
+					if( rtn instanceof Object[] && ((Object[])rtn).length==0 )
+						rtn = ENV;
+					return rtn;
+				}
+			};
+		}
 	}
 
 	private static FnDef parse(LuanState luan,LuanParser parser,boolean allowExpr) throws LuanException {
--- a/core/src/luan/impl/LuanParser.java	Wed Jul 02 04:52:25 2014 +0000
+++ b/core/src/luan/impl/LuanParser.java	Thu Jul 03 08:19:48 2014 +0000
@@ -272,8 +272,10 @@
 		if( exp == null )
 			return null;
 		Expr fnExp = (Expr)nameVar(start,"Io").expr();
-		fnExp = new IndexExpr( se(start,"stdout"), fnExp, new ConstExpr("stdout") );
-		fnExp = new IndexExpr( se(start,"write"), fnExp, new ConstExpr("write") );
+		LuanSource.Element se = se(start,"stdout");
+		fnExp = new IndexExpr( se, fnExp, new ConstExpr(se,"stdout") );
+		se = se(start,"write");
+		fnExp = new IndexExpr( se, fnExp, new ConstExpr(se,"write") );
 		FnCall fnCall = new FnCall( se(start), fnExp, exp );
 		return new ExpressionsStmt(fnCall);
 	}
@@ -304,7 +306,7 @@
 						throw parser.exception("Unclosed template expression");
 				} while( !parser.test( "<%" ) );
 				String match = parser.textFrom(i);
-				builder.add( new ConstExpr(match) );
+				builder.add( new ConstExpr(se(i),match) );
 			}
 		}
 	}
@@ -360,7 +362,7 @@
 		if( !isValidName(varName) )
 			throw parser.exception("invalid variable name '"+varName+"' in import");
 		LuanSource.Element se = se(start);
-		FnCall require = new FnCall( se, new ConstExpr(se,PackageLuan.requireFn), new ConstExpr(modName) );
+		FnCall require = new FnCall( se, new ConstExpr(se,PackageLuan.requireFn), new ConstExpr(se(start,modName),modName) );
 		Settable settable;
 		if( interactive ) {
 			settable = nameVar(se,varName).settable();
@@ -974,7 +976,7 @@
 				index = upValueIndex(name);
 				if( index != -1 )
 					return new GetUpVar(se,index);
-				return new IndexExpr( se, env(), new ConstExpr(name) );
+				return new IndexExpr( se, env(), new ConstExpr(se,name) );
 			}
 
 			public Settable settable() {
@@ -984,7 +986,7 @@
 				index = upValueIndex(name);
 				if( index != -1 )
 					return new SetUpVar(index);
-				return new SetTableEntry( se, env(), new ConstExpr(name) );
+				return new SetTableEntry( se, env(), new ConstExpr(se,name) );
 			}
 		};
 	}
@@ -1023,6 +1025,7 @@
 	}
 
 	private boolean args(In in,List<Expressions> builder) throws ParseException {
+		int start = parser.begin();
 		if( parser.match('(') ) {
 			In inParens = in.parens();
 			Spaces(inParens);
@@ -1030,26 +1033,26 @@
 			if( !parser.match(')') )
 				throw parser.exception("Expression or ')' expected");
 			Spaces(in);
-			return true;
+			return parser.success();
 		}
 		Expr exp = TableExpr(in);
 		if( exp != null ) {
 			builder.add(exp);
-			return true;
+			return parser.success();
 		}
 		String s = StringLiteral(in);
 		if( s != null ) {
-			builder.add( new ConstExpr(s) );
-			return true;
+			builder.add( new ConstExpr(se(start),s) );
+			return parser.success();
 		}
 /*
 		Expressions exps = TemplateExpressions(in);
 		if( exps != null ) {
 			builder.add(exps);
-			return true;
+			return parser.success();
 		}
 */
-		return false;
+		return parser.failure();
 	}
 
 	private Expressions ExpList(In in) throws ParseException {
@@ -1093,8 +1096,11 @@
 	}
 
 	private Expr NameExpr(In in) throws ParseException {
+		int start = parser.begin();
 		String name = Name(in);
-		return name==null ? null : new ConstExpr(name);
+		if( name==null )
+			return parser.failure(null);
+		return parser.success(new ConstExpr(se(start),name));
 	}
 
 	private String RequiredName(In in) throws ParseException {
@@ -1177,18 +1183,19 @@
 	));
 
 	private Expr Literal(In in) throws ParseException {
+		int start = parser.begin();
 		if( NilLiteral(in) )
-			return new ConstExpr(null);
+			return parser.success(new ConstExpr(se(start),null));
 		Boolean b = BooleanLiteral(in);
 		if( b != null )
-			return new ConstExpr(b);
+			return parser.success(new ConstExpr(se(start),b));
 		Number n = NumberLiteral(in);
 		if( n != null )
-			return new ConstExpr(n);
+			return parser.success(new ConstExpr(se(start),n));
 		String s = StringLiteral(in);
 		if( s != null )
-			return new ConstExpr(s);
-		return null;
+			return parser.success(new ConstExpr(se(start),s));
+		return parser.failure(null);
 	}
 
 	private boolean NilLiteral(In in) throws ParseException {
--- a/core/src/luan/impl/LuanStateImpl.java	Wed Jul 02 04:52:25 2014 +0000
+++ b/core/src/luan/impl/LuanStateImpl.java	Thu Jul 03 08:19:48 2014 +0000
@@ -21,6 +21,7 @@
 		final Closure closure;
 		final Object[] stack;
 		final Object[] varArgs;
+		MtGetterLink mtGetterLink;
 		UpValue[] downValues = null;
 
 		Frame( Frame previousFrame, Closure closure, int stackSize, Object[] varArgs) {
@@ -28,6 +29,7 @@
 			this.closure = closure;
 			this.stack = new Object[stackSize];
 			this.varArgs = varArgs;
+			this.mtGetterLink = closure.mtGetterLink();
 		}
 
 		void stackClear(int start,int end) {
@@ -52,22 +54,24 @@
 				downValues[index] = new UpValue(stack,index);
 			return downValues[index];
 		}
+
+		void addMetatableGetter(MetatableGetter mg) {
+			if( mtGetterLink==null || !mtGetterLink.contains(mg) )
+				mtGetterLink = new MtGetterLink(mg,mtGetterLink);
+		}
 	}
 
 	private Frame frame = null;
 	Object returnValues;
 	Closure tailFn;
+	MtGetterLink mtGetterLink = null;
 
 	LuanStateImpl() {}
 
-	private LuanStateImpl(LuanStateImpl luan) {
-		super(luan);
-	}
-
 	@Override public LuanState shallowClone() {
 //		if( frame != null )
 //			throw new IllegalStateException("frame isn't null");
-		return new LuanStateImpl(this);
+		return new LuanStateImpl();
 	}
 
 	// returns stack
@@ -81,6 +85,7 @@
 	void popFrame() {
 		returnValues = LuanFunction.NOTHING;
 		tailFn = null;
+		mtGetterLink = frame.mtGetterLink;
 		frame = frame.previousFrame;
 	}
 
@@ -114,4 +119,21 @@
 		return (LuanTable)frame.closure.upValues()[0].get();
 	}
 
+	MtGetterLink mtGetterLink() {
+		return frame==null ? null : frame.mtGetterLink;
+	}
+
+	@Override public LuanTable getMetatable(Object obj,MetatableGetter beforeThis) {
+		if( obj instanceof LuanTable ) {
+			LuanTable table = (LuanTable)obj;
+			return table.getMetatable();
+		}
+		MtGetterLink mtGetterLink = mtGetterLink();
+		return mtGetterLink==null ? null : mtGetterLink.getMetatable(obj,beforeThis);
+	}
+
+	@Override public void addMetatableGetter(MetatableGetter mg) {
+		frame.addMetatableGetter(mg);
+	}
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/impl/MtGetterLink.java	Thu Jul 03 08:19:48 2014 +0000
@@ -0,0 +1,44 @@
+package luan.impl;
+
+import luan.MetatableGetter;
+import luan.LuanTable;
+import luan.DeepCloneable;
+import luan.DeepCloner;
+
+
+final class MtGetterLink implements DeepCloneable<MtGetterLink> {
+	private MetatableGetter mg;
+	private MtGetterLink next;
+
+	private MtGetterLink() {}
+
+	MtGetterLink(MetatableGetter mg,MtGetterLink next) {
+		this.mg = mg;
+		this.next = next;
+	}
+
+	LuanTable getMetatable(Object obj,MetatableGetter beforeThis) {
+		if( beforeThis != null ) {
+			if( beforeThis==mg )
+				beforeThis = null;
+		} else {
+			LuanTable mt = mg.getMetatable(obj);
+			if( mt != null )
+				return mt;
+		}
+		return next==null ? null : next.getMetatable(obj,beforeThis);
+	}
+
+	boolean contains(MetatableGetter mg) {
+		return this.mg==mg || next!=null && next.contains(mg);
+	}
+
+	@Override public MtGetterLink shallowClone() {
+		return new MtGetterLink();
+	}
+
+	@Override public void deepenClone(MtGetterLink clone,DeepCloner cloner) {
+		clone.mg = cloner.deepClone(mg);
+		clone.next = cloner.deepClone(next);
+	}
+}
--- a/core/src/luan/modules/JavaLuan.java	Wed Jul 02 04:52:25 2014 +0000
+++ b/core/src/luan/modules/JavaLuan.java	Thu Jul 03 08:19:48 2014 +0000
@@ -24,14 +24,15 @@
 import luan.LuanFunction;
 import luan.LuanJavaFunction;
 import luan.LuanElement;
+import luan.DeepCloner;
 
 
 public final class JavaLuan {
 
 	public static final LuanFunction LOADER = new LuanFunction() {
 		@Override public Object call(LuanState luan,Object[] args) {
-			luan.addMetatableGetter(mg);
 			LuanTable module = new LuanTable();
+			module.metatableGetter = mg;
 			try {
 				module.put( "class", new LuanJavaFunction(JavaLuan.class.getMethod("getClass",LuanState.class,String.class),null) );
 				add( module, "proxy", LuanState.class, Static.class, LuanTable.class, Object.class );
@@ -73,14 +74,30 @@
 	}
 
 	private static final MetatableGetter mg = new MetatableGetter() {
+
 		public LuanTable getMetatable(Object obj) {
 			if( obj==null )
 				return null;
 			return mt;
 		}
+
+		@Override public MetatableGetter shallowClone() {
+			return this;
+		}
+		@Override public void deepenClone(MetatableGetter clone,DeepCloner cloner) {}
 	};
 
 	public static Object __index(LuanState luan,Object obj,Object key) throws LuanException {
+		LuanTable mt = luan.getMetatable(obj,mg);
+		if( mt != null ) {
+			Object h = mt.get("__index");
+			if( h instanceof LuanFunction ) {
+				LuanFunction fn = (LuanFunction)h;
+				Object rtn = Luan.first(luan.call(fn,new Object[]{obj,key}));
+				if( rtn != null )
+					return rtn;
+			}
+		}
 		if( obj instanceof Static ) {
 			if( key instanceof String ) {
 				String name = (String)key;
--- a/core/src/luan/modules/PackageLuan.java	Wed Jul 02 04:52:25 2014 +0000
+++ b/core/src/luan/modules/PackageLuan.java	Thu Jul 03 08:19:48 2014 +0000
@@ -82,6 +82,11 @@
 				}
 			}
 		}
+		if( mod instanceof LuanTable ) {
+			LuanTable modTbl = (LuanTable)mod;
+			if( modTbl.metatableGetter != null )
+				luan.addMetatableGetter(modTbl.metatableGetter);
+		}
 		return mod;
 	}
 
--- a/core/src/luan/modules/StringLuan.java	Wed Jul 02 04:52:25 2014 +0000
+++ b/core/src/luan/modules/StringLuan.java	Thu Jul 03 08:19:48 2014 +0000
@@ -10,14 +10,15 @@
 import luan.LuanElement;
 import luan.LuanException;
 import luan.MetatableGetter;
+import luan.DeepCloner;
 
 
 public final class StringLuan {
 
 	public static final LuanFunction LOADER = new LuanFunction() {
 		@Override public Object call(LuanState luan,Object[] args) {
-			luan.addMetatableGetter(mg);
 			LuanTable module = new LuanTable();
+			module.metatableGetter = new MyMetatableGetter(module);
 			try {
 				add( module, "to_binary", String.class );
 				add( module, "to_integers", String.class );
@@ -44,25 +45,23 @@
 		t.put( method, new LuanJavaFunction(StringLuan.class.getMethod(method,parameterTypes),null) );
 	}
 
-	private static final LuanTable mt = new LuanTable();
-	static {
-		try {
-			add( mt, "__index", LuanState.class, String.class, Object.class );
-		} catch(NoSuchMethodException e) {
-			throw new RuntimeException(e);
-		}
-	}
+	public static class MyMetatableGetter implements MetatableGetter {
+		private LuanTable module;
+		private LuanTable metatable;
+
+		private MyMetatableGetter() {}
 
-	private static final MetatableGetter mg = new MetatableGetter() {
-		public LuanTable getMetatable(Object obj) {
-			return obj instanceof String ? mt : null;
+		MyMetatableGetter(LuanTable module) {
+			this.module = module;
+			this.metatable = table();
 		}
-	};
 
-	public static Object __index(LuanState luan,final String s,Object key) throws LuanException {
-		LuanTable mod = (LuanTable)luan.loaded().get("String");
-		if( mod!=null ) {
-			Object obj = mod.get(key);
+		@Override public LuanTable getMetatable(Object obj) {
+			return obj instanceof String ? metatable : null;
+		}
+
+		public Object __index(LuanState luan,final String s,Object key) throws LuanException {
+			Object obj = module.get(key);
 			if( obj instanceof LuanFunction ) {
 				final LuanFunction fn = (LuanFunction)obj;
 				return new LuanFunction() {
@@ -74,10 +73,37 @@
 					}
 				};
 			}
+			LuanTable mt = luan.getMetatable(s,this);
+			if( mt == null )
+				return null;
+			Object h = mt.get("__index");
+			if( !(h instanceof LuanFunction) )
+				return null;
+			LuanFunction fn = (LuanFunction)h;
+			return luan.call(fn,new Object[]{s,key});
 		}
-		if( luan.loaded().get("Java") != null )
-			return JavaLuan.__index(luan,s,key);
-		return null;
+
+		LuanTable table() {
+			LuanTable tbl = new LuanTable();
+			try {
+				tbl.put( "__index", new LuanJavaFunction(
+					MyMetatableGetter.class.getMethod( "__index", LuanState.class, String.class, Object.class ), this
+				) );
+			} catch(NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			}
+			return tbl;
+		}
+
+		@Override public MetatableGetter shallowClone() {
+			return new MyMetatableGetter();
+		}
+
+		@Override public void deepenClone(MetatableGetter c,DeepCloner cloner) {
+			MyMetatableGetter clone = (MyMetatableGetter)c;
+			clone.module = cloner.deepClone(module);
+			clone.metatable = clone.table();
+		}
 	}
 
 	static int start(String s,int i) {
--- a/web/src/luan/modules/web/Web_server.luan	Wed Jul 02 04:52:25 2014 +0000
+++ b/web/src/luan/modules/web/Web_server.luan	Thu Jul 03 08:19:48 2014 +0000
@@ -1,4 +1,5 @@
 import "Java"
+import "String"
 import "Io"
 import "Package"
 import "web/Http"