diff --git a/js/compiler/compiler.js b/js/compiler/compiler.js index 046d6f5..f674ace 100644 --- a/js/compiler/compiler.js +++ b/js/compiler/compiler.js @@ -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); } }); diff --git a/js/compiler/demo-during-criterion-snapshotting.js b/js/compiler/demo-during-criterion-snapshotting.js new file mode 100644 index 0000000..032f9af --- /dev/null +++ b/js/compiler/demo-during-criterion-snapshotting.js @@ -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); + } + } + } + } +} diff --git a/racket/syndicate/actor.rkt b/racket/syndicate/actor.rkt index 9ed32f0..ec58243 100644 --- a/racket/syndicate/actor.rkt +++ b/racket/syndicate/actor.rkt @@ -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) diff --git a/racket/syndicate/examples/actor/example-during-criterion-snapshotting.rkt b/racket/syndicate/examples/actor/example-during-criterion-snapshotting.rkt new file mode 100644 index 0000000..262f4a4 --- /dev/null +++ b/racket/syndicate/examples/actor/example-during-criterion-snapshotting.rkt @@ -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)])))