Compute `retracted` pattern just once during `during`. Avoids mutation-related bug.

This commit is contained in:
Tony Garnock-Jones 2016-06-11 06:34:34 -04:00
parent 7e144ab33b
commit 0b964bb1bb
4 changed files with 95 additions and 12 deletions

View File

@ -152,7 +152,7 @@ var modifiedSourceActions = {
},
FacetSituation_assert: function(_assert, expr, whenClause, _sc) {
return '\n.addAssertion(' + buildSubscription([expr], 'assert', 'pattern', whenClause) + ')';
return '\n.addAssertion(' + buildSubscription([expr], 'assert', 'pattern', whenClause, null) + ')';
},
FacetSituation_event: function(_on, eventPattern, block) {
return buildOnEvent(false,
@ -166,18 +166,20 @@ var modifiedSourceActions = {
return '\n.addOnEventHandler((function(' + id.asES5 + ') ' + block.asES5 + '))';
},
FacetSituation_during: function(_during, pattern, facetBlock) {
var cachedAssertionVar = gensym('cachedAssertion');
return buildOnEvent(false,
'asserted',
pattern.subscription,
pattern.projection,
pattern.bindings,
'{ ' + facetBlock.facetVarDecls +
'\nvar '+cachedAssertionVar+' = '+pattern.instantiatedAssertion+';'+
'\nSyndicate.Actor.createFacet()' +
facetBlock.asES5 +
buildOnEvent(true,
'retracted',
pattern.instantiatedSubscription,
pattern.instantiatedProjection,
pattern.instantiatedSubscription(cachedAssertionVar),
pattern.instantiatedProjection(cachedAssertionVar),
[],
'{}') +
'.completeBuild(); }');
@ -221,7 +223,7 @@ semantics.addAttribute('eventType', {
FacetTransitionEventPattern_risingEdge: function (_lp, expr, _rp) { return 'risingEdge'; }
});
function buildSubscription(children, patchMethod, mode, whenClause) {
function buildSubscription(children, patchMethod, mode, whenClause, cachedAssertionVar) {
var fragments = [];
var hasWhenClause = (whenClause && (whenClause.numChildren === 1));
fragments.push('(function() { var _ = Syndicate.__; return ');
@ -233,7 +235,11 @@ function buildSubscription(children, patchMethod, mode, whenClause) {
} else {
fragments.push('{ assertion: ');
}
children.forEach(function (c) { c.buildSubscription(fragments, mode); });
if (cachedAssertionVar) {
fragments.push(cachedAssertionVar);
} else {
children.forEach(function (c) { c.buildSubscription(fragments, mode); });
}
if (patchMethod) {
fragments.push(', ');
} else {
@ -254,25 +260,35 @@ function buildSubscription(children, patchMethod, mode, whenClause) {
semantics.addAttribute('subscription', {
_default: function(children) {
return buildSubscription(children, 'sub', 'pattern', null);
return buildSubscription(children, 'sub', 'pattern', null, null);
}
});
semantics.addAttribute('instantiatedSubscription', {
semantics.addAttribute('instantiatedAssertion', {
_default: function(children) {
return buildSubscription(children, 'sub', 'instantiated', null);
var fragments = [];
fragments.push('(function() { var _ = Syndicate.__; return ');
children.forEach(function (c) { c.buildSubscription(fragments, 'instantiated'); });
fragments.push('; })()');
return fragments.join('');
}
});
semantics.addAttribute('instantiatedProjection', {
semantics.addOperation('instantiatedSubscription(cachedAssertionVar)', {
_default: function(children) {
return buildSubscription(children, null, 'instantiated', null);
return buildSubscription(children, 'sub', 'instantiated', null, this.args.cachedAssertionVar);
}
});
semantics.addOperation('instantiatedProjection(cachedAssertionVar)', {
_default: function(children) {
return buildSubscription(children, null, 'instantiated', null, this.args.cachedAssertionVar);
}
});
semantics.addAttribute('projection', {
_default: function(children) {
return buildSubscription(children, null, 'projection', null);
return buildSubscription(children, null, 'projection', null, null);
}
});

View File

@ -0,0 +1,40 @@
// Illustrates a (now fixed) bug where mutation altering a
// subscription caused the `retracted` half of a during instance to be
// lost.
//
// Symptomatic output:
// x= 123 v= 999
// x= 124 v= 999
//
// Correct output:
// x= 123 v= 999
// finally for x= 124 v= 999
// x= 124 v= 999
//
// Should eventually be turned into some kind of test case.
var Syndicate = require('./src/main.js');
var Dataspace = Syndicate.Dataspace;
assertion type foo(x, y);
ground dataspace {
actor {
var x = 123;
react {
assert foo(x, 999);
during foo(x, $v) {
do {
console.log('x=', x, 'v=', v);
if (x === 123) {
x = 124;
}
}
finally {
console.log('finally for x=', x, 'v=', v);
}
}
}
}
}

View File

@ -624,7 +624,7 @@
(define (analyze-during! index P-stx O-stxs)
(define E-stx #`(asserted #,P-stx))
(define-values (_proj _pat _bindings instantiated) (analyze-pattern E-stx P-stx))
(define I-stx #`(until (retracted #,instantiated) #,@O-stxs))
(define I-stx #`(let ((p #,instantiated)) (until (retracted p) #,@O-stxs)))
(analyze-event! index E-stx #`(#,I-stx)))
(define (analyze-assertion! index Pred-stx outer-expr-stx P-stx L-stx)

View File

@ -0,0 +1,27 @@
#lang syndicate
;; Illustrates a (now fixed) bug where mutation altering a
;; subscription caused the `retracted` half of a during instance to be
;; lost.
;;
;; Symptomatic output:
;; x=123 v=999
;; x=124 v=999
;;
;; Correct output:
;; x=123 v=999
;; finally for x=124 v=999
;; x=124 v=999
;;
;; Should eventually be turned into some kind of test case.
(require syndicate/actor)
(struct foo (x y) #:prefab)
(actor (define x 123)
(forever
(assert (foo x 999))
(during (foo x $v)
#:init [(log-info "x=~a v=~a" x v)
(when (= x 123) (set! x 124))]
#:done [(log-info "finally for x=~a v=~a" x v)])))