X-Git-Url: https://git.distorted.org.uk/~mdw/dep-ui/blobdiff_plain/ac26861cd2097171f4f61adf7277af93c7094b16..f65560a6585a8369f9fd95c66f74d74b2adf5b04:/dep.js diff --git a/dep.js b/dep.js index 4376b89..f4b0ff9 100644 --- a/dep.js +++ b/dep.js @@ -21,11 +21,11 @@ * along with this program; if not, see . */ -var DEP = { }; (function () { with (DEP) { +var DEP = { }; (function () { /*----- Utility functions and classes -------------------------------------*/ -DEP.dolist = function (list, func) { +function dolist(list, func) { /* Apply FUNC to each element of LIST, discarding the results. * * JavaScript's looping iterates over indices rather than values, which is @@ -36,6 +36,7 @@ DEP.dolist = function (list, func) { var i; for (i in list) func(list[i]); } +DEP.dolist = dolist; function eql(x, y) { /* A standard equality function, suitable for detecting changes to `Dep' @@ -46,22 +47,51 @@ function eql(x, y) { return x === y; } +function try_finally(trythunk, finalthunk) { + /* Call the TRYTHUNK. When TRYTHUNK exits (even if it's with an exception) + * then call FINALTHUNK. If TRYTHUNK returned normally then return its + * value; otherwise continue with the non-local flow control. + * + * This is an unpleasant hack because V8 doesn't optimize functions which + * contain try/finally very well (despite this being an easy transformation + * for the compiler). + */ + + try { return trythunk(); } + finally { finalthunk(); } +} + +function try_cleanup(trythunk, cleanthunk) { + /* Call the TRYTHUNK. If TRYTHUNK returns normally, just return its value; + * otherwise call CLEANTHUNK and re-throw the exception. + * + * This is an unpleasant hack because V8 doesn't optimize functions which + * contain try/catch very well (despite this being an easy transformation + * for the compiler). + */ + + var e; + try { return trythunk(); } + catch (e) { cleanthunk(); throw e; } +} + /* A Tag is an object which is interesting only because of its identity, and * that the set of Tags is basically determined by the static structure of * the program. */ -DEP.Tag = function (what) { +function Tag(what) { this.what = what; } Tag.prototype = { toString: function () { return '#{Tag ' + this.what + '}'; } } +DEP.Tag = Tag; /* A Generation is like a Tag, except that it's expected that a program will * manufacture Generations repeatedly, and perhaps use them to determine * whether an object is `up-to-date' in some sense. */ -DEP.Generation = function (what) { +function Generation(what) { this.what = what; this.seq = Generation._next++; } @@ -95,8 +125,11 @@ var PENDING = []; // Deps awaiting recomputation. /*----- Exceptions --------------------------------------------------------*/ -DEP.CircularDependency = new Tag('CircularDependency'); -DEP.BusyRecomputing = new Tag('BusyRecomputing'); +CircularDependency = new Tag('CircularDependency'); +DEP.CircularDependency = CircularDependency; + +BusyRecomputing = new Tag('BusyRecomputing'); +DEP.BusyRecomputing = BusyRecomputing; /*----- Main code ---------------------------------------------------------*/ @@ -112,7 +145,7 @@ DEP.BusyRecomputing = new Tag('BusyRecomputing'); * causes that `Dep' to become bad in turn. */ -DEP.Dep = function (value, maybefunc) { +function Dep(value, maybefunc) { /* Initialize a new `Dep' object. * * Handling of the arguments is a little fiddly. If two arguments are @@ -177,6 +210,7 @@ DEP.Dep = function (value, maybefunc) { } } +DEP.Dep = Dep; Dep._seq = 0; // Next sequence number to allocate. Dep.prototype = { @@ -253,20 +287,17 @@ Dep.prototype = { */ var old = EVALUATING; - var val, e; + var me = this; + var val; this._dependencies = { }; - try { - EVALUATING = this; - try { - return this._value_function(); - } catch (e) { - if (e === BAD) return BAD; - else throw e; - } - } finally { + return try_finally(function () { + EVALUATING = me; + return orelse(function () { return me._value_function(); }, + function () { return BAD; }); + }, function() { EVALUATING = old; - } + }); }, _propagate: function () { @@ -302,7 +333,6 @@ Dep.prototype = { */ var queued = this.__flags & F_QUEUED; - var e; var me = this; // Update us with the given VALUE. @@ -319,12 +349,8 @@ Dep.prototype = { // Try to recompute our value. If that doesn't work then mark us as bad // and propagate that. - try { - return update(this._new_value()); - } catch (e) { - update(BAD); - throw e; - } + return try_cleanup(function () { return update(me._new_value()); }, + function () { update(BAD); }); }, _force: function () { @@ -416,7 +442,7 @@ Dep.prototype = { } }; -DEP.orelse = function (thunk, errthunk) { +function orelse(thunk, errthunk) { /* Call THUNK. If it succeeds, then return its result. If THUNK * reads a bad dep then call ERRTHINK and return its result instead. */ @@ -428,11 +454,13 @@ DEP.orelse = function (thunk, errthunk) { else throw e; } } +DEP.orelse = orelse; -DEP.bad = function () { +function bad() { /* For use in a value-function: make the dep be bad. */ throw BAD; } +DEP.bad = bad; function recompute_pending() { /* Recompute any pending dependencies. @@ -443,7 +471,7 @@ function recompute_pending() { var d, f; state = 'recomputing'; - try { + try_finally(function () { while (PENDING.length) { d = PENDING.shift(); f = d.__flags; @@ -455,15 +483,15 @@ function recompute_pending() { d.__flags = f | F_DEPS; } } - } finally { + }, function () { while (PENDING.length) { d = PENDING.shift(); d._value = BAD; } - } + }); } -DEP.with_frozen = function (body, delay) { +function with_frozen(body, delay) { /* Call BODY with dependencies frozen. * * If the BODY function changes any dep values (using D.set_value(...)) @@ -489,7 +517,7 @@ DEP.with_frozen = function (body, delay) { case 'ready': old_delayed = DELAYED; old_pending = PENDING; - try { + try_finally(function () { DELAYED = []; PENDING = []; GENERATION = new Generation('dep-generation'); @@ -500,13 +528,14 @@ DEP.with_frozen = function (body, delay) { op = DELAYED.shift(); op(); } - } finally { + }, function () { DELAYED = old_delayed; PENDING = old_pending; - } + }); break; } } +DEP.with_frozen = with_frozen; /*----- That's all, folks -------------------------------------------------*/ -} })(); +})();