1    	package acl2s.lib.session;
2    	
3    	import java.io.File;
4    	import java.io.IOException;
5    	import java.io.InputStreamReader;
6    	import java.io.OutputStreamWriter;
7    	import java.io.Serializable;
8    	import java.util.ArrayList;
9    	import java.util.HashMap;
10   	import java.util.Map;
11   	import java.util.Set;
12   	import java.util.concurrent.locks.Lock;
13   	import java.util.concurrent.locks.ReentrantLock;
14   	
15   	import org.peterd.util.Misc;
16   	import org.peterd.util.io.AsyncWriter;
17   	import org.peterd.util.io.LineReader;
18   	import org.peterd.util.process.InterruptibleProcess;
19   	
20   	import acl2s.lib.parse.IPackageDefinition;
21   	import acl2s.lib.parse.IParseContext;
22   	import acl2s.lib.parse.PackageDefinition;
23   	import acl2s.lib.parse.ParseContext;
24   	import acl2s.lib.parse.ParseException;
25   	import acl2s.lib.parse.obj.Cons;
26   	import acl2s.lib.parse.obj.Int;
27   	import acl2s.lib.parse.obj.LispObject;
28   	import acl2s.lib.parse.obj.NotListException;
29   	import acl2s.lib.parse.obj.Num;
30   	import acl2s.lib.parse.obj.Rat;
31   	import acl2s.lib.parse.obj.Str;
32   	import acl2s.lib.parse.obj.Sym;
33   	import acl2s.lib.parse.obj.Zero;
34   	import acl2s.lib_driver.IAcl2sLibDriver;
35   	import acl2s.lib_driver.IAcl2sLibErrorHandler;
36   	import acl2s.lib_driver.IAcl2sLibThreadManager;
37   	
38   	public class InteractiveSession {
39   		protected InterruptibleProcess proc;
40   		protected boolean busy;
41   		protected Lock outReadLock;
42   		protected AsyncWriter acl2Input;
43   		protected LineReader acl2Output;
44   		protected StatusData status;
45   		protected EnvData env;
46   		protected EnvData oldEnv;
47   		protected final IAcl2sLibErrorHandler errorHandler;
48   		protected final IAcl2sLibThreadManager threadMgr;
49   		protected final ISessionConfig config;
50   		protected final File workingDir;
51   		protected final Killer killer;
52   		protected long secret;
53   		protected boolean interruptPending;
54   		protected String[] cmdLine;
55   		protected HashMap<String,IPackageDefinition> pkgDefs = new HashMap<String, IPackageDefinition>();
56   		
57   		public InteractiveSession(IAcl2sLibDriver driver, ISessionConfig config, File workingDir) {
58   			proc = null;
59   			this.config = config;
60   			this.workingDir = workingDir;
61   			errorHandler = driver.getErrorHandler();
62   			threadMgr = driver.getThreadManager();
63   			killer = new Killer();
64   			pkgDefs.putAll(BootstrapParseContext.pkgs);
65   		}
66   	
67   		public synchronized void beginInit() throws IOException, IllegalStateException {
Event lock_event: Locking "acl2s.lib.session.InteractiveSession.this".
Also see events: [unguarded_access][lock_event][guarded_by_access][lock_event][guarded_by_access][guarded_by_access]
68   			if (proc != null)  throw new IllegalStateException();
69   			cmdLine = config.getBaseConfig().getCmdLine();
70   			proc = InterruptibleProcess.exec(cmdLine, workingDir, config.getBaseConfig(), true);
71   			acl2Input = new AsyncWriter(new OutputStreamWriter(proc.getStdin())).fork();
72   			acl2Output = new LineReader(new InputStreamReader(proc.getStdout()));
73   			busy = true;
74   			interruptPending = false;
75   			outReadLock = new ReentrantLock();
Event guarded_by_access: Field "acl2s.lib.session.InteractiveSession.status" guarded by lock "acl2s.lib.session.InteractiveSession.this".
Also see events: [unguarded_access][lock_event][guarded_by_access][lock_event][guarded_by_access][lock_event]
76   			status = new StatusData();
77   			status.success = true;  // so that startup appears to succeed
78   			oldEnv = new EnvData();
79   			env = null;
80   			secret = Misc.random.nextLong() & 0xfffffffffffffffL;
81   	
82   			// Initial input stuff
83   			String[] startup = config.getStartup(secret);
84   			if (startup != null) {
85   				for (int i = 0; i < startup.length; i++) {
86   					acl2Input.write(startup[i]);
87   				}
88   			}
89   		}
90   		
91   		public synchronized IResultInfo fullInit() throws IllegalStateException, IOException, SessionDeadException {
92   			beginInit();
93   			try {
94   				return finishCmd();
95   			} catch (SessionNotBusyException e) {
96   				// shouldn't happen
97   				throw new IllegalStateException(e);
98   			}
99   		}
100  		
101  		public void dispose() {
102  			kill();
103  		}
104  		
105  		public void close() throws SessionStateException {
106  			close(1000);
107  		}
108  		
109  		public synchronized void close(int millis) throws SessionStateException {
110  			if (proc == null) return;
111  			if (busy) throw new SessionBusyException();
112  			if (env == null) throw new SessionStateException();
113  			try {
114  				acl2Input.write("#.\n(acl2s::clear-acl2s-password " + secret + " acl2::state)\n(acl2::value :q)\n(acl2::good-bye)\n");
115  				acl2Input.close();
116  				busy = true;
117  			} catch (IOException ioe) {
118  				errorHandler.warning("Error while closing ACL2 session.", ioe);
119  			} finally {
120  				try {
121  					proc.waitFor(millis);
122  				} catch (Exception e) {
123  					proc.kill();
124  				}
125  				proc = null;
126  			}
127  		}
128  		
129  		public synchronized boolean interrupted() {
130  			return interruptPending || status.interrupted;
131  		}
132  		
133  		public synchronized void interrupt() throws SessionDeadException, SessionNotBusyException {
134  			if (proc == null) throw new SessionDeadException();
135  			if (!busy && env != null) throw new SessionNotBusyException();
136  			if (interrupted()) return;
137  			try {
138  				proc.interrupt();
139  				busy = true;
140  				interruptPending = true;
141  			} catch (IOException ioe) {
142  				errorHandler.error("Error while interrupting ACL2 session.", ioe);
143  				proc.kill();
144  				proc = null;
145  				return;
146  			}
147  		}
148  	
149  		/*
150  		public void abort() throws SessionDeadException, SessionStateException {
151  			miscInput("#.");
152  		}
153  		
154  		public synchronized void interruptOrAbort()	throws SessionDeadException {
155  			try {
156  				if (busy || env == null) {
157  					interrupt();
158  				} else if (env == null || env.inWormhole()) {
159  					abort();
160  				}
161  			} catch (SessionStateException sse) {
162  				// shouldn't happen
163  			}
164  		}
165  		//*/
166  		
167  		public synchronized boolean isAlive() {
168  			return proc != null;
169  		}
170  		
171  		// call this if you already have the mutex or don't mind waiting
172  		public synchronized void kill() {
173  			try {
174  				close();
175  			} catch (SessionStateException e) {
176  				if (proc != null) {
177  					proc.kill();
178  					proc = null;
179  				}
180  			}
181  		}
182  		
183  		class Killer implements Runnable {
184  			public void run() {
185  				kill();
186  			}
187  		}
188  			
189  		// call this if you don't want to wait for this mutex
190  		public void forkKill() {
191  			threadMgr.delayedExec(killer, 0, true);
192  		}
193  		
194  		public Runnable getKiller() { return killer; }
195  		
196  		
197  		static final Sym WITH_LIMIT = Sym.bootstrapCreate("WITH-PROVER-TIME-LIMIT");
198  		static final int LIMIT_DENOM = 1000;
199  		static final float LIMIT_MAX = Integer.MAX_VALUE / LIMIT_DENOM;
200  		
201  		public synchronized IResultInfo systemCmd(LispObject cmd, float proverTimeLimit)
202  		throws SessionDeadException, SessionStateException {
203  			if (proverTimeLimit <= 0f || proverTimeLimit >= LIMIT_MAX) throw new IllegalArgumentException();
204  	
205  			Num limit = Rat.create((int)(proverTimeLimit * LIMIT_DENOM) + 1, LIMIT_DENOM);
206  			return systemCmd(new Cons(WITH_LIMIT,
207  					         new Cons(limit,
208  					         new Cons(cmd, Sym.nilInstance))));
209  		}
210  		
211  		public synchronized IResultInfo systemCmd(LispObject cmd)
212  		throws SessionDeadException, SessionStateException {
213  			return systemCmd(cmd.toString(env.getParseContext()));
214  		}
215  		
216  		public synchronized IResultInfo systemCmd(String cmd, float proverTimeLimit)
217  		throws SessionDeadException, SessionStateException {
218  			if (proverTimeLimit <= 0f || proverTimeLimit >= LIMIT_MAX) throw new IllegalArgumentException();
219  			
220  			String num = Integer.toString((int)(proverTimeLimit * LIMIT_DENOM) + 1, env.getReadBase());
221  			String denom = Integer.toString(LIMIT_DENOM, env.getReadBase());
222  			return systemCmd("(ACL2::WITH-PROVER-TIME-LIMIT " + num + "/" + denom + " " + cmd + ")");
223  		}
224  		
225  		public synchronized IResultInfo systemCmd(String cmd)
226  		throws SessionDeadException, SessionStateException {
227  			userCmd(cmd);
228  			return finishCmd();
229  		}
230  		
231  		public synchronized void userCmd(String s)
232  		throws SessionDeadException, SessionStateException {
Event lock_event: Locking "acl2s.lib.session.InteractiveSession.this".
Event lock_event: Locking "acl2s.lib.session.InteractiveSession.this".
Also see events: [unguarded_access][guarded_by_access][guarded_by_access][lock_event][guarded_by_access]
233  			if (s == null) {
Event guarded_by_access: Field "acl2s.lib.session.InteractiveSession.status" guarded by lock "acl2s.lib.session.InteractiveSession.this".
Also see events: [unguarded_access][lock_event][lock_event][guarded_by_access][lock_event][guarded_by_access]
234  				status = new StatusData();
235  				return;
236  			}
237  			if (proc == null) throw new SessionDeadException();
238  			if (busy) throw new SessionBusyException();
239  			if (env == null) throw new SessionStateException("Not accepting commands at the moment.");
Event guarded_by_access: Field "acl2s.lib.session.InteractiveSession.status" guarded by lock "acl2s.lib.session.InteractiveSession.this".
Also see events: [unguarded_access][lock_event][guarded_by_access][lock_event][lock_event][guarded_by_access]
240  			status = new StatusData();
241  			oldEnv = env;
242  			env = null;
243  			try {
244  				acl2Input.write(s);
245  				acl2Input.write('\n');
246  				//acl2Input.flush();
247  				busy = true;
248  			} catch (IOException ioe) {
249  				errorHandler.error("Error while writing command input to ACL2.", ioe);
250  				kill();
251  			}
252  		}
253  	
254  		public synchronized void miscInput(LispObject obj) throws SessionDeadException, SessionStateException {
255  			miscInput(obj.toString());
256  		}
257  		
258  		public synchronized void miscInput(String s) throws SessionDeadException, SessionStateException {
259  			if (proc == null) throw new SessionDeadException();
260  			if (busy) throw new SessionBusyException();
261  			if (env != null) throw new SessionStateException("Not expecting non-command input.");
262  			try {
263  				acl2Input.write(s);
264  				acl2Input.write('\n');
265  				//acl2Input.flush();
266  				busy = true;
267  			} catch (IOException ioe) {
268  				errorHandler.error("Error while writing misc input to ACL2.", ioe);
269  				kill();
270  			}
271  		}
272  		
273  		public synchronized IResultInfo finishCmd() throws SessionDeadException, SessionNotBusyException {
274  			if (!busy) {
275  				if (isAlive()) {
276  					throw new SessionNotBusyException();
277  				} else {
278  					throw new SessionDeadException();
279  				}
280  			}
281  			StringBuilder ret = new StringBuilder();
282  			String s;
283  			for (;;) {
284  				s = readLineUntilPrompt();
285  				if (s == null) return new Result(ret.toString(),status);
286  				ret.append(s);
287  			}
288  		}
289  		
290  		private transient final ArrayList<String> lines = new ArrayList<String>();
291  		public String[] readLinesUntilPrompt(int max) {
292  			String first = readLineUntilPrompt();
293  			if (first == null) return null;
294  			
295  			lines.clear();
296  			lines.add(first);
297  			for (;;) {
298  				if (max > 0 && lines.size() >= max) break;
299  				if (!acl2Output.ready()) {
300  					try {
301  						Thread.sleep(100);
302  					} catch (InterruptedException e) {
303  						// ignore
304  					}
305  					if (!acl2Output.ready()) {
306  						break;
307  					}
308  				}
309  				String line = readLineUntilPrompt();
310  				if (line == null) {
311  					break;
312  				} else {
313  					lines.add(line);
314  				}
315  			}
316  			return lines.toArray(new String[lines.size()]);
317  		}
318  		
319  		static final String SUCCESS = "${SuCcEsS}$\n";
320  		static final int SUCCESS_LEN = SUCCESS.length();
321  		
322  		static final String BEGIN_PKG = "$DeFpKg{$\n";
323  		static final int BEGIN_PKG_LEN = BEGIN_PKG.length();
324  		static final String END_PKG = "$DeFpKg}$\n";
325  		static final int END_PKG_LEN = END_PKG.length();
326  	
327  		static final String BEGIN_ENV = "$EnViRoNmEnT{$\n";
328  		static final int BEGIN_ENV_LEN = BEGIN_ENV.length();
329  		static final String MIDDLE_ENV = "$EnViRoNmEnT$";
330  		static final int MIDDLE_ENV_LEN = MIDDLE_ENV.length();
331  		static final String END_ENV = "$EnViRoNmEnT}$\n"; 
332  		static final int END_ENV_LEN = END_ENV.length();
333  		
334  		static final String READ_OBJECT = "${ReAd-ObJeCt}$\n";
335  		static final int READ_OBJECT_LEN = READ_OBJECT.length();
336  		
337  		private transient final StringBuilder buf = new StringBuilder(100);	
338  		public String readLineUntilPrompt() {
339  			if (!outReadLock.tryLock()) throw new IllegalStateException("Multiple threads trying to read.");
340  			try {
341  				String s;
342  				int start;
343  				synchronized (this) {
344  					if (proc == null) throw new SessionDeadException();
345  					if (!busy) return null;
346  				}
347  				boolean lineRead;
348  				buf.setLength(0);
349  				lineRead = acl2Output.readLineTo(buf);
350  				if (!lineRead) throw new SessionDeadException();
351  				buf.append('\n');
352  				s = buf.toString();
353  				synchronized (this) {
354  					if (interruptPending && MyBaseConfig.isConsoleInterrupt(s)) {
355  						interruptPending = false;
356  						status.interrupted = true;
357  					}
358  				}
359  				
360  				if (s.endsWith("$\n")) { // optimization: check for common suffix
361  	
362  					if (s.endsWith(SUCCESS)) {
363  						synchronized (this) {
364  							status.success = true;
365  						}
366  						s = s.substring(0,s.length() - SUCCESS_LEN);
367  					}
368  	
369  					if (s.endsWith(BEGIN_ENV)) {
370  						start = s.length() - BEGIN_ENV_LEN;
371  						// slow but rare:
372  						while (!s.endsWith(END_ENV)) {
373  							lineRead = acl2Output.readLineTo(buf);
374  							if (!lineRead) throw new SessionDeadException();
375  							buf.append('\n');
376  							s = buf.toString();
377  						}
378  						int end = s.indexOf(MIDDLE_ENV);
379  						if (end < start + BEGIN_ENV_LEN) {
380  							errorHandler.error("Bad environment output from ACL2.  Aborting session.", null);
381  							throw new SessionDeadException();
382  						}
383  						String sub = s.substring(start + BEGIN_ENV_LEN,end);
384  						synchronized (this) {
385  							env = new EnvData();
386  							try {
387  								LispObject o = LispObject.canonicalParseFirst(sub);
388  								
389  								processPrompt(o);
390  							} catch (ParseException pe) {
391  								errorHandler.error("Erroroneous environment output from ACL2.  Aborting session.", pe);
392  								throw new SessionDeadException();
393  							}
394  							String pr = s.substring(end + MIDDLE_ENV_LEN, s.length() - END_ENV_LEN);
395  							env.setPrompt(pr);
396  						}
397  	
398  						s = s.substring(0,start);
399  					}
400  	
401  					if (s.endsWith(BEGIN_PKG)) {
402  						start = s.length() - BEGIN_PKG_LEN;
403  						// slow but rare enough:
404  						while (!s.endsWith(END_PKG)) {
405  							lineRead = acl2Output.readLineTo(buf);
406  							if (!lineRead) throw new SessionDeadException();
407  							buf.append('\n');
408  							s = buf.toString();
409  						}
410  						
411  						String defStr = s.substring(start + BEGIN_PKG_LEN, s.length() - END_PKG_LEN);
412  						
413  						try {
414  							LispObject defObj = LispObject.canonicalParseFirst(defStr);
415  							Cons defCons = (Cons) defObj;
416  							String pkgName = ((Str)defCons.car).value;
417  							LispObject[] importObjs = Cons.listToArray(defCons.cdr);
418  							Sym[] imports = new Sym[importObjs.length];
419  							System.arraycopy(importObjs, 0, imports, 0, importObjs.length);
420  							IPackageDefinition def = new PackageDefinition(pkgName, imports);
421  							synchronized (this) {
422  								pkgDefs.put(pkgName, def);
423  							}
424  						} catch (ClassCastException e) {
425  							errorHandler.error("Erroroneous package definition output from ACL2.  Aborting session.", e);
426  							throw new SessionDeadException();
427  						} catch (ArrayStoreException e) {
428  							errorHandler.error("Erroroneous package definition output from ACL2.  Aborting session.", e);
429  							throw new SessionDeadException();
430  						} catch (ParseException e) {
431  							errorHandler.error("Erroroneous package definition output from ACL2.  Aborting session.", e);
432  							throw new SessionDeadException();
433  						} catch (NotListException e) {
434  							errorHandler.error("Erroroneous package definition output from ACL2.  Aborting session.", e);
435  							throw new SessionDeadException();
436  						}
437  						
438  						s = s.substring(0,start);
439  					}
440  					
441  					if (s.endsWith(READ_OBJECT)) {
442  						s = s.substring(0,s.length() - READ_OBJECT_LEN);
443  						synchronized (this) {
444  							if (!interruptPending) {
445  								busy = false;
446  								notifyAll();
447  							} else {
448  								s = s + "\n(interruption arrived after completion of above)";
449  								
450  								// if win32 SBCL with my ctrl+c hack, poke it to get interrupt to happen 
451  								//acl2Input.write("#+sbcl-ctrlc-hack (acl2::mv (acl2::time-limit4-reached-p \"foo\") :invisible acl2::state)\n");
452  							}
453  						}
454  					}
455  				}
456  				
457  				return s;
458  			} catch (SessionDeadException sde) {
459  				synchronized (this) {
460  					status.interrupted = true;
461  					status.success = false;
462  					if (proc != null) {
463  						proc.kill();
464  						proc = null;
465  					}
466  				}
467  				return null;
468  			} catch (IOException ioe) {
469  				synchronized (this) {
470  					if (proc == null) return null;
471  					status.interrupted = true;
472  					status.success = false;
473  					kill();
474  				}
475  				errorHandler.error("Error while reading ACL2 output.", ioe);
476  				return null;
477  			} finally {
478  				outReadLock.unlock();
479  			}
480  		}
481  		
482  		protected synchronized void processPrompt(LispObject obj) throws ParseException {
483  			env.fromObjAndPkgDefs(obj, pkgDefs);
484  			status.superDone = env.superDone;
485  			status.extendedWorld = env.superDone > oldEnv.superDone;
486  			if (env.ldLevel > oldEnv.ldLevel) { // another way success can happen
487  				status.success = true;
488  			}
489  		}
490  		
491  		public long getSecret() { return secret; }
492  		public IEnvInfo getEnvInfo() { return env; }
Event unguarded_access: Accessing "acl2s.lib.session.InteractiveSession.status" without holding a lock on "acl2s.lib.session.InteractiveSession.this".
Also see events: [lock_event][guarded_by_access][lock_event][guarded_by_access][lock_event][guarded_by_access]
493  		public IStatusInfo getStatusInfo() { return status; }
494  		
495  		static class StatusData implements IStatusInfo, Serializable {
496  			private static final long serialVersionUID = 3L;
497  			
498  			boolean extendedWorld = false;
499  			boolean interrupted = false;
500  			boolean success = false;
501  			int superDone;
502  			
503  			public boolean wasInterrupted() {
504  				return interrupted;
505  			}
506  	
507  			public boolean wasSuccess() {
508  				return success;
509  			}
510  	
511  			public boolean extendedLogicalState() {
512  				return extendedWorld;
513  			}
514  	
515  			public int getSuperDoneSize() {
516  				return superDone;
517  			}
518  		}
519  	
520  		static Set<String> bootstrapPkgs = BootstrapParseContext.instance.getBootstrapPackages();
521  		
522  		static class EnvData extends ParseContext implements IEnvInfo, Serializable {
523  			private static final long serialVersionUID = 2L;
524  			
525  			String prompt;
526  			int ldLevel;
527  			int superDone;
528  			int superUndone;
529  			int eventIndex;
530  			boolean inWormhole;
531  			boolean inRawMode;
532  			
533  			EnvData() {
534  				ldLevel = 1;
535  				superDone = 1;
536  				superUndone = 0;
537  				pkgs = new HashMap<String, IPackageDefinition>();
538  			}
539  			
540  			void fromObjAndPkgDefs(LispObject obj, Map<String,IPackageDefinition> pkgDefs) throws ParseException {
541  				try {
542  					Cons lst = (Cons)obj;
543  					defltPackage = ((Str)lst.car).value;
544  					
545  					lst = (Cons)(lst.cdr);
546  					ldLevel = ((Int)lst.car).v.intValue();
547  					
548  					lst = (Cons)(lst.cdr);
549  					inWormhole = !(lst.car instanceof Zero);
550  					
551  					lst = (Cons)(lst.cdr);
552  					inRawMode = !(lst.car instanceof Zero);
553  					
554  					lst = (Cons)(lst.cdr);
555  					LispObject[] pkgNames = Cons.listToArray(lst.car);
556  					
557  					pkgs.clear();
558  					for (LispObject o : pkgNames) {
559  						String pkgName = ((Str)o).value; 
560  						pkgs.put(pkgName, pkgDefs.get(pkgName));
561  					}
562  					
563  					lst = (Cons)(lst.cdr);
564  					eventIndex = ((Int)lst.car).v.intValue();
565  	
566  					lst = (Cons)(lst.cdr);
567  					superDone = ((Int)lst.car).v.intValue();
568  					
569  					lst = (Cons)(lst.cdr);
570  					superUndone = ((Int)lst.car).v.intValue();
571  				} catch (ClassCastException cce) {
572  					throw new ParseException("Invalid prompt data.");
573  				} catch (NotListException e) {
574  					throw new ParseException("Invalid prompt data.");
575  				}
576  			}
577  	
578  			void setPrompt(String s) {
579  				prompt = s;
580  			}
581  			
582  			public int getLdLevel() {
583  				return ldLevel;
584  			}
585  	
586  			public String getPakage() {
587  				return getDefaultPackage();
588  			}
589  	
590  			public String getPrompt() {
591  				return prompt;
592  			}
593  			
594  			public int getLastEventIndex() {
595  				return eventIndex;
596  			}
597  			
598  			public boolean inWormhole() {
599  				return inWormhole;
600  			}
601  	
602  			public boolean inRawMode() {
603  				return inRawMode;
604  			}
605  	
606  			public int getSuperDoneSize() {
607  				return superDone;
608  			}
609  	
610  			public int getSuperUndoneSize() {
611  				return superUndone;
612  			}
613  	
614  			public IParseContext getParseContext() {
615  				return this;
616  			}
617  	
618  			public void forgetRedo() {
619  				superUndone = 0;
620  			}
621  		}
622  		
623  		static class Result implements IResultInfo {
624  			private static final long serialVersionUID = 2L;
625  	
626  			final String output;
627  			final boolean interrupted;
628  			final boolean success;
629  			final boolean affectedWorld;
630  			final int superDone;
631  			
632  			public Result(String output, StatusData p) {
633  				this.output = output;
634  				this.interrupted = p.interrupted;
635  				this.success = p.success;
636  				this.affectedWorld = p.extendedWorld;
637  				this.superDone = p.superDone;
638  			}
639  			
640  			public String getOutput() {
641  				return output;
642  			}
643  	
644  			public boolean wasInterrupted() {
645  				return interrupted;
646  			}
647  	
648  			public boolean wasSuccess() {
649  				return success;
650  			}
651  	
652  			public boolean extendedLogicalState() {
653  				return affectedWorld;
654  			}
655  	
656  			public int getSuperDoneSize() {
657  				return superDone;
658  			}
659  		}
660  		
661  		public String[] getCmdLine() {
662  			return cmdLine;
663  		}
664  		
665  		public ISessionConfig getConfig() {
666  			return config;
667  		}
668  	}