Merge branch 'with-exclusive-lock'
Conflicts: js-assembler/runtime-src/runtime.js version.rkt web-world/main.rkt
This commit is contained in:
commit
976b6112f1
|
@ -128,13 +128,22 @@
|
||||||
|
|
||||||
var coerseClosureToJavaScript = function (v, MACHINE) {
|
var coerseClosureToJavaScript = function (v, MACHINE) {
|
||||||
var f = function (succ, fail) {
|
var f = function (succ, fail) {
|
||||||
|
var args = [];
|
||||||
|
var i;
|
||||||
|
for (i = 0; i < arguments.length; i++) {
|
||||||
|
args.push(arguments[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
MACHINE.exclusiveLock.acquire(
|
||||||
|
"js-as-closure",
|
||||||
|
function(releaseLock) {
|
||||||
succ = succ || function () {};
|
succ = succ || function () {};
|
||||||
fail = fail || function () {};
|
fail = fail || function () {};
|
||||||
|
if (!(baselib.arity.isArityMatching(v.racketArity, args.length - 2))) {
|
||||||
if (!(baselib.arity.isArityMatching(v.racketArity, arguments.length - 2))) {
|
|
||||||
var msg = baselib.format.format(
|
var msg = baselib.format.format(
|
||||||
"arity mismatch: ~s expected ~s argument(s) but received ~s",
|
"arity mismatch: ~s expected ~s argument(s) but received ~s",
|
||||||
[v.displayName, v.racketArity, arguments.length - 2]);
|
[v.displayName, v.racketArity, args.length - 2]);
|
||||||
|
releaseLock();
|
||||||
return fail(new baselib.exceptions.RacketError(
|
return fail(new baselib.exceptions.RacketError(
|
||||||
msg,
|
msg,
|
||||||
baselib.exceptions.makeExnFailContractArity(msg,
|
baselib.exceptions.makeExnFailContractArity(msg,
|
||||||
|
@ -144,8 +153,8 @@
|
||||||
var oldVal = MACHINE.v;
|
var oldVal = MACHINE.v;
|
||||||
var oldArgcount = MACHINE.a;
|
var oldArgcount = MACHINE.a;
|
||||||
var oldProc = MACHINE.p;
|
var oldProc = MACHINE.p;
|
||||||
|
|
||||||
var oldErrorHandler = MACHINE.params['currentErrorHandler'];
|
var oldErrorHandler = MACHINE.params['currentErrorHandler'];
|
||||||
|
|
||||||
var afterGoodInvoke = function (MACHINE) {
|
var afterGoodInvoke = function (MACHINE) {
|
||||||
plt.runtime.PAUSE(
|
plt.runtime.PAUSE(
|
||||||
function (restart) {
|
function (restart) {
|
||||||
|
@ -174,10 +183,10 @@
|
||||||
|
|
||||||
MACHINE.c.push(
|
MACHINE.c.push(
|
||||||
new baselib.frames.CallFrame(afterGoodInvoke, v));
|
new baselib.frames.CallFrame(afterGoodInvoke, v));
|
||||||
MACHINE.a = arguments.length - 2;
|
MACHINE.a = args.length - 2;
|
||||||
var i;
|
var i;
|
||||||
for (i = 0; i < arguments.length - 2; i++) {
|
for (i = 0; i < args.length - 2; i++) {
|
||||||
MACHINE.e.push(arguments[arguments.length - 1 - i]);
|
MACHINE.e.push(args[args.length - 1 - i]);
|
||||||
}
|
}
|
||||||
MACHINE.p = v;
|
MACHINE.p = v;
|
||||||
MACHINE.params['currentErrorHandler'] = function (MACHINE, e) {
|
MACHINE.params['currentErrorHandler'] = function (MACHINE, e) {
|
||||||
|
@ -187,7 +196,9 @@
|
||||||
MACHINE.p = oldProc;
|
MACHINE.p = oldProc;
|
||||||
fail(e);
|
fail(e);
|
||||||
};
|
};
|
||||||
MACHINE.trampoline(v.label);
|
|
||||||
|
MACHINE._trampoline(v.label, false, releaseLock);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
return f;
|
return f;
|
||||||
};
|
};
|
||||||
|
@ -214,21 +225,39 @@
|
||||||
|
|
||||||
|
|
||||||
// internallCallDuringPause: call a Racket procedure and get its results.
|
// internallCallDuringPause: call a Racket procedure and get its results.
|
||||||
// The use assumes the machine is in a running-but-paused state.
|
// The use assumes the machine is in a running-but-paused state, where the
|
||||||
|
// lock is still in effect. The lock will continue to be in effect
|
||||||
|
// after coming back from the internal call.
|
||||||
var internalCallDuringPause = function (MACHINE, proc, success, fail) {
|
var internalCallDuringPause = function (MACHINE, proc, success, fail) {
|
||||||
|
var args = [];
|
||||||
var i;
|
var i;
|
||||||
var oldArgcount, oldVal, oldProc, oldErrorHandler;
|
for (i = 0; i < arguments.length; i++) {
|
||||||
if (! baselib.arity.isArityMatching(proc.racketArity, arguments.length - 4)) {
|
args.push(arguments[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var i;
|
||||||
|
var oldArgcount, oldVal, oldProc, oldErrorHandler, oldControlLength, oldEnvLength;
|
||||||
|
if (! baselib.arity.isArityMatching(proc.racketArity, args.length - 4)) {
|
||||||
var msg = baselib.format.format("arity mismatch: ~s expected ~s arguments, but received ~s",
|
var msg = baselib.format.format("arity mismatch: ~s expected ~s arguments, but received ~s",
|
||||||
[proc.displayName, proc.racketArity, arguments.length - 4]);
|
[proc.displayName, proc.racketArity, args.length - 4]);
|
||||||
return fail(baselib.exceptions.makeExnFailContractArity(msg,
|
fail(baselib.exceptions.makeExnFailContractArity(msg,
|
||||||
MACHINE.captureContinuationMarks()));
|
MACHINE.captureContinuationMarks()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isClosure(proc)) {
|
if (! isClosure(proc)) {
|
||||||
|
fail(baselib.exceptions.makeExnFail(
|
||||||
|
baselib.format.format(
|
||||||
|
"Not a procedure: ~e",
|
||||||
|
proc),
|
||||||
|
MACHINE.captureContinuationMarks()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
oldVal = MACHINE.v;
|
oldVal = MACHINE.v;
|
||||||
oldArgcount = MACHINE.a;
|
oldArgcount = MACHINE.a;
|
||||||
oldProc = MACHINE.p;
|
oldProc = MACHINE.p;
|
||||||
|
oldControlLength = MACHINE.c.length;
|
||||||
|
oldEnvLength = MACHINE.e.length;
|
||||||
|
|
||||||
oldErrorHandler = MACHINE.params['currentErrorHandler'];
|
oldErrorHandler = MACHINE.params['currentErrorHandler'];
|
||||||
var afterGoodInvoke = function (MACHINE) {
|
var afterGoodInvoke = function (MACHINE) {
|
||||||
|
@ -258,9 +287,9 @@
|
||||||
|
|
||||||
MACHINE.c.push(
|
MACHINE.c.push(
|
||||||
new baselib.frames.CallFrame(afterGoodInvoke, proc));
|
new baselib.frames.CallFrame(afterGoodInvoke, proc));
|
||||||
MACHINE.a = arguments.length - 4;
|
MACHINE.a = args.length - 4;
|
||||||
for (i = 0; i < arguments.length - 4; i++) {
|
for (i = 0; i < args.length - 4; i++) {
|
||||||
MACHINE.e.push(arguments[arguments.length - 1 - i]);
|
MACHINE.e.push(args[args.length - 1 - i]);
|
||||||
}
|
}
|
||||||
MACHINE.p = proc;
|
MACHINE.p = proc;
|
||||||
MACHINE.params['currentErrorHandler'] = function (MACHINE, e) {
|
MACHINE.params['currentErrorHandler'] = function (MACHINE, e) {
|
||||||
|
@ -268,16 +297,16 @@
|
||||||
MACHINE.v = oldVal;
|
MACHINE.v = oldVal;
|
||||||
MACHINE.a = oldArgcount;
|
MACHINE.a = oldArgcount;
|
||||||
MACHINE.p = oldProc;
|
MACHINE.p = oldProc;
|
||||||
|
MACHINE.c.length = oldControlLength;
|
||||||
|
MACHINE.e.length = oldEnvLength;
|
||||||
fail(e);
|
fail(e);
|
||||||
};
|
};
|
||||||
MACHINE.trampoline(proc.label);
|
MACHINE._trampoline(proc.label,
|
||||||
} else {
|
false,
|
||||||
fail(baselib.exceptions.makeExnFail(
|
function() {
|
||||||
baselib.format.format(
|
// The lock should still being held, so we don't
|
||||||
"Not a procedure: ~e",
|
// automatically unlock control.
|
||||||
proc),
|
});
|
||||||
MACHINE.captureContinuationMarks()));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -286,7 +315,6 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var makeClosure = function (name, arity, f, closureArgs) {
|
var makeClosure = function (name, arity, f, closureArgs) {
|
||||||
if (! closureArgs) { closureArgs = []; }
|
if (! closureArgs) { closureArgs = []; }
|
||||||
return new Closure(f,
|
return new Closure(f,
|
||||||
|
|
|
@ -62,15 +62,24 @@
|
||||||
if (this.isInvoked) {
|
if (this.isInvoked) {
|
||||||
succ();
|
succ();
|
||||||
} else {
|
} else {
|
||||||
|
if (isInternal) {
|
||||||
MACHINE.params['currentErrorHandler'] = function (MACHINE, anError) {
|
MACHINE.params['currentErrorHandler'] = function (MACHINE, anError) {
|
||||||
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||||
fail(MACHINE, anError);
|
fail(MACHINE, anError);
|
||||||
};
|
};
|
||||||
MACHINE.c.push(new plt.baselib.frames.CallFrame(afterGoodInvoke, null));
|
MACHINE.c.push(new plt.baselib.frames.CallFrame(afterGoodInvoke, null));
|
||||||
if (isInternal) {
|
|
||||||
throw that.label;
|
throw that.label;
|
||||||
} else {
|
} else {
|
||||||
MACHINE.trampoline(that.label);
|
MACHINE.exclusiveLock.acquire(
|
||||||
|
undefined,
|
||||||
|
function(release) {
|
||||||
|
MACHINE.params['currentErrorHandler'] = function (MACHINE, anError) {
|
||||||
|
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||||
|
fail(MACHINE, anError);
|
||||||
|
};
|
||||||
|
MACHINE.c.push(new plt.baselib.frames.CallFrame(afterGoodInvoke, null));
|
||||||
|
MACHINE._trampoline(that.label, false, release);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -161,6 +161,76 @@
|
||||||
defaultCurrentPrintImplementation);
|
defaultCurrentPrintImplementation);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Exclusive Locks. Even though JavaScript is a single-threaded
|
||||||
|
// evaluator, we still have a need to create exclusive regions
|
||||||
|
// of evaluation, since we might inadvertantly access some state
|
||||||
|
// with two computations, with use of setTimeout.
|
||||||
|
var ExclusiveLock = function() {
|
||||||
|
this.locked = false; // (U false string)
|
||||||
|
this.waiters = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
// makeRandomNonce: -> string
|
||||||
|
// Creates a randomly-generated nonce.
|
||||||
|
ExclusiveLock.makeRandomNonce = function() {
|
||||||
|
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
|
||||||
|
var LEN = 32;
|
||||||
|
var result = [];
|
||||||
|
var i;
|
||||||
|
for (i = 0; i < LEN; i++) {
|
||||||
|
result.push(chars.charAt(Math.floor(Math.random() * chars.length)));
|
||||||
|
}
|
||||||
|
return result.join('');
|
||||||
|
};
|
||||||
|
|
||||||
|
ExclusiveLock.prototype.acquire = function(id, onAcquire) {
|
||||||
|
var that = this;
|
||||||
|
if (!id) {
|
||||||
|
id = ExclusiveLock.makeRandomNonce();
|
||||||
|
}
|
||||||
|
|
||||||
|
var alreadyReleased = false;
|
||||||
|
|
||||||
|
if (this.locked === false) {
|
||||||
|
this.locked = id;
|
||||||
|
onAcquire.call(
|
||||||
|
that,
|
||||||
|
// releaseLock
|
||||||
|
function() {
|
||||||
|
var waiter;
|
||||||
|
if (alreadyReleased) {
|
||||||
|
throw new Error(
|
||||||
|
"Internal error: trying to release the lock, but already released");
|
||||||
|
}
|
||||||
|
if (that.locked === false) {
|
||||||
|
throw new Error(
|
||||||
|
"Internal error: trying to unlock the lock, but already unlocked");
|
||||||
|
}
|
||||||
|
that.locked = false;
|
||||||
|
alreadyReleased = true;
|
||||||
|
if (that.waiters.length > 0) {
|
||||||
|
waiter = that.waiters.shift();
|
||||||
|
setTimeout(
|
||||||
|
function() {
|
||||||
|
that.acquire(waiter.id, waiter.onAcquire);
|
||||||
|
},
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.waiters.push({ id: id,
|
||||||
|
onAcquire: onAcquire } );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////]
|
//////////////////////////////////////////////////////////////////////]
|
||||||
// The MACHINE
|
// The MACHINE
|
||||||
|
|
||||||
|
@ -229,6 +299,7 @@
|
||||||
|
|
||||||
};
|
};
|
||||||
this.primitives = Primitives;
|
this.primitives = Primitives;
|
||||||
|
this.exclusiveLock = new ExclusiveLock();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -376,26 +447,27 @@
|
||||||
//
|
//
|
||||||
var recomputeMaxNumBouncesBeforeYield;
|
var recomputeMaxNumBouncesBeforeYield;
|
||||||
|
|
||||||
var scheduleTrampoline = function(MACHINE, f) {
|
var scheduleTrampoline = function(MACHINE, f, release) {
|
||||||
// FIXME: at the moment, the setTimeout is breaking when we get
|
setTimeout(
|
||||||
// a rapid set of events from web-world. We are running into
|
function() {
|
||||||
// a very ugly re-entrancy bug. https://github.com/dyoo/whalesong/issues/70
|
MACHINE._trampoline(f, false, release);
|
||||||
|
},
|
||||||
// setTimeout(
|
0);
|
||||||
// function() {
|
|
||||||
return MACHINE.trampoline(f);
|
|
||||||
// },
|
|
||||||
// 0);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Creates a restarting function, that reschedules f in a context
|
// Creates a restarting function, that reschedules f in a context
|
||||||
// with the old argcount in place.
|
// with the old argcount in place.
|
||||||
// Meant to be used only by the trampoline.
|
// Meant to be used only by the trampoline.
|
||||||
var makeRestartFunction = function(MACHINE) {
|
var makeRestartFunction = function(MACHINE, release, pauseLock) {
|
||||||
var oldArgcount = MACHINE.a;
|
var oldArgcount = MACHINE.a;
|
||||||
return function(f) {
|
return function(f) {
|
||||||
|
pauseLock.acquire(
|
||||||
|
undefined,
|
||||||
|
function(pauseReleaseLock) {
|
||||||
MACHINE.a = oldArgcount;
|
MACHINE.a = oldArgcount;
|
||||||
return scheduleTrampoline(MACHINE, f);
|
MACHINE._trampoline(f, false, release);
|
||||||
|
pauseReleaseLock();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -419,17 +491,34 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// WARNING WARNING WARNING
|
||||||
|
//
|
||||||
|
// Make sure to get an exclusive lock before jumping into trampoline.
|
||||||
|
// Otherwise, Bad Things will happen.
|
||||||
|
//
|
||||||
|
// e.g. machine.lock.acquire('id', function(release) { machine.trampoline... release();});
|
||||||
Machine.prototype.trampoline = function(initialJump, noJumpingOff) {
|
Machine.prototype.trampoline = function(initialJump, noJumpingOff) {
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
that.exclusiveLock.acquire(
|
||||||
|
'trampoline',
|
||||||
|
function(release) {
|
||||||
|
that._trampoline(initialJump, noJumpingOff, release);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Machine.prototype._trampoline = function(initialJump, noJumpingOff, release) {
|
||||||
|
var that = this;
|
||||||
var thunk = initialJump;
|
var thunk = initialJump;
|
||||||
var startTime = (new Date()).valueOf();
|
var startTime = (new Date()).valueOf();
|
||||||
this.cbt = STACK_LIMIT_ESTIMATE;
|
that.cbt = STACK_LIMIT_ESTIMATE;
|
||||||
this.params.numBouncesBeforeYield =
|
that.params.numBouncesBeforeYield =
|
||||||
this.params.maxNumBouncesBeforeYield;
|
that.params.maxNumBouncesBeforeYield;
|
||||||
this.running = true;
|
that.running = true;
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
try {
|
try {
|
||||||
thunk(this);
|
thunk(that);
|
||||||
break;
|
break;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// There are a few kinds of things that can get thrown
|
// There are a few kinds of things that can get thrown
|
||||||
|
@ -454,7 +543,7 @@
|
||||||
// The running flag is set to false.
|
// The running flag is set to false.
|
||||||
if (typeof(e) === 'function') {
|
if (typeof(e) === 'function') {
|
||||||
thunk = e;
|
thunk = e;
|
||||||
this.cbt = STACK_LIMIT_ESTIMATE;
|
that.cbt = STACK_LIMIT_ESTIMATE;
|
||||||
|
|
||||||
|
|
||||||
// If we're running an a model that prohibits
|
// If we're running an a model that prohibits
|
||||||
|
@ -463,36 +552,76 @@
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.params.numBouncesBeforeYield-- < 0) {
|
if (that.params.numBouncesBeforeYield-- < 0) {
|
||||||
recomputeMaxNumBouncesBeforeYield(
|
recomputeMaxNumBouncesBeforeYield(
|
||||||
this,
|
that,
|
||||||
(new Date()).valueOf() - startTime);
|
(new Date()).valueOf() - startTime);
|
||||||
scheduleTrampoline(this, thunk);
|
scheduleTrampoline(that, thunk, release);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (e instanceof Pause) {
|
} else if (e instanceof Pause) {
|
||||||
var restart = makeRestartFunction(this);
|
var pauseLock = new ExclusiveLock();
|
||||||
e.onPause(restart);
|
var oldArgcount = that.a;
|
||||||
|
var restarted = false;
|
||||||
|
var restart = function(f) {
|
||||||
|
pauseLock.acquire(
|
||||||
|
undefined,
|
||||||
|
function(releasePauseLock) {
|
||||||
|
restarted = true;
|
||||||
|
that.a = oldArgcount;
|
||||||
|
that._trampoline(f, false, release);
|
||||||
|
releasePauseLock();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var internalCall = function(proc, success, fail) {
|
||||||
|
var i;
|
||||||
|
if (restarted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var args = [];
|
||||||
|
for (i = 3; i < arguments.length; i++) {
|
||||||
|
args.push(arguments[i]);
|
||||||
|
}
|
||||||
|
pauseLock.acquire(
|
||||||
|
undefined,
|
||||||
|
function(release) {
|
||||||
|
var newSuccess = function() {
|
||||||
|
success.apply(null, arguments);
|
||||||
|
release();
|
||||||
|
};
|
||||||
|
var newFail = function() {
|
||||||
|
fail.apply(null, arguments);
|
||||||
|
release();
|
||||||
|
};
|
||||||
|
baselib.functions.internalCallDuringPause.apply(
|
||||||
|
null, [that, proc, newSuccess, newFail].concat(args));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
e.onPause(restart, internalCall);
|
||||||
return;
|
return;
|
||||||
} else if (e instanceof HaltError) {
|
} else if (e instanceof HaltError) {
|
||||||
this.running = false;
|
that.running = false;
|
||||||
e.onHalt(this);
|
e.onHalt(that);
|
||||||
|
release();
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
// General error condition: just exit out
|
// General error condition: just exit out
|
||||||
// of the trampoline and call the current error handler.
|
// of the trampoline and call the current error handler.
|
||||||
this.running = false;
|
that.running = false;
|
||||||
this.params.currentErrorHandler(this, e);
|
that.params.currentErrorHandler(that, e);
|
||||||
|
release();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.running = false;
|
that.running = false;
|
||||||
var that = this;
|
that.params.currentSuccessHandler(that);
|
||||||
this.params.currentSuccessHandler(this);
|
release();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// recomputeGas: state number -> number
|
// recomputeGas: state number -> number
|
||||||
recomputeMaxNumBouncesBeforeYield = function(MACHINE, observedDelay) {
|
recomputeMaxNumBouncesBeforeYield = function(MACHINE, observedDelay) {
|
||||||
// We'd like to see a delay of DESIRED_DELAY_BETWEEN_BOUNCES so
|
// We'd like to see a delay of DESIRED_DELAY_BETWEEN_BOUNCES so
|
||||||
|
|
|
@ -114,58 +114,74 @@ Prerequisites: at least @link["http://racket-lang.org/"]{Racket
|
||||||
Here are a collection of programs that use the @emph{web-world} library described
|
Here are a collection of programs that use the @emph{web-world} library described
|
||||||
later in this document:
|
later in this document:
|
||||||
@itemize[
|
@itemize[
|
||||||
@item{@link["http://hashcollision.org/whalesong/examples/attr-animation/attr-animation.html"]{attr-animation.html} [@link["http://hashcollision.org/whalesong/examples/attr-animation/attr-animation.rkt"]{src}] Uses @racket[update-view-attr] and @racket[on-tick] to perform a simple color animation.}
|
@item{@link["http://hashcollision.org/whalesong/examples/attr-animation/attr-animation.html"]{attr-animation.html}
|
||||||
|
[@link["http://hashcollision.org/whalesong/examples/attr-animation/attr-animation.rkt"]{src}
|
||||||
|
@link["http://hashcollision.org/whalesong/examples/attr-animation/index.html"]{index.html}
|
||||||
|
@link["http://hashcollision.org/whalesong/examples/attr-animation/style.css"]{style.css}]
|
||||||
|
Uses @racket[update-view-attr] and @racket[on-tick] to perform a simple color animation.}
|
||||||
|
|
||||||
|
|
||||||
@item{@link["http://hashcollision.org/whalesong/examples/boid/boid.html"]{boid.html} [@link["http://hashcollision.org/whalesong/examples/boid/boid.rkt"]{src}] Uses @racket[update-view-css] and @racket[on-tick] to perform an animation of a flock of @link["http://en.wikipedia.org/wiki/Boids"]{boids}.}
|
@item{@link["http://hashcollision.org/whalesong/examples/boid/boid.html"]{boid.html}
|
||||||
|
[@link["http://hashcollision.org/whalesong/examples/boid/boid.rkt"]{src}
|
||||||
|
@link["http://hashcollision.org/whalesong/examples/boid/index.html"]{index.html}] Uses @racket[update-view-css] and @racket[on-tick] to perform an animation of a flock of @link["http://en.wikipedia.org/wiki/Boids"]{boids}.}
|
||||||
|
|
||||||
|
|
||||||
@item{@link["http://hashcollision.org/whalesong/examples/dwarves/dwarves.html"]{dwarves.html}
|
@item{@link["http://hashcollision.org/whalesong/examples/dwarves/dwarves.html"]{dwarves.html}
|
||||||
[@link["http://hashcollision.org/whalesong/examples/dwarves/dwarves.rkt"]{src}]
|
[@link["http://hashcollision.org/whalesong/examples/dwarves/dwarves.rkt"]{src}
|
||||||
|
@link["http://hashcollision.org/whalesong/examples/dwarves/index.html"]{index.html}]
|
||||||
Uses @racket[view-show] and @racket[view-hide] to manipulate a view. Click on a dwarf to make them hide.
|
Uses @racket[view-show] and @racket[view-hide] to manipulate a view. Click on a dwarf to make them hide.
|
||||||
}
|
}
|
||||||
|
|
||||||
@item{@link["http://hashcollision.org/whalesong/examples/dwarves-with-remove/dwarves-with-remove.html"]{dwarves-with-remove.html}
|
@item{@link["http://hashcollision.org/whalesong/examples/dwarves-with-remove/dwarves-with-remove.html"]{dwarves-with-remove.html}
|
||||||
[@link["http://hashcollision.org/whalesong/examples/dwarves-with-remove/dwarves-with-remove.rkt"]{src}]
|
[@link["http://hashcollision.org/whalesong/examples/dwarves-with-remove/dwarves-with-remove.rkt"]{src}
|
||||||
|
@link["http://hashcollision.org/whalesong/examples/dwarves-with-remove/index.html"]{index.html}]
|
||||||
Uses @racket[view-focus?] and @racket[view-remove] to see if a dwarf should be removed from the view.
|
Uses @racket[view-focus?] and @racket[view-remove] to see if a dwarf should be removed from the view.
|
||||||
}
|
}
|
||||||
|
|
||||||
@item{@link["http://hashcollision.org/whalesong/examples/field/field.html"]{field.html}
|
@item{@link["http://hashcollision.org/whalesong/examples/field/field.html"]{field.html}
|
||||||
[@link["http://hashcollision.org/whalesong/examples/field/field.rkt"]{src}]
|
[@link["http://hashcollision.org/whalesong/examples/field/field.rkt"]{src}
|
||||||
|
@link["http://hashcollision.org/whalesong/examples/field/index.html"]{index.html}]
|
||||||
Uses @racket[view-bind] to read a text field, and @racket[update-view-text] to change
|
Uses @racket[view-bind] to read a text field, and @racket[update-view-text] to change
|
||||||
the text content of an element.
|
the text content of an element.
|
||||||
}
|
}
|
||||||
|
|
||||||
@item{@link["http://hashcollision.org/whalesong/examples/phases/phases.html"]{phases.html}
|
@item{@link["http://hashcollision.org/whalesong/examples/phases/phases.html"]{phases.html}
|
||||||
[@link["http://hashcollision.org/whalesong/examples/phases/phases.rkt"]{src}]
|
[@link["http://hashcollision.org/whalesong/examples/phases/phases.rkt"]{src}
|
||||||
|
@link["http://hashcollision.org/whalesong/examples/phases/index1.html"]{index1.html}
|
||||||
|
@link["http://hashcollision.org/whalesong/examples/phases/index2.html"]{index2.html}]
|
||||||
Switches out one view entirely in place of another. Different views can correspond to phases in a program.
|
Switches out one view entirely in place of another. Different views can correspond to phases in a program.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@item{@link["http://hashcollision.org/whalesong/examples/tick-tock/tick-tock.html"]{tick-tock.html}
|
@item{@link["http://hashcollision.org/whalesong/examples/tick-tock/tick-tock.html"]{tick-tock.html}
|
||||||
[@link["http://hashcollision.org/whalesong/examples/tick-tock/tick-tock.rkt"]{src}]
|
[@link["http://hashcollision.org/whalesong/examples/tick-tock/tick-tock.rkt"]{src}
|
||||||
|
@link["http://hashcollision.org/whalesong/examples/tick-tock/index.html"]{index.html}]
|
||||||
Uses @racket[on-tick] to show a timer counting up.
|
Uses @racket[on-tick] to show a timer counting up.
|
||||||
}
|
}
|
||||||
|
|
||||||
@item{@link["http://hashcollision.org/whalesong/examples/redirected/redirected.html"]{redirected.html}
|
@item{@link["http://hashcollision.org/whalesong/examples/redirected/redirected.html"]{redirected.html}
|
||||||
[@link["http://hashcollision.org/whalesong/examples/redirected/redirected.rkt"]{src}]
|
[@link["http://hashcollision.org/whalesong/examples/redirected/redirected.rkt"]{src}
|
||||||
|
@link["http://hashcollision.org/whalesong/examples/redirected/index.html"]{index.html}]
|
||||||
Uses @racket[on-tick] to show a timer counting up, and also uses @racket[open-output-element] to
|
Uses @racket[on-tick] to show a timer counting up, and also uses @racket[open-output-element] to
|
||||||
pipe side-effecting @racket[printf]s to a hidden @tt{div}.
|
pipe side-effecting @racket[printf]s to a hidden @tt{div}.
|
||||||
}
|
}
|
||||||
|
|
||||||
@item{@link["http://hashcollision.org/whalesong/examples/todo/todo.html"]{todo.html}
|
@item{@link["http://hashcollision.org/whalesong/examples/todo/todo.html"]{todo.html}
|
||||||
[@link["http://hashcollision.org/whalesong/examples/todo/todo.rkt"]{src}]
|
[@link["http://hashcollision.org/whalesong/examples/todo/todo.rkt"]{src}
|
||||||
|
@link["http://hashcollision.org/whalesong/examples/todo/index.html"]{index.html}]
|
||||||
A simple TODO list manager.
|
A simple TODO list manager.
|
||||||
}
|
}
|
||||||
|
|
||||||
@item{@link["http://hashcollision.org/whalesong/examples/where-am-i/where-am-i.html"]{where-am-i.html}
|
@item{@link["http://hashcollision.org/whalesong/examples/where-am-i/where-am-i.html"]{where-am-i.html}
|
||||||
[@link["http://hashcollision.org/whalesong/examples/where-am-i/where-am-i.rkt"]{src}]
|
[@link["http://hashcollision.org/whalesong/examples/where-am-i/where-am-i.rkt"]{src}
|
||||||
|
@link["http://hashcollision.org/whalesong/examples/where-am-i/index.html"]{index.html}]
|
||||||
Uses @racket[on-location-change] and @racket[on-mock-location-change] to demonstrate location services.
|
Uses @racket[on-location-change] and @racket[on-mock-location-change] to demonstrate location services.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@item{@link["http://hashcollision.org/whalesong/examples/hot-cross-buns/hot-cross-buns.html"]{hot-cross-buns.html}
|
@item{@link["http://hashcollision.org/whalesong/examples/hot-cross-buns/hot-cross-buns.html"]{hot-cross-buns.html}
|
||||||
[@link["http://hashcollision.org/whalesong/examples/hot-cross-buns/hot-cross-buns.rkt"]{src}]
|
[@link["http://hashcollision.org/whalesong/examples/hot-cross-buns/hot-cross-buns.rkt"]{src}
|
||||||
|
@link["http://hashcollision.org/whalesong/examples/hot-cross-buns/index.html"]{index.html}]
|
||||||
Demonstrates use of checkboxes. Uses @racket[view-has-attr?] to see if a checkbox has been
|
Demonstrates use of checkboxes. Uses @racket[view-has-attr?] to see if a checkbox has been
|
||||||
checked, and @racket[remove-view-attr] to change the @emph{checked} attribute when the user
|
checked, and @racket[remove-view-attr] to change the @emph{checked} attribute when the user
|
||||||
wants to reset the page.
|
wants to reset the page.
|
||||||
|
|
|
@ -7,4 +7,4 @@
|
||||||
(provide version)
|
(provide version)
|
||||||
(: version String)
|
(: version String)
|
||||||
|
|
||||||
(define version "1.101")
|
(define version "1.102")
|
||||||
|
|
|
@ -1171,7 +1171,7 @@
|
||||||
MACHINE.params.currentOutputPort = find(handlers, isWithOutputToHandler).outputPort;
|
MACHINE.params.currentOutputPort = find(handlers, isWithOutputToHandler).outputPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
PAUSE(function(restart) {
|
PAUSE(function(restart, internalCall) {
|
||||||
var onCleanRestart, onMessyRestart,
|
var onCleanRestart, onMessyRestart,
|
||||||
startEventHandlers, stopEventHandlers,
|
startEventHandlers, stopEventHandlers,
|
||||||
startEventHandler, stopEventHandler,
|
startEventHandler, stopEventHandler,
|
||||||
|
@ -1260,7 +1260,7 @@
|
||||||
var onGoodWorldUpdate =
|
var onGoodWorldUpdate =
|
||||||
function(newWorld) {
|
function(newWorld) {
|
||||||
world = newWorld;
|
world = newWorld;
|
||||||
stopWhen(MACHINE,
|
stopWhen(internalCall,
|
||||||
world,
|
world,
|
||||||
mockView,
|
mockView,
|
||||||
function(shouldStop) {
|
function(shouldStop) {
|
||||||
|
@ -1274,14 +1274,14 @@
|
||||||
fail);
|
fail);
|
||||||
};
|
};
|
||||||
if (plt.baselib.arity.isArityMatching(racketWorldCallback.racketArity, 3)) {
|
if (plt.baselib.arity.isArityMatching(racketWorldCallback.racketArity, 3)) {
|
||||||
racketWorldCallback(MACHINE,
|
racketWorldCallback(internalCall,
|
||||||
world,
|
world,
|
||||||
mockView,
|
mockView,
|
||||||
data,
|
data,
|
||||||
onGoodWorldUpdate,
|
onGoodWorldUpdate,
|
||||||
fail);
|
fail);
|
||||||
} else {
|
} else {
|
||||||
racketWorldCallback(MACHINE,
|
racketWorldCallback(internalCall,
|
||||||
world,
|
world,
|
||||||
mockView,
|
mockView,
|
||||||
onGoodWorldUpdate,
|
onGoodWorldUpdate,
|
||||||
|
@ -1299,7 +1299,7 @@
|
||||||
// update, and have to do it from scratch.
|
// update, and have to do it from scratch.
|
||||||
var nonce = Math.random();
|
var nonce = Math.random();
|
||||||
var originalMockView = view.getMockAndResetFocus(nonce);
|
var originalMockView = view.getMockAndResetFocus(nonce);
|
||||||
toDraw(MACHINE,
|
toDraw(internalCall,
|
||||||
world,
|
world,
|
||||||
originalMockView,
|
originalMockView,
|
||||||
function(newMockView) {
|
function(newMockView) {
|
||||||
|
@ -1334,13 +1334,12 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
var wrapFunction = function(proc) {
|
var wrapFunction = function(proc) {
|
||||||
var f = function(MACHINE) {
|
var f = function(internalCall) {
|
||||||
var success = arguments[arguments.length - 2];
|
var success = arguments[arguments.length - 2];
|
||||||
var fail = arguments[arguments.length - 1];
|
var fail = arguments[arguments.length - 1];
|
||||||
var args = [].slice.call(arguments, 1, arguments.length - 2);
|
var args = [].slice.call(arguments, 1, arguments.length - 2);
|
||||||
return plt.baselib.functions.internalCallDuringPause.apply(null,
|
return internalCall.apply(null,
|
||||||
[MACHINE,
|
[proc,
|
||||||
proc,
|
|
||||||
success,
|
success,
|
||||||
fail].concat(args));
|
fail].concat(args));
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
(require "impl.rkt"
|
(require "impl.rkt"
|
||||||
"helpers.rkt"
|
"helpers.rkt"
|
||||||
"event.rkt")
|
"event.rkt"
|
||||||
|
(for-syntax racket/base))
|
||||||
|
|
||||||
(require (for-syntax racket/base racket/stxparam-exptime)
|
(require (for-syntax racket/base racket/stxparam-exptime)
|
||||||
(only-in "../lang/kernel.rkt" define-syntax-parameter syntax-parameterize))
|
(only-in "../lang/kernel.rkt" define-syntax-parameter syntax-parameterize))
|
||||||
|
@ -18,6 +19,7 @@
|
||||||
(all-from-out "helpers.rkt")
|
(all-from-out "helpers.rkt")
|
||||||
(all-from-out "event.rkt"))
|
(all-from-out "event.rkt"))
|
||||||
|
|
||||||
|
(provide view-bind*)
|
||||||
|
|
||||||
(provide (rename-out [internal-big-bang big-bang]
|
(provide (rename-out [internal-big-bang big-bang]
|
||||||
[big-bang big-bang/f]
|
[big-bang big-bang/f]
|
||||||
|
@ -77,3 +79,20 @@
|
||||||
on-location-change
|
on-location-change
|
||||||
to-draw))
|
to-draw))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;; A syntactic form to make it more convenient to focus and bind multiple things
|
||||||
|
;; (view-bind* a-view
|
||||||
|
;; [id type function]
|
||||||
|
;; [id type function] ...)
|
||||||
|
(define-syntax (view-bind* stx)
|
||||||
|
(syntax-case stx ()
|
||||||
|
[(_ a-view [a-selector a-type a-function] ...)
|
||||||
|
(foldl (lambda (a-selector a-type a-function a-view-stx)
|
||||||
|
#'(view-bind (view-focus #,a-view-stx #,a-selector)
|
||||||
|
#,a-type
|
||||||
|
#,a-function))
|
||||||
|
#'a-view
|
||||||
|
(syntax->list #'(a-selector ...))
|
||||||
|
(syntax->list #'(a-type ...))
|
||||||
|
(syntax->list #'(a-function ...)))]))
|
||||||
|
|
Loading…
Reference in New Issue
Block a user