package org.dynjs.runtime;
import org.dynjs.debugger.Debugger;
import org.dynjs.exception.DynJSException;
import org.dynjs.exception.ThrowException;
import org.dynjs.parser.js.ParserException;
import org.dynjs.parser.js.SyntaxError;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Reader;
import java.util.concurrent.Executor;
public class Runner {
private final DynJS runtime;
private Compiler compiler;
private ExecutionContext context;
private boolean directEval;
private JSProgram source;
private Executor executor;
private Object result;
private State state = State.COMPLETE;
private Debugger debugger;
private enum State {
RUNNING,
COMPLETE;
}
public Runner(DynJS runtime) {
this.compiler = new Compiler(runtime.getConfig());
this.runtime = runtime;
}
public Runner forceStrict() {
this.compiler.forceStrict();
return this;
}
public Runner forceStrict(boolean forceStrict) {
this.compiler.forceStrict(forceStrict);
return this;
}
public Runner directEval() {
return directEval(true);
}
public Runner directEval(boolean directEval) {
this.directEval = directEval;
return this;
}
public Runner withSource(JSProgram source) {
this.source = source;
return this;
}
public Runner withExecutor(Executor executor) {
this.executor = executor;
return this;
}
public Runner withSource(String source) {
this.compiler.withSource(source);
return this;
}
/*
public Runner withSource(Reader source) {
this.compiler.withSource( source );
return this;
}
*/
public Runner withSource(SourceProvider source) {
this.compiler.withSource(source);
return this;
}
public Runner withSource(File source) throws IOException {
this.compiler.withSource(source);
return this;
}
public Runner withContext(ExecutionContext context) {
this.context = context;
this.compiler.withContext(context);
return this;
}
public Runner withFileName(String fileName) {
this.compiler.withFileName(fileName);
return this;
}
public Runner debug(boolean debug) {
if (debug) {
this.debugger = new Debugger();
}
return this;
}
public Runner withDebugger(Debugger debugger) {
this.debugger = debugger;
return this;
}
protected ExecutionContext executionContext() {
if (this.context == null) {
return this.runtime.getDefaultExecutionContext();
}
return this.context;
}
protected JSProgram program() throws IOException {
if (this.source != null) {
return this.source;
}
return this.compiler.compile();
}
public Object result() {
return this.result;
}
public boolean isRunning() {
return this.state == State.RUNNING;
}
public synchronized boolean isComplete() {
return this.state == State.COMPLETE;
}
public synchronized void join() throws InterruptedException {
while (this.state != State.COMPLETE) {
wait();
}
}
public Debugger getDebugger() {
return this.debugger;
}
protected void setup() {
// no-op;
}
public synchronized Object execute() {
if (this.state != State.COMPLETE) {
throw new DynJSException("Running is currently in-use");
}
setup();
if (this.executor == null) {
this.result = doExecute();
return this.result;
}
this.state = State.RUNNING;
this.executor.execute(new Runnable() {
@Override
public void run() {
try {
Runner.this.result = doExecute();
} finally {
synchronized (Runner.this) {
Runner.this.state = State.COMPLETE;
Runner.this.notifyAll();
}
}
}
});
return null;
}
private Object doExecute() {
try {
if ( this.debugger != null ) {
this.debugger.setGlobalContext( executionContext() );
}
Completion completion = executionContext().execute(program(), this.debugger);
if (completion.type == Completion.Type.BREAK || completion.type == Completion.Type.CONTINUE) {
throw new ThrowException(executionContext(), executionContext().createSyntaxError("illegal break or continue"));
}
Object v = completion.value;
if (v instanceof Reference) {
return ((Reference) v).getValue(context);
}
return v;
} catch (SyntaxError e) {
throw new ThrowException(executionContext(), executionContext().createSyntaxError(e.getMessage()));
} catch (ParserException e) {
throw new ThrowException(executionContext(), e);
} catch (IOException e) {
throw new ThrowException(executionContext(), e);
}
}
public synchronized Object evaluate() {
if (this.state != State.COMPLETE) {
throw new DynJSException("Running is currently in-use");
}
setup();
if (this.executor == null) {
this.result = doEvaluate();
return this.result;
}
this.state = State.RUNNING;
this.executor.execute(new Runnable() {
@Override
public void run() {
try {
Runner.this.result = doEvaluate();
} finally {
synchronized (Runner.this) {
Runner.this.state = State.COMPLETE;
Runner.this.notifyAll();
}
}
}
});
return null;
}
private Object doEvaluate() {
if ( this.debugger != null ) {
this.debugger.setGlobalContext( executionContext() );
}
try {
return executionContext().eval(program(), this.directEval);
} catch (SyntaxError e) {
throw new ThrowException(executionContext(), executionContext().createSyntaxError(e.getMessage()));
} catch (ParserException e) {
throw new ThrowException(executionContext(), e);
} catch (IOException e) {
throw new ThrowException(executionContext(), e);
}
}
}