Skip to content

Commit 3e4c47e

Browse files
committed
Improved JS error detection during DFS traversal
1 parent 54a2581 commit 3e4c47e

File tree

13 files changed

+284
-217
lines changed

13 files changed

+284
-217
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,15 @@ If you use BPjs in an academic work, please consider citing it as:
5454

5555

5656
### 2022-02 (Including the 1st BP Day Hackathon)
57+
* :part_alternation_mark: Calling `bp.sync` from global scope during runtime does not cause an ugly exception anymore ([#174](https://github.com/bThink-BGU/BPjs/issues/174)). The error is reported to the listeners.
58+
* Also when making the illegal call from an interrupt handler.
59+
* :arrow_up: Improved JavaScript code error detection during analysis -
5760
* :arrow_up: Improvements to handling `null`s in event set arrays ([#178](https://github.com/bThink-BGU/BPjs/issues/178)).
5861
* :arrow_up: Improvements to handling Rhino context in BPjs class ([#176](https://github.com/bThink-BGU/BPjs/issues/176)).
5962
* :arrow_up: Improvements to the `MapProxy` classes, allowing client code pre-process changes.
6063
* :part_alternation_mark: `BEvent::getDataField` data accessors wrap the lower-level `getData()` ([#161](https://github.com/bThink-BGU/BPjs/issues/161)).
6164
* :arrow_up: `JsEventSet` now honors the event set name during equality checks.
65+
* :put_litter_in_its_place: Consolidated tests for `BProgramJsProxy`.
6266

6367
### 2022-01
6468
* :arrow_up: Improvements to the `BEvent` class, to make it better support client Java code.

src/main/java/il/ac/bgu/cs/bp/bpjs/analysis/DfsBProgramVerifier.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,10 @@ public VerificationResult verify(BProgram aBp) throws Exception {
170170
ExecutorService execSvc = BPjs.getExecutorServiceMaker().makeWithName("DfsBProgramRunner-" + INSTANCE_COUNTER.incrementAndGet());
171171
long start = System.currentTimeMillis();
172172
listener.started(this);
173-
Violation vio = dfsUsingStack(new DfsTraversalNode(currentBProgram, currentBProgram.setup().start(execSvc, currentBProgram.getStorageModificationStrategy()), null), execSvc);
173+
Violation vio = dfsUsingStack(new DfsTraversalNode(currentBProgram,
174+
currentBProgram.setup().start(execSvc, currentBProgram.getStorageModificationStrategy()), null),
175+
execSvc
176+
);
174177
long end = System.currentTimeMillis();
175178
execSvc.shutdown();
176179
listener.done(this);

src/main/java/il/ac/bgu/cs/bp/bpjs/analysis/DfsTraversalNode.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.util.*;
77

88
import il.ac.bgu.cs.bp.bpjs.execution.listeners.BProgramRunnerListener;
9+
import il.ac.bgu.cs.bp.bpjs.execution.listeners.BProgramRunnerListenerAdapter;
910
import il.ac.bgu.cs.bp.bpjs.model.BProgram;
1011
import il.ac.bgu.cs.bp.bpjs.model.BProgramSyncSnapshot;
1112
import il.ac.bgu.cs.bp.bpjs.model.BEvent;
@@ -89,8 +90,26 @@ public String toString() {
8990
*
9091
*/
9192
public DfsTraversalNode getNextNode(BEvent e, ExecutorService exSvc) throws BPjsRuntimeException {
93+
final Exception[] caughtException = new Exception[1];
94+
final BProgramRunnerListener l = new BProgramRunnerListenerAdapter(){
95+
@Override
96+
public void error(BProgram bp, Exception ex) {
97+
caughtException[0] = ex;
98+
}
99+
100+
};
92101
try {
93-
return new DfsTraversalNode(bp, BProgramSyncSnapshotCloner.clone(systemState).triggerEvent(e, exSvc, Collections.emptyList(), bp.getStorageModificationStrategy()), e);
102+
DfsTraversalNode next = new DfsTraversalNode(bp,
103+
BProgramSyncSnapshotCloner.clone(systemState).triggerEvent(e, exSvc, Collections.singletonList(l),bp.getStorageModificationStrategy()), e);
104+
if ( caughtException[0] != null ) {
105+
if ( caughtException[0] instanceof BPjsRuntimeException ){
106+
throw (BPjsRuntimeException)(caughtException[0]);
107+
} else {
108+
throw new BPjsRuntimeException("Exception while reaching for a new node", caughtException[0]);
109+
}
110+
}
111+
return next;
112+
94113
} catch ( InterruptedException ie ) {
95114
throw new BPjsRuntimeException("Thread interrupted during event invocaiton", ie);
96115
}

src/main/java/il/ac/bgu/cs/bp/bpjs/execution/BProgramRunner.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,11 @@ public BProgramRunner(BProgram aBProgram) {
6868
bprog.setAddBThreadCallback( (bp,bt)->listeners.forEach(l->l.bthreadAdded(bp, bt)));
6969
}
7070
}
71-
71+
/**
72+
* Kicks off the execution of the {@link BProgram}. This method does not
73+
* {@code return} or {@code throw} anything, as it is part or the {@link Runnable}
74+
* interface. Errors and results are reported to the listeners.
75+
*/
7276
@Override
7377
public void run() {
7478
try {

src/main/java/il/ac/bgu/cs/bp/bpjs/execution/jsproxy/BProgramJsProxy.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,11 @@ public SyncStatementBuilder hot(boolean isHot) {
234234
*/
235235
@Override
236236
void synchronizationPoint(NativeObject jsRWB, Boolean hot, Object data) {
237+
238+
if ( CURRENT_BTHREAD.get() == null ) {
239+
throw new BPjsRuntimeException("Calling bp.sync outside of a b-thread is forbidden");
240+
}
241+
237242
Map<String, Object> jRWB = (Map) Context.jsToJava(jsRWB, Map.class);
238243

239244
SyncStatement stmt = SyncStatement.make();
@@ -252,7 +257,9 @@ void synchronizationPoint(NativeObject jsRWB, Boolean hot, Object data) {
252257
stmt = stmt.request((BEvent) req);
253258
}
254259
} catch (ClassCastException cce) {
255-
throw new BPjsRuntimeException("A non-event object requested in a sync statement. Offending object:'" + ScriptableUtils.stringify(req) + "'");
260+
throw new BPjsRuntimeException("A non-event object requested in a sync statement. "
261+
+ "Offending object:'" + ScriptableUtils.stringify(req) + "' in BThread " +
262+
CURRENT_BTHREAD.get().snapshot.getName());
256263
}
257264
}
258265

src/main/java/il/ac/bgu/cs/bp/bpjs/model/BProgram.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -226,12 +226,7 @@ protected Object evaluate(String script, String scriptName, Context curCtx) {
226226
} catch (BPjsException be ) {
227227
throw be;
228228
} catch ( IllegalStateException ise ) {
229-
String msg = ise.getMessage();
230-
if ( msg.contains("Cannot capture continuation") && msg.contains("executeScriptWithContinuations or callFunctionWithContinuations") ){
231-
throw new BPjsCodeEvaluationException("bp.sync called outside of a b-thread");
232-
} else {
233-
throw ise;
234-
}
229+
throw ise;
235230
} catch ( Throwable generalException ) {
236231
throw new BPjsRuntimeException("(Wrapped) Exception evaluating BProgram code: " + generalException.getMessage(), generalException);
237232
}

src/main/java/il/ac/bgu/cs/bp/bpjs/model/BProgramSyncSnapshot.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,13 @@ private void handleInterrupts(BEvent anEvent, Iterable<BProgramRunnerListener> l
248248
final Scriptable scope = bt.getScope();
249249
try {
250250
ctxt.callFunctionWithContinuations(func, scope, new Object[]{anEvent});
251-
} catch ( ContinuationPending ise ) {
252-
throw new BPjsRuntimeException("Cannot call bp.sync or fork from a break-upon handler. Please consider pushing an external event.");
251+
} catch ( BPjsRuntimeException e) {
252+
listeners.forEach( l -> l.error(bprog, e));
253+
} catch ( WrappedException wpe ) {
254+
if ( wpe.getWrappedException() instanceof BPjsRuntimeException ) {
255+
BPjsRuntimeException e = (BPjsRuntimeException) wpe.getWrappedException();
256+
listeners.forEach( l -> l.error(bprog, e));
257+
}
253258
}
254259
});
255260
});

src/test/java/il/ac/bgu/cs/bp/bpjs/execution/BpProxyFunctionalityTest.java

Lines changed: 0 additions & 190 deletions
This file was deleted.

src/test/java/il/ac/bgu/cs/bp/bpjs/execution/EngineExceptionTest.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
import il.ac.bgu.cs.bp.bpjs.model.BProgram;
44
import il.ac.bgu.cs.bp.bpjs.exceptions.BPjsCodeEvaluationException;
5+
import il.ac.bgu.cs.bp.bpjs.execution.listeners.BProgramRunnerListenerAdapter;
56
import il.ac.bgu.cs.bp.bpjs.model.StringBProgram;
7+
import java.util.concurrent.atomic.AtomicBoolean;
68
import static org.junit.Assert.assertEquals;
79
import static org.junit.Assert.assertTrue;
810
import static org.junit.Assert.fail;
@@ -22,14 +24,17 @@ public void testInvalidBSyncCall() throws InterruptedException {
2224
"var i=0;\n var j=42;\n var k=5; bp.sync({request:bp.Event(\"A\")});"
2325
);
2426

25-
try {
26-
new BProgramRunner(sut).run();
27-
fail("System should have thrown an error due to bp.sync called outside of a BThread.");
28-
} catch (BPjsCodeEvaluationException exp) {
29-
assertTrue( exp.getMessage().contains("bp.sync"));
30-
System.out.println("Error message details: ");
31-
System.out.println(exp.getDetails());
32-
}
27+
final AtomicBoolean errorCalled = new AtomicBoolean(false);
28+
final BProgramRunner bProgramRunner = new BProgramRunner(sut);
29+
bProgramRunner.addListener( new BProgramRunnerListenerAdapter(){
30+
@Override
31+
public void error(BProgram bp, Exception ex) {
32+
errorCalled.set(true);
33+
}
34+
35+
});
36+
bProgramRunner.run();
37+
assertTrue("The runner listener should have had its 'error' method called ", errorCalled.get() );
3338
}
3439

3540
@Test

0 commit comments

Comments
 (0)