changeset 77:4bf3d0c0b6b9

make LuanState cloneable git-svn-id: https://luan-java.googlecode.com/svn/trunk@78 21e917c8-12df-6dd8-5cb6-c86387c605b9
author fschmidt@gmail.com <fschmidt@gmail.com@21e917c8-12df-6dd8-5cb6-c86387c605b9>
date Fri, 15 Feb 2013 09:55:17 +0000
parents 97b03fc807ad
children 7c08b611125d
files src/luan/DeepCloneable.java src/luan/DeepCloner.java src/luan/LuanState.java src/luan/LuanTable.java src/luan/interp/Closure.java src/luan/interp/LuanStateImpl.java src/luan/interp/UpValue.java src/luan/lib/BasicLib.java src/luan/lib/HtmlLib.java src/luan/lib/HttpLib.java src/luan/lib/JavaLib.java src/luan/lib/MathLib.java src/luan/lib/PackageLib.java src/luan/lib/StringLib.java src/luan/lib/TableLib.java src/luan/tools/CmdLine.java src/luan/tools/WebRun.java src/luan/tools/WebServlet.java src/luan/tools/WebShell.java
diffstat 19 files changed, 197 insertions(+), 48 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/DeepCloneable.java	Fri Feb 15 09:55:17 2013 +0000
@@ -0,0 +1,7 @@
+package luan;
+
+
+public interface DeepCloneable<T extends DeepCloneable> {
+	public T shallowClone();
+	public void deepenClone(T clone,DeepCloner cloner);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/DeepCloner.java	Fri Feb 15 09:55:17 2013 +0000
@@ -0,0 +1,34 @@
+package luan;
+
+import java.util.Map;
+import java.util.IdentityHashMap;
+
+
+public final class DeepCloner {
+	private final Map<Object,Object> cloned = new IdentityHashMap<Object,Object>();
+
+	public <T extends DeepCloneable<T>> T deepClone(T obj) {
+		@SuppressWarnings("unchecked")
+		T rtn = (T)cloned.get(obj);
+		if( rtn == null ) {
+			rtn = obj.shallowClone();
+			cloned.put(obj,rtn);
+			obj.deepenClone(rtn,this);
+		}
+		return rtn;
+	}
+
+	public void deepenClone(Object[] a) {
+		for( int i=0; i<a.length; i++ ) {
+			a[i] = get(a[i]);
+		}
+	}
+
+	public Object get(Object obj) {
+		if( !(obj instanceof DeepCloneable) )
+			return obj;
+		@SuppressWarnings("unchecked")
+		DeepCloneable dc = deepClone((DeepCloneable)obj);
+		return dc;
+	}
+}
--- a/src/luan/LuanState.java	Fri Feb 15 04:52:16 2013 +0000
+++ b/src/luan/LuanState.java	Fri Feb 15 09:55:17 2013 +0000
@@ -14,21 +14,53 @@
 import luan.lib.HtmlLib;
 
 
-public abstract class LuanState {
+public abstract class LuanState implements DeepCloneable<LuanState> {
 
-	public final LuanTable global = new LuanTable();
-	public final LuanTable loaded = new LuanTable();
-	public final LuanTable preload = new LuanTable();
+	private LuanTable global;
+	private LuanTable loaded;
+	private LuanTable preload;
 
 	public InputStream in = System.in;
 	public PrintStream out = System.out;
 	public PrintStream err = System.err;
 
-	private final List<MetatableGetter> mtGetters = new ArrayList<MetatableGetter>();
+	private final List<MetatableGetter> mtGetters;
 	final List<StackTraceElement> stackTrace = new ArrayList<StackTraceElement>();
 
+	protected LuanState() {
+		global = new LuanTable();
+		loaded = new LuanTable();
+		preload = new LuanTable();
+		mtGetters = new ArrayList<MetatableGetter>();
+	}
 
-	public Object get(String name) {
+	protected LuanState(LuanState luan) {
+		mtGetters = new ArrayList<MetatableGetter>(luan.mtGetters);
+	}
+
+	public final LuanState deepClone() {
+		return new DeepCloner().deepClone(this);
+	}
+
+	@Override public void deepenClone(LuanState clone,DeepCloner cloner) {
+		clone.global = cloner.deepClone(global);
+		clone.loaded = cloner.deepClone(loaded);
+		clone.preload = cloner.deepClone(preload);
+	}
+
+	public final LuanTable global() {
+		return global;
+	}
+
+	public final LuanTable loaded() {
+		return loaded;
+	}
+
+	public final LuanTable preload() {
+		return preload;
+	}
+
+	public final Object get(String name) {
 		String[] a = name.split("\\.");
 		LuanTable t = global;
 		for( int i=0; i<a.length-1; i++ ) {
@@ -40,7 +72,7 @@
 		return t.get(a[a.length-1]);
 	}
 
-	public Object set(String name,Object value) {
+	public final Object set(String name,Object value) {
 		String[] a = name.split("\\.");
 		LuanTable t = global;
 		for( int i=0; i<a.length-1; i++ ) {
@@ -52,7 +84,7 @@
 		return t.put(a[a.length-1],value);
 	}
 
-	public void load(LuanFunction loader,String modName) throws LuanException {
+	public final void load(LuanFunction loader,String modName) throws LuanException {
 		Object mod = Luan.first(call(loader,LuanElement.JAVA,"loader",modName));
 		if( mod == null )
 			mod = true;
@@ -77,7 +109,7 @@
 		}
 	}
 
-	public Object[] eval(String cmd,String sourceName) throws LuanException {
+	public final Object[] eval(String cmd,String sourceName) throws LuanException {
 		LuanFunction fn = BasicLib.load(this,cmd,sourceName);
 		return call(fn,null,null);
 	}
@@ -100,7 +132,7 @@
 		mtGetters.add(mg);
 	}
 
-	public Object[] call(LuanFunction fn,LuanElement calledFrom,String fnName,Object... args) throws LuanException {
+	public final Object[] call(LuanFunction fn,LuanElement calledFrom,String fnName,Object... args) throws LuanException {
 		stackTrace.add( new StackTraceElement(calledFrom,fnName) );
 		try {
 			return fn.call(this,args);
--- a/src/luan/LuanTable.java	Fri Feb 15 04:52:16 2013 +0000
+++ b/src/luan/LuanTable.java	Fri Feb 15 09:55:17 2013 +0000
@@ -13,7 +13,7 @@
 import java.util.IdentityHashMap;
 
 
-public class LuanTable {
+public class LuanTable implements DeepCloneable<LuanTable> {
 	private Map<Object,Object> map = null;
 	private List<Object> list = null;
 	private LuanTable metatable = null;
@@ -35,6 +35,27 @@
 		}
 	}
 
+	@Override public LuanTable shallowClone() {
+		return new LuanTable();
+	}
+
+	@Override public void deepenClone(LuanTable clone,DeepCloner cloner) {
+		if( map != null ) {
+			clone.map = new HashMap<Object,Object>();
+			for( Map.Entry<Object,Object> entry : map.entrySet() ) {
+				clone.map.put( cloner.get(entry.getKey()), cloner.get(entry.getValue()) );
+			}
+		}
+		if( list != null ) {
+			clone.list = new ArrayList<Object>();
+			for( Object obj : list ) {
+				clone.list.add( cloner.get(obj) );
+			}
+		}
+		if( metatable != null )
+			clone.metatable = cloner.deepClone(metatable);
+	}
+
 	public boolean isList() {
 		return map==null || map.isEmpty();
 	}
--- a/src/luan/interp/Closure.java	Fri Feb 15 04:52:16 2013 +0000
+++ b/src/luan/interp/Closure.java	Fri Feb 15 09:55:17 2013 +0000
@@ -4,9 +4,11 @@
 import luan.LuanState;
 import luan.LuanElement;
 import luan.LuanException;
+import luan.DeepCloner;
+import luan.DeepCloneable;
 
 
-final class Closure extends LuanFunction {
+final class Closure extends LuanFunction implements DeepCloneable<Closure> {
 	private final Chunk chunk;
 	final UpValue[] upValues;
 	private final static UpValue[] NO_UP_VALUES = new UpValue[0];
@@ -24,6 +26,19 @@
 		}
 	}
 
+	private Closure(Closure c) {
+		this.chunk = c.chunk;
+		this.upValues = c.upValues==NO_UP_VALUES ? NO_UP_VALUES : c.upValues.clone();
+	}
+
+	@Override public Closure shallowClone() {
+		return new Closure(this);
+	}
+
+	@Override public void deepenClone(Closure clone,DeepCloner cloner) {
+		cloner.deepenClone(clone.upValues);
+	}
+
 	public Object[] call(LuanState luan,Object[] args) throws LuanException {
 		return call(this,(LuanStateImpl)luan,args);
 	}
--- a/src/luan/interp/LuanStateImpl.java	Fri Feb 15 04:52:16 2013 +0000
+++ b/src/luan/interp/LuanStateImpl.java	Fri Feb 15 09:55:17 2013 +0000
@@ -9,19 +9,11 @@
 import luan.MetatableGetter;
 import luan.LuanException;
 import luan.LuanElement;
+import luan.DeepCloner;
 
 
 final class LuanStateImpl extends LuanState {
 
-	final Object arithmetic(LuanElement el,String op,Object o1,Object o2) throws LuanException {
-		LuanFunction fn = getBinHandler(el,op,o1,o2);
-		if( fn != null )
-			return Luan.first(call(fn,el,op,o1,o2));
-		String type = Luan.toNumber(o1)==null ? Luan.type(o1) : Luan.type(o2);
-		throw new LuanException(this,el,"attempt to perform arithmetic on a "+type+" value");
-	}
-
-
 	private static class Frame {
 		final Frame previousFrame;
 		final Closure closure;
@@ -64,6 +56,18 @@
 	Object[] returnValues = LuanFunction.EMPTY_RTN;
 	Closure tailFn;
 
+	public 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);
+	}
+
 	// returns stack
 	Object[] newFrame(Closure closure, int stackSize, Object[] varArgs) {
 		frame = new Frame(frame,closure,stackSize,varArgs);
@@ -99,4 +103,14 @@
 	UpValue getUpValue(int index) {
 		return frame.getUpValue(index);
 	}
+
+
+	final Object arithmetic(LuanElement el,String op,Object o1,Object o2) throws LuanException {
+		LuanFunction fn = getBinHandler(el,op,o1,o2);
+		if( fn != null )
+			return Luan.first(call(fn,el,op,o1,o2));
+		String type = Luan.toNumber(o1)==null ? Luan.type(o1) : Luan.type(o2);
+		throw new LuanException(this,el,"attempt to perform arithmetic on a "+type+" value");
+	}
+
 }
--- a/src/luan/interp/UpValue.java	Fri Feb 15 04:52:16 2013 +0000
+++ b/src/luan/interp/UpValue.java	Fri Feb 15 09:55:17 2013 +0000
@@ -1,7 +1,10 @@
 package luan.interp;
 
+import luan.DeepCloner;
+import luan.DeepCloneable;
 
-final class UpValue {
+
+final class UpValue implements DeepCloneable<UpValue> {
 	private Object[] stack;
 	private int index;
 	private boolean isClosed = false;
@@ -17,6 +20,23 @@
 		this.isClosed = true;
 	}
 
+	private UpValue() {}
+
+	@Override public UpValue shallowClone() {
+		return new UpValue();
+	}
+
+	@Override public void deepenClone(UpValue clone,DeepCloner cloner) {
+		clone.isClosed = isClosed;
+		if( isClosed ) {
+			clone.value = cloner.get(value);
+		} else {
+			clone.stack = stack.clone();
+			cloner.deepenClone(clone.stack);
+			clone.index = index;
+		}
+	}
+
 	Object get() {
 		return isClosed ? value : stack[index];
 	}
@@ -65,7 +85,7 @@
 
 	static final Getter globalGetter = new Getter() {
 		public UpValue get(LuanStateImpl luan) {
-			return new UpValue(luan.global);
+			return new UpValue(luan.global());
 		}
 	};
 
--- a/src/luan/lib/BasicLib.java	Fri Feb 15 04:52:16 2013 +0000
+++ b/src/luan/lib/BasicLib.java	Fri Feb 15 09:55:17 2013 +0000
@@ -23,7 +23,7 @@
 
 	public static final LuanFunction LOADER = new LuanFunction() {
 		public Object[] call(LuanState luan,Object[] args) throws LuanException {
-			LuanTable global = luan.global;
+			LuanTable global = luan.global();
 			global.put( "_G", global );
 			try {
 				global.put( "assert", new LuanJavaFunction(BasicLib.class.getMethod("assert_",LuanState.class,Object.class,String.class),null) );
@@ -63,7 +63,7 @@
 	}
 
 	public static void make_standard(LuanState luan) {
-		LuanTable global = luan.global;
+		LuanTable global = luan.global();
 		global.put( "dofile", global.get("do_file") );
 		global.put( "getmetatable", global.get("get_metatable") );
 		global.put( "loadfile", global.get("load_file") );
--- a/src/luan/lib/HtmlLib.java	Fri Feb 15 04:52:16 2013 +0000
+++ b/src/luan/lib/HtmlLib.java	Fri Feb 15 09:55:17 2013 +0000
@@ -13,7 +13,7 @@
 	public static final LuanFunction LOADER = new LuanFunction() {
 		public Object[] call(LuanState luan,Object[] args) {
 			LuanTable module = new LuanTable();
-			LuanTable global = luan.global;
+			LuanTable global = luan.global();
 			try {
 				add( module, "encode", String.class );
 			} catch(NoSuchMethodException e) {
--- a/src/luan/lib/HttpLib.java	Fri Feb 15 04:52:16 2013 +0000
+++ b/src/luan/lib/HttpLib.java	Fri Feb 15 09:55:17 2013 +0000
@@ -23,12 +23,12 @@
 	public static void service(LuanState luan,HttpServletRequest request,HttpServletResponse response)
 		throws LuanException, IOException
 	{
-		LuanFunction fn = (LuanFunction)luan.global.get(FN_NAME);
+		LuanFunction fn = (LuanFunction)luan.global().get(FN_NAME);
 		ServletOutputStream sout = response.getOutputStream();
 		luan.out = new PrintStream(sout);
 
 		LuanTable module = new LuanTable();
-		luan.global.put(NAME,module);
+		luan.global().put(NAME,module);
 
 		LuanTable parameters = new LuanTable();
 		LuanTable parameter_lists = new LuanTable();
--- a/src/luan/lib/JavaLib.java	Fri Feb 15 04:52:16 2013 +0000
+++ b/src/luan/lib/JavaLib.java	Fri Feb 15 09:55:17 2013 +0000
@@ -33,7 +33,7 @@
 		public Object[] call(LuanState luan,Object[] args) throws LuanException {
 			luan.addMetatableGetter(mg);
 			LuanTable module = new LuanTable();
-			LuanTable global = luan.global;
+			LuanTable global = luan.global();
 			try {
 				global.put( "import", new LuanJavaFunction(JavaLib.class.getMethod("importClass",LuanState.class,String.class),null) );
 				module.put( "class", new LuanJavaFunction(JavaLib.class.getMethod("getClass",LuanState.class,String.class),null) );
@@ -298,7 +298,7 @@
 	}
 
 	public static void importClass(LuanState luan,String name) throws LuanException {
-		luan.global.put( name.substring(name.lastIndexOf('.')+1), getClass(luan,name) );
+		luan.global().put( name.substring(name.lastIndexOf('.')+1), getClass(luan,name) );
 	}
 
 	static class AmbiguousJavaFunction extends LuanFunction {
--- a/src/luan/lib/MathLib.java	Fri Feb 15 04:52:16 2013 +0000
+++ b/src/luan/lib/MathLib.java	Fri Feb 15 09:55:17 2013 +0000
@@ -13,7 +13,7 @@
 	public static final LuanFunction LOADER = new LuanFunction() {
 		public Object[] call(LuanState luan,Object[] args) {
 			LuanTable module = new LuanTable();
-			LuanTable global = luan.global;
+			LuanTable global = luan.global();
 			try {
 				add( module, "floor", Double.TYPE );
 			} catch(NoSuchMethodException e) {
--- a/src/luan/lib/PackageLib.java	Fri Feb 15 04:52:16 2013 +0000
+++ b/src/luan/lib/PackageLib.java	Fri Feb 15 09:55:17 2013 +0000
@@ -19,10 +19,10 @@
 
 	public static final LuanFunction LOADER = new LuanFunction() {
 		public Object[] call(LuanState luan,Object[] args) throws LuanException {
-			LuanTable global = luan.global;
+			LuanTable global = luan.global();
 			LuanTable module = new LuanTable();
-			module.put("loaded",luan.loaded);
-			module.put("preload",luan.preload);
+			module.put("loaded",luan.loaded());
+			module.put("preload",luan.preload());
 //			module.put("path","?.lua");
 			try {
 				add( global, "require", LuanState.class, String.class );
@@ -43,11 +43,11 @@
 	public static void require(LuanState luan,String modName) throws LuanException {
 		Object mod = module(luan,modName);
 		if( mod instanceof LuanTable )
-			luan.global.put(modName,mod);
+			luan.global().put(modName,mod);
 	}
 
 	public static Object module(LuanState luan,String modName) throws LuanException {
-		Object mod = luan.loaded.get(modName);
+		Object mod = luan.loaded().get(modName);
 		if( mod == null ) {
 			LuanTable searchers = (LuanTable)luan.get("package.searchers");
 			for( Object s : searchers.asList() ) {
@@ -59,7 +59,7 @@
 					mod = Luan.first(luan.call(loader,LuanElement.JAVA,"loader",modName,extra));
 					if( mod == null )
 						mod = true;
-					luan.loaded.put(modName,mod);
+					luan.loaded().put(modName,mod);
 				}
 			}
 			if( mod == null )
@@ -100,7 +100,7 @@
 	public static final LuanFunction preloadSearcher = new LuanFunction() {
 		public Object[] call(LuanState luan,Object[] args) throws LuanException {
 			String modName = (String)args[0];
-			return new Object[]{luan.preload.get(modName)};
+			return new Object[]{luan.preload().get(modName)};
 		}
 	};
 
--- a/src/luan/lib/StringLib.java	Fri Feb 15 04:52:16 2013 +0000
+++ b/src/luan/lib/StringLib.java	Fri Feb 15 09:55:17 2013 +0000
@@ -18,7 +18,7 @@
 	public static final LuanFunction LOADER = new LuanFunction() {
 		public Object[] call(LuanState luan,Object[] args) throws LuanException {
 			LuanTable module = new LuanTable();
-			LuanTable global = luan.global;
+			LuanTable global = luan.global();
 			try {
 				module.put( "byte", new LuanJavaFunction(StringLib.class.getMethod("byte_",String.class,Integer.class,Integer.class),null) );
 				module.put( "char", new LuanJavaFunction(StringLib.class.getMethod("char_",new byte[0].getClass()),null) );
--- a/src/luan/lib/TableLib.java	Fri Feb 15 04:52:16 2013 +0000
+++ b/src/luan/lib/TableLib.java	Fri Feb 15 09:55:17 2013 +0000
@@ -20,7 +20,7 @@
 	public static final LuanFunction LOADER = new LuanFunction() {
 		public Object[] call(LuanState luan,Object[] args) throws LuanException {
 			LuanTable module = new LuanTable();
-			LuanTable global = luan.global;
+			LuanTable global = luan.global();
 			try {
 				add( module, "concat", LuanState.class, LuanTable.class, String.class, Integer.class, Integer.class );
 				add( module, "insert", LuanState.class, LuanTable.class, Integer.TYPE, Object.class );
--- a/src/luan/tools/CmdLine.java	Fri Feb 15 04:52:16 2013 +0000
+++ b/src/luan/tools/CmdLine.java	Fri Feb 15 09:55:17 2013 +0000
@@ -65,7 +65,7 @@
 			for( int j=0; j<args.length; j++ ) {
 				argsTable.put( j, args[j] );
 			}
-			luan.global.put("arg",argsTable);
+			luan.global().put("arg",argsTable);
 			try {
 				LuanFunction fn = BasicLib.load_file(luan,file);
 				luan.call(fn,null,null,varArgs);
--- a/src/luan/tools/WebRun.java	Fri Feb 15 04:52:16 2013 +0000
+++ b/src/luan/tools/WebRun.java	Fri Feb 15 09:55:17 2013 +0000
@@ -33,8 +33,8 @@
 		try {
 			LuanState luan = newLuanState();
 			luan.out = out;
-			luan.global.put("request",request);
-			luan.global.put("response",response);
+			luan.global().put("request",request);
+			luan.global().put("response",response);
 			luan.eval(code,"WebRun");
 		} catch(LuanException e) {
 			logger.error(null,e);
--- a/src/luan/tools/WebServlet.java	Fri Feb 15 04:52:16 2013 +0000
+++ b/src/luan/tools/WebServlet.java	Fri Feb 15 09:55:17 2013 +0000
@@ -25,6 +25,8 @@
 
 	public static final String HTTP_SERVER = "http_server";
 
+	protected LuanState luanState = null;
+
 	protected void loadLibs(LuanState luan) throws LuanException {
 		luan.load(BasicLib.LOADER,BasicLib.NAME);
 		luan.load(PackageLib.LOADER,PackageLib.NAME);
@@ -36,7 +38,7 @@
 
 	protected void loadLuan(LuanState luan) throws LuanException {
 		PackageLib.require(luan,HTTP_SERVER);
-		Object fn = luan.global.get(HttpLib.FN_NAME);
+		Object fn = luan.global().get(HttpLib.FN_NAME);
 		if( !(fn instanceof LuanFunction) )
 			throw new LuanException( luan, LuanElement.JAVA, "function '"+HttpLib.FN_NAME+"' not defined" );
 	}
@@ -48,8 +50,12 @@
 		return luan;
 	}
 
-	protected LuanState getLuanState() throws LuanException {
-		return newLuanState();
+	protected  LuanState getLuanState() throws LuanException {
+		synchronized(this) {
+			if( luanState == null )
+				luanState = newLuanState();
+		}
+		return luanState.deepClone();
 	}
 
 	protected void service(HttpServletRequest request,HttpServletResponse response)
--- a/src/luan/tools/WebShell.java	Fri Feb 15 04:52:16 2013 +0000
+++ b/src/luan/tools/WebShell.java	Fri Feb 15 09:55:17 2013 +0000
@@ -59,8 +59,8 @@
 						session.putValue("luan",luan);
 					}
 					luan.out = new PrintStream(history);
-					luan.global.put("request",request);
-					luan.global.put("response",response);
+					luan.global().put("request",request);
+					luan.global().put("response",response);
 					Object[] result = eval(luan,cmd);
 					if( result.length > 0 ) {
 						for( int i=0; i<result.length; i++ ) {