il.ac.bgu.cs.bp.bpjs.execution.tasks.BPEngineTask Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of BPjs Show documentation
Show all versions of BPjs Show documentation
Provides runtime and analysis for behavioral programs written in
JavaScript. It can run stand-alone (from the commmandline) or be
embedded in larger JVM-based systems.
The newest version!
package il.ac.bgu.cs.bp.bpjs.execution.tasks;
import il.ac.bgu.cs.bp.bpjs.BPjs;
import il.ac.bgu.cs.bp.bpjs.exceptions.BPjsCodeEvaluationException;
import il.ac.bgu.cs.bp.bpjs.exceptions.BPjsException;
import il.ac.bgu.cs.bp.bpjs.exceptions.BPjsRuntimeException;
import il.ac.bgu.cs.bp.bpjs.execution.jsproxy.BProgramJsProxy;
import il.ac.bgu.cs.bp.bpjs.execution.jsproxy.BProgramJsProxy.CapturedBThreadState;
import il.ac.bgu.cs.bp.bpjs.execution.jsproxy.MapProxy;
import il.ac.bgu.cs.bp.bpjs.model.BProgram;
import il.ac.bgu.cs.bp.bpjs.model.BProgramSyncSnapshot;
import il.ac.bgu.cs.bp.bpjs.model.SyncStatement;
import java.util.concurrent.Callable;
import il.ac.bgu.cs.bp.bpjs.model.BThreadSyncSnapshot;
import il.ac.bgu.cs.bp.bpjs.model.FailedAssertionViolation;
import il.ac.bgu.cs.bp.bpjs.model.ForkStatement;
import il.ac.bgu.cs.bp.bpjs.model.eventsets.EventSets;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContinuationPending;
import org.mozilla.javascript.EcmaError;
import org.mozilla.javascript.EvaluatorException;
import org.mozilla.javascript.JavaScriptException;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.Undefined;
import org.mozilla.javascript.WrappedException;
/**
* Base interface for a parallel task executed during the execution of a {@link BProgram}.
* @author Michael
*/
public abstract class BPEngineTask implements Callable{
/**
* Callback interface for when assertions fail.
*/
public static interface Listener {
public void assertionFailed( FailedAssertionViolation fa );
public void addFork( ForkStatement stmt );
}
protected final BThreadSyncSnapshot btss;
protected final BProgramSyncSnapshot bpss;
protected final Listener listener;
BPEngineTask(BProgramSyncSnapshot aBpss, BThreadSyncSnapshot aBtss, Listener aListener) {
bpss = aBpss;
btss = aBtss;
listener = aListener;
}
abstract void callImpl(Context jsContext);
@Override
public BThreadSyncSnapshot call() {
Context jsContext = BPjs.enterRhinoContext();
try {
BProgramJsProxy.setCurrentBThread(bpss, btss);
callImpl( jsContext );
// capture proxy changes
MapProxy changes = BProgramJsProxy.getCurrentChanges();
if ( changes != null ) {
if ( ! changes.getModifications().isEmpty() ) {
return new BThreadSyncSnapshot(btss.getName(), null, null, null, null, null, changes);
}
}
return null;
} catch (ContinuationPending cbs) {
return handleContinuationPending(cbs, jsContext);
} catch ( WrappedException wfae ) {
return handleWrappedException(wfae);
} catch ( EvaluatorException eve ) {
throw new BPjsCodeEvaluationException(eve);
} catch ( JavaScriptException eve ) {
throw new BPjsCodeEvaluationException(eve);
} catch ( EcmaError jsError ) {
if ( jsError.getMessage().startsWith("ReferenceError") ) {
throw new BPjsCodeEvaluationException(jsError);
} else {
throw new BPjsRuntimeException("JavaScript error: " + jsError.getMessage(), jsError);
}
} catch ( Throwable generalThrowable ) {
System.err.println("BPjs Error: Unhandled exception in BPEngineTask.");
System.err.println(" This is a bug in BPjs. Please report. Sorry.");
generalThrowable.printStackTrace( System.err );
throw generalThrowable;
} finally {
Context.exit();
BProgramJsProxy.clearCurrentBThread();
}
}
/**
* Handle a captures continuation. This can be because of a sync statement,
* or because of a fork.
* @param cbs
* @param jsContext
* @return Snapshot for the continued execution of the parent.
* @throws IllegalStateException
*/
private BThreadSyncSnapshot handleContinuationPending(ContinuationPending cbs, Context jsContext) throws IllegalStateException {
final Object capturedStatement = cbs.getApplicationState();
if ( capturedStatement instanceof CapturedBThreadState ) {
final CapturedBThreadState capturedState = (CapturedBThreadState) cbs.getApplicationState();
SyncStatement syncStatement = capturedState.syncStmt;
// warn on self-blocking
boolean hasRequest = ! syncStatement.getRequest().isEmpty();
boolean hasBlock = (syncStatement.getBlock() != EventSets.none );
if ( hasRequest && hasBlock ) {
boolean hasCollision = syncStatement.getRequest().stream().allMatch(syncStatement.getBlock()::contains);
if ( hasCollision ) {
System.err.println("Warning: B-thread '"+btss.getName()+"' is blocking an event it is also requesting, this may lead to a deadlock.");
}
}
return btss.makeNext(cbs.getContinuation(), syncStatement, capturedState.modifications);
} else if ( capturedStatement instanceof ForkStatement ) {
ForkStatement forkStmt = (ForkStatement) capturedStatement;
forkStmt.setForkingBThread(btss);
forkStmt.cloneBThreadData(bpss); // and then there were two
listener.addFork(forkStmt);
return continueParentOfFork(cbs, jsContext);
} else {
throw new IllegalStateException("Captured a statement of an unknown type: " + capturedStatement);
}
}
private BThreadSyncSnapshot continueParentOfFork( ContinuationPending cbs, Context jsContext){
try {
jsContext.resumeContinuation(cbs.getContinuation(),
(Scriptable)cbs.getContinuation(), Undefined.instance);
return null;
} catch ( ContinuationPending cbs2 ) {
return handleContinuationPending(cbs2, jsContext);
} catch ( WrappedException wfae ) {
return handleWrappedException(wfae);
}
}
private BThreadSyncSnapshot handleWrappedException(WrappedException wfae) throws WrappedException {
if ( wfae.getCause() instanceof FailedAssertionException ) {
FailedAssertionException fae = (FailedAssertionException) wfae.getCause();
FailedAssertionViolation fa = new FailedAssertionViolation( fae.getMessage(), btss.getName() );
listener.assertionFailed( fa );
return null;
} else if (wfae.getCause() instanceof BPjsException) {
throw (BPjsException)wfae.getCause();
} else {
throw wfae;
}
}
}