BPjs tips¶
Mainly for commands that are useful, but not in frequent use.
Build jar with tests¶
Useful for running long verifications outside of NetBeans
mvn jar:test-jar
NOTE: The build must use jdk8 for now. Execution can be done on any jdk (at least, worked for us with jdk11).
Running verifications that live in the tests directory from the terminal¶
to run the actual test, also build the uber-jar:
mvn package -P uber-jar
Now both live in the target
directory. You can now run the test
using Java, as usual, with both jars in the -cp
parameter:
java -cp target/BPjs-0.9.2-SNAPSHOT.uber.jar:target/BPjs-0.9.2-SNAPSHOT-tests.jar il.ac.bgu.cs.bp.bpjs.TicTacToe.TicTacToeVerMain
Event comparison¶
Better to use non-strict JavaScript object comparison for now. So prefer
var evt = bp.sync({waitFor:ANY_ADDITION, block:chooseBlock(bias)});
if ( evt.name == ADD_WETS.name ) {
...
over
var evt = bp.sync({waitFor:ANY_ADDITION, block:chooseBlock(bias)});
if ( evt === ADD_WETS ) {
...
as the latter may return a false negative, especially during verification.
Implementing Custom Events and Using Custom Objects as Event Data¶
ALWAYS make sure that you have state-based, meaningful equals()
and
hashCode()
, and that serialization and de-serialization works. Or verification fails.
State minimization¶
(yes, this is informed by the “data minimization” directive of privacy by design) It’s better to store least amount of data. E.g. in the fruitRatio.js file, this version of the b-threads yields three states:
bp.registerBThread( "RaspberryAdder", function(){
var fruitIndex=0;
while (true) {
var evt = null;
if ( fruitIndex > 0 ) {
evt = bp.hot(true).sync({request:ADD_RASPB,
waitFor:ADD_FRUIT});
} else {
evt = bp.sync({waitFor:ADD_FRUIT});
}
fruitIndex = fruitIndex +
evt.data.blueberries-evt.data.raspberries;
}
});
Where this version yields 4 (note different location of var evt
) :
bp.registerBThread( "RaspberryAdder", function(){
var fruitIndex=0;
var evt = null;
while (true) {
if ( fruitIndex > 0 ) {
evt = bp.hot(true).sync({request:ADD_RASPB,
waitFor:ADD_FRUIT});
} else {
evt = bp.sync({waitFor:ADD_FRUIT});
}
fruitIndex = fruitIndex +
evt.data.blueberries-evt.data.raspberries;
}
});
That’s because the former does not store the event from the previous iteration.
Variables Defined in Loops¶
When defining variables in loops, prefer let
to const
. This is because const
won’t
change after the first iteration, and the assignment attempt will fail silently.
E.g. :
for ( let i=0; i<10; i++ ) {
let evtName = bp.thread.data.eventPrefix + String(i);
req(evtName); // convenience function for bp.sync({request... (non-standard)
}
Will yield a series of events event-0
to event-9
, whereas:
for ( let i=0; i<10; i++ ) {
const evtName = bp.thread.data.eventPrefix + String(i);
req(evtName); // convenience function for bp.sync({request... (non-standard)
}
Will yield a series of 10 events named event-0
.
Working with Java Strings¶
Java strings are a bit different from JavaScript strings – they are equal, but not strictly equal. For example, say that j
is a java.lang.String
, and n
is a JavaScript string, we get j==n
but not j===n
.
BPjs uses wrapping to iron out these inequalities, which can turn ugly when strings do a round-trip from JavaScript to Java and back. The only case in which BPjs programmers need to worry about this is in when directly instantiating Java strings in JS code (see example below).
var jString = java.lang.String("hello"); // Java String
var jsString = "hello"; // JavaScript String
var list = java.util.ArrayList();
list.add(jString);
var rString = list.get(0); // Java String that have done a round-trip
bp.log.info( jString == jsString ); // true
bp.log.info( jString === jsString ); // false
bp.log.info( rString == jsString ); // true
bp.log.info( rString === jsString ); // true
Working in Java with Objects from JavaScript¶
When invoking methods on objects that come from Rhino, it is often required that these methods
are invoked in a context. This context can be obtained by calling BPjs.enterRhinoContext()
.
However, this method requires the client code to ensure that the context is closed properly.
An alternative would be to use the consumer pattern, with BPjs.withContext()
:
BProgram bprog = createBProgram(); // create b-program
bprog.setup(); // run initial part
JsEventSet es = bprog.getFromGlobalScope("esA", JsEventSet.class).get();
// es now holds a JsEventSet, which includes a Rhino function,
// and so, must be run within a context.
BPjs.withContext((c)->{
assertTrue( es.contains(BEvent.named("AAA")));
});