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,66 +128,77 @@
|
||||||
|
|
||||||
var coerseClosureToJavaScript = function (v, MACHINE) {
|
var coerseClosureToJavaScript = function (v, MACHINE) {
|
||||||
var f = function (succ, fail) {
|
var f = function (succ, fail) {
|
||||||
succ = succ || function () {};
|
var args = [];
|
||||||
fail = fail || function () {};
|
|
||||||
|
|
||||||
if (!(baselib.arity.isArityMatching(v.racketArity, arguments.length - 2))) {
|
|
||||||
var msg = baselib.format.format(
|
|
||||||
"arity mismatch: ~s expected ~s argument(s) but received ~s",
|
|
||||||
[v.displayName, v.racketArity, arguments.length - 2]);
|
|
||||||
return fail(new baselib.exceptions.RacketError(
|
|
||||||
msg,
|
|
||||||
baselib.exceptions.makeExnFailContractArity(msg,
|
|
||||||
MACHINE.captureContinuationMarks())));
|
|
||||||
}
|
|
||||||
|
|
||||||
var oldVal = MACHINE.v;
|
|
||||||
var oldArgcount = MACHINE.a;
|
|
||||||
var oldProc = MACHINE.p;
|
|
||||||
|
|
||||||
var oldErrorHandler = MACHINE.params['currentErrorHandler'];
|
|
||||||
var afterGoodInvoke = function (MACHINE) {
|
|
||||||
plt.runtime.PAUSE(
|
|
||||||
function (restart) {
|
|
||||||
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
|
||||||
var returnValue = MACHINE.v;
|
|
||||||
MACHINE.v = oldVal;
|
|
||||||
MACHINE.a = oldArgcount;
|
|
||||||
MACHINE.p = oldProc;
|
|
||||||
succ(returnValue);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
afterGoodInvoke.mvr = function (MACHINE) {
|
|
||||||
plt.runtime.PAUSE(
|
|
||||||
function (restart) {
|
|
||||||
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
|
||||||
var returnValues = [MACHINE.v], i;
|
|
||||||
for (i = 0; i < MACHINE.a - 1; i++) {
|
|
||||||
returnValues.push(MACHINE.e.pop());
|
|
||||||
}
|
|
||||||
MACHINE.v = oldVal;
|
|
||||||
MACHINE.a = oldArgcount;
|
|
||||||
MACHINE.p = oldProc;
|
|
||||||
succ.apply(null, returnValues);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
MACHINE.c.push(
|
|
||||||
new baselib.frames.CallFrame(afterGoodInvoke, v));
|
|
||||||
MACHINE.a = arguments.length - 2;
|
|
||||||
var i;
|
var i;
|
||||||
for (i = 0; i < arguments.length - 2; i++) {
|
for (i = 0; i < arguments.length; i++) {
|
||||||
MACHINE.e.push(arguments[arguments.length - 1 - i]);
|
args.push(arguments[i]);
|
||||||
}
|
}
|
||||||
MACHINE.p = v;
|
|
||||||
MACHINE.params['currentErrorHandler'] = function (MACHINE, e) {
|
MACHINE.exclusiveLock.acquire(
|
||||||
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
"js-as-closure",
|
||||||
MACHINE.v = oldVal;
|
function(releaseLock) {
|
||||||
MACHINE.a = oldArgcount;
|
succ = succ || function () {};
|
||||||
MACHINE.p = oldProc;
|
fail = fail || function () {};
|
||||||
fail(e);
|
if (!(baselib.arity.isArityMatching(v.racketArity, args.length - 2))) {
|
||||||
};
|
var msg = baselib.format.format(
|
||||||
MACHINE.trampoline(v.label);
|
"arity mismatch: ~s expected ~s argument(s) but received ~s",
|
||||||
|
[v.displayName, v.racketArity, args.length - 2]);
|
||||||
|
releaseLock();
|
||||||
|
return fail(new baselib.exceptions.RacketError(
|
||||||
|
msg,
|
||||||
|
baselib.exceptions.makeExnFailContractArity(msg,
|
||||||
|
MACHINE.captureContinuationMarks())));
|
||||||
|
}
|
||||||
|
|
||||||
|
var oldVal = MACHINE.v;
|
||||||
|
var oldArgcount = MACHINE.a;
|
||||||
|
var oldProc = MACHINE.p;
|
||||||
|
var oldErrorHandler = MACHINE.params['currentErrorHandler'];
|
||||||
|
|
||||||
|
var afterGoodInvoke = function (MACHINE) {
|
||||||
|
plt.runtime.PAUSE(
|
||||||
|
function (restart) {
|
||||||
|
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||||
|
var returnValue = MACHINE.v;
|
||||||
|
MACHINE.v = oldVal;
|
||||||
|
MACHINE.a = oldArgcount;
|
||||||
|
MACHINE.p = oldProc;
|
||||||
|
succ(returnValue);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
afterGoodInvoke.mvr = function (MACHINE) {
|
||||||
|
plt.runtime.PAUSE(
|
||||||
|
function (restart) {
|
||||||
|
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||||
|
var returnValues = [MACHINE.v], i;
|
||||||
|
for (i = 0; i < MACHINE.a - 1; i++) {
|
||||||
|
returnValues.push(MACHINE.e.pop());
|
||||||
|
}
|
||||||
|
MACHINE.v = oldVal;
|
||||||
|
MACHINE.a = oldArgcount;
|
||||||
|
MACHINE.p = oldProc;
|
||||||
|
succ.apply(null, returnValues);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
MACHINE.c.push(
|
||||||
|
new baselib.frames.CallFrame(afterGoodInvoke, v));
|
||||||
|
MACHINE.a = args.length - 2;
|
||||||
|
var i;
|
||||||
|
for (i = 0; i < args.length - 2; i++) {
|
||||||
|
MACHINE.e.push(args[args.length - 1 - i]);
|
||||||
|
}
|
||||||
|
MACHINE.p = v;
|
||||||
|
MACHINE.params['currentErrorHandler'] = function (MACHINE, e) {
|
||||||
|
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||||
|
MACHINE.v = oldVal;
|
||||||
|
MACHINE.a = oldArgcount;
|
||||||
|
MACHINE.p = oldProc;
|
||||||
|
fail(e);
|
||||||
|
};
|
||||||
|
|
||||||
|
MACHINE._trampoline(v.label, false, releaseLock);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
return f;
|
return f;
|
||||||
};
|
};
|
||||||
|
@ -214,72 +225,89 @@
|
||||||
|
|
||||||
|
|
||||||
// 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 msg = baselib.format.format("arity mismatch: ~s expected ~s arguments, but received ~s",
|
|
||||||
[proc.displayName, proc.racketArity, arguments.length - 4]);
|
|
||||||
return fail(baselib.exceptions.makeExnFailContractArity(msg,
|
|
||||||
MACHINE.captureContinuationMarks()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isClosure(proc)) {
|
var i;
|
||||||
oldVal = MACHINE.v;
|
var oldArgcount, oldVal, oldProc, oldErrorHandler, oldControlLength, oldEnvLength;
|
||||||
oldArgcount = MACHINE.a;
|
if (! baselib.arity.isArityMatching(proc.racketArity, args.length - 4)) {
|
||||||
oldProc = MACHINE.p;
|
var msg = baselib.format.format("arity mismatch: ~s expected ~s arguments, but received ~s",
|
||||||
|
[proc.displayName, proc.racketArity, args.length - 4]);
|
||||||
|
fail(baselib.exceptions.makeExnFailContractArity(msg,
|
||||||
|
MACHINE.captureContinuationMarks()));
|
||||||
|
}
|
||||||
|
|
||||||
oldErrorHandler = MACHINE.params['currentErrorHandler'];
|
if (! isClosure(proc)) {
|
||||||
var afterGoodInvoke = function (MACHINE) {
|
|
||||||
plt.runtime.PAUSE(function (restart) {
|
|
||||||
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
|
||||||
var returnValue = MACHINE.v;
|
|
||||||
MACHINE.v = oldVal;
|
|
||||||
MACHINE.a = oldArgcount;
|
|
||||||
MACHINE.p = oldProc;
|
|
||||||
success(returnValue);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
afterGoodInvoke.mvr = function (MACHINE) {
|
|
||||||
plt.runtime.PAUSE(function (restart) {
|
|
||||||
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
|
||||||
var returnValues = [MACHINE.v];
|
|
||||||
var i;
|
|
||||||
for (i = 0; i < MACHINE.a - 1; i++) {
|
|
||||||
returnValues.push(MACHINE.e.pop());
|
|
||||||
}
|
|
||||||
MACHINE.v = oldVal;
|
|
||||||
MACHINE.a = oldArgcount;
|
|
||||||
MACHINE.p = oldProc;
|
|
||||||
success.apply(null, returnValues);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
MACHINE.c.push(
|
|
||||||
new baselib.frames.CallFrame(afterGoodInvoke, proc));
|
|
||||||
MACHINE.a = arguments.length - 4;
|
|
||||||
for (i = 0; i < arguments.length - 4; i++) {
|
|
||||||
MACHINE.e.push(arguments[arguments.length - 1 - i]);
|
|
||||||
}
|
|
||||||
MACHINE.p = proc;
|
|
||||||
MACHINE.params['currentErrorHandler'] = function (MACHINE, e) {
|
|
||||||
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
|
||||||
MACHINE.v = oldVal;
|
|
||||||
MACHINE.a = oldArgcount;
|
|
||||||
MACHINE.p = oldProc;
|
|
||||||
fail(e);
|
|
||||||
};
|
|
||||||
MACHINE.trampoline(proc.label);
|
|
||||||
} else {
|
|
||||||
fail(baselib.exceptions.makeExnFail(
|
fail(baselib.exceptions.makeExnFail(
|
||||||
baselib.format.format(
|
baselib.format.format(
|
||||||
"Not a procedure: ~e",
|
"Not a procedure: ~e",
|
||||||
proc),
|
proc),
|
||||||
MACHINE.captureContinuationMarks()));
|
MACHINE.captureContinuationMarks()));
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = MACHINE.v;
|
||||||
|
oldArgcount = MACHINE.a;
|
||||||
|
oldProc = MACHINE.p;
|
||||||
|
oldControlLength = MACHINE.c.length;
|
||||||
|
oldEnvLength = MACHINE.e.length;
|
||||||
|
|
||||||
|
oldErrorHandler = MACHINE.params['currentErrorHandler'];
|
||||||
|
var afterGoodInvoke = function (MACHINE) {
|
||||||
|
plt.runtime.PAUSE(function (restart) {
|
||||||
|
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||||
|
var returnValue = MACHINE.v;
|
||||||
|
MACHINE.v = oldVal;
|
||||||
|
MACHINE.a = oldArgcount;
|
||||||
|
MACHINE.p = oldProc;
|
||||||
|
success(returnValue);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
afterGoodInvoke.mvr = function (MACHINE) {
|
||||||
|
plt.runtime.PAUSE(function (restart) {
|
||||||
|
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||||
|
var returnValues = [MACHINE.v];
|
||||||
|
var i;
|
||||||
|
for (i = 0; i < MACHINE.a - 1; i++) {
|
||||||
|
returnValues.push(MACHINE.e.pop());
|
||||||
|
}
|
||||||
|
MACHINE.v = oldVal;
|
||||||
|
MACHINE.a = oldArgcount;
|
||||||
|
MACHINE.p = oldProc;
|
||||||
|
success.apply(null, returnValues);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
MACHINE.c.push(
|
||||||
|
new baselib.frames.CallFrame(afterGoodInvoke, proc));
|
||||||
|
MACHINE.a = args.length - 4;
|
||||||
|
for (i = 0; i < args.length - 4; i++) {
|
||||||
|
MACHINE.e.push(args[args.length - 1 - i]);
|
||||||
|
}
|
||||||
|
MACHINE.p = proc;
|
||||||
|
MACHINE.params['currentErrorHandler'] = function (MACHINE, e) {
|
||||||
|
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||||
|
MACHINE.v = oldVal;
|
||||||
|
MACHINE.a = oldArgcount;
|
||||||
|
MACHINE.p = oldProc;
|
||||||
|
MACHINE.c.length = oldControlLength;
|
||||||
|
MACHINE.e.length = oldEnvLength;
|
||||||
|
fail(e);
|
||||||
|
};
|
||||||
|
MACHINE._trampoline(proc.label,
|
||||||
|
false,
|
||||||
|
function() {
|
||||||
|
// The lock should still being held, so we don't
|
||||||
|
// automatically unlock control.
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -62,19 +62,28 @@
|
||||||
if (this.isInvoked) {
|
if (this.isInvoked) {
|
||||||
succ();
|
succ();
|
||||||
} else {
|
} else {
|
||||||
MACHINE.params['currentErrorHandler'] = function (MACHINE, anError) {
|
|
||||||
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
|
||||||
fail(MACHINE, anError);
|
|
||||||
};
|
|
||||||
MACHINE.c.push(new plt.baselib.frames.CallFrame(afterGoodInvoke, null));
|
|
||||||
if (isInternal) {
|
if (isInternal) {
|
||||||
|
MACHINE.params['currentErrorHandler'] = function (MACHINE, anError) {
|
||||||
|
MACHINE.params['currentErrorHandler'] = oldErrorHandler;
|
||||||
|
fail(MACHINE, anError);
|
||||||
|
};
|
||||||
|
MACHINE.c.push(new plt.baselib.frames.CallFrame(afterGoodInvoke, null));
|
||||||
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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
exports.ModuleRecord = ModuleRecord;
|
exports.ModuleRecord = ModuleRecord;
|
||||||
|
|
|
@ -114,7 +114,7 @@
|
||||||
var ArityAtLeast = baselib.arity.ArityAtLeast;
|
var ArityAtLeast = baselib.arity.ArityAtLeast;
|
||||||
var makeArityAtLeast = baselib.arity.makeArityAtLeast;
|
var makeArityAtLeast = baselib.arity.makeArityAtLeast;
|
||||||
var isArityMatching = baselib.arity.isArityMatching;
|
var isArityMatching = baselib.arity.isArityMatching;
|
||||||
|
|
||||||
|
|
||||||
var testArgument = baselib.check.testArgument;
|
var testArgument = baselib.check.testArgument;
|
||||||
var testArity = baselib.check.testArity;
|
var testArity = baselib.check.testArity;
|
||||||
|
@ -138,17 +138,17 @@
|
||||||
|
|
||||||
|
|
||||||
var defaultCurrentPrintImplementation = function (MACHINE) {
|
var defaultCurrentPrintImplementation = function (MACHINE) {
|
||||||
if(--MACHINE.cbt < 0) {
|
if(--MACHINE.cbt < 0) {
|
||||||
throw defaultCurrentPrintImplementation;
|
throw defaultCurrentPrintImplementation;
|
||||||
}
|
}
|
||||||
var oldArgcount = MACHINE.a;
|
var oldArgcount = MACHINE.a;
|
||||||
|
|
||||||
var elt = MACHINE.e[MACHINE.e.length - 1];
|
var elt = MACHINE.e[MACHINE.e.length - 1];
|
||||||
var outputPort =
|
var outputPort =
|
||||||
MACHINE.params.currentOutputPort;
|
MACHINE.params.currentOutputPort;
|
||||||
if (elt !== VOID) {
|
if (elt !== VOID) {
|
||||||
outputPort.writeDomNode(
|
outputPort.writeDomNode(
|
||||||
MACHINE,
|
MACHINE,
|
||||||
toDomNode(elt, MACHINE.params['print-mode']));
|
toDomNode(elt, MACHINE.params['print-mode']));
|
||||||
outputPort.writeDomNode(MACHINE, toDomNode("\n", 'display'));
|
outputPort.writeDomNode(MACHINE, toDomNode("\n", 'display'));
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -188,7 +258,7 @@
|
||||||
'currentDisplayer': function(MACHINE, domNode) {
|
'currentDisplayer': function(MACHINE, domNode) {
|
||||||
$(domNode).appendTo(document.body);
|
$(domNode).appendTo(document.body);
|
||||||
},
|
},
|
||||||
|
|
||||||
// currentErrorDisplayer: DomNode -> Void
|
// currentErrorDisplayer: DomNode -> Void
|
||||||
// currentErrorDisplayer is responsible for displaying errors to the browser.
|
// currentErrorDisplayer is responsible for displaying errors to the browser.
|
||||||
'currentErrorDisplayer': function(MACHINE, domNode) {
|
'currentErrorDisplayer': function(MACHINE, domNode) {
|
||||||
|
@ -196,7 +266,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
'currentInspector': baselib.inspectors.DEFAULT_INSPECTOR,
|
'currentInspector': baselib.inspectors.DEFAULT_INSPECTOR,
|
||||||
|
|
||||||
'currentOutputPort': new StandardOutputPort(),
|
'currentOutputPort': new StandardOutputPort(),
|
||||||
'currentErrorPort': new StandardErrorPort(),
|
'currentErrorPort': new StandardErrorPort(),
|
||||||
'currentInputPort': new StandardInputPort(),
|
'currentInputPort': new StandardInputPort(),
|
||||||
|
@ -206,9 +276,9 @@
|
||||||
MACHINE,
|
MACHINE,
|
||||||
toDomNode(exn, MACHINE.params['print-mode']));
|
toDomNode(exn, MACHINE.params['print-mode']));
|
||||||
},
|
},
|
||||||
|
|
||||||
'currentNamespace': {},
|
'currentNamespace': {},
|
||||||
|
|
||||||
// These parameters control how often
|
// These parameters control how often
|
||||||
// control yields back to the browser
|
// control yields back to the browser
|
||||||
// for response. The implementation is a
|
// for response. The implementation is a
|
||||||
|
@ -229,8 +299,9 @@
|
||||||
|
|
||||||
};
|
};
|
||||||
this.primitives = Primitives;
|
this.primitives = Primitives;
|
||||||
|
this.exclusiveLock = new ExclusiveLock();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Try to get the continuation mark key used for procedure application tracing.
|
// Try to get the continuation mark key used for procedure application tracing.
|
||||||
var getTracedAppKey = function(MACHINE) {
|
var getTracedAppKey = function(MACHINE) {
|
||||||
|
@ -260,7 +331,7 @@
|
||||||
return MACHINE.c.slice(i + 1,
|
return MACHINE.c.slice(i + 1,
|
||||||
MACHINE.c.length - skip);
|
MACHINE.c.length - skip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
raise(MACHINE, new Error("captureControl: unable to find tag " + tag));
|
raise(MACHINE, new Error("captureControl: unable to find tag " + tag));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -268,20 +339,20 @@
|
||||||
|
|
||||||
// restoreControl clears the control stack (up to, but not including the
|
// restoreControl clears the control stack (up to, but not including the
|
||||||
// prompt tagged by tag), and then appends the rest of the control frames.
|
// prompt tagged by tag), and then appends the rest of the control frames.
|
||||||
// At the moment, the rest of the control frames is assumed to be in the
|
// At the moment, the rest of the control frames is assumed to be in the
|
||||||
// top of the environment.
|
// top of the environment.
|
||||||
Machine.prototype.restoreControl = function(tag) {
|
Machine.prototype.restoreControl = function(tag) {
|
||||||
var MACHINE = this;
|
var MACHINE = this;
|
||||||
var i;
|
var i;
|
||||||
for (i = MACHINE.c.length - 1; i >= 0; i--) {
|
for (i = MACHINE.c.length - 1; i >= 0; i--) {
|
||||||
if (MACHINE.c[i].tag === tag) {
|
if (MACHINE.c[i].tag === tag) {
|
||||||
MACHINE.c =
|
MACHINE.c =
|
||||||
MACHINE.c.slice(0, i+1).concat(
|
MACHINE.c.slice(0, i+1).concat(
|
||||||
MACHINE.e[MACHINE.e.length - 1]);
|
MACHINE.e[MACHINE.e.length - 1]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
raise(MACHINE, new Error("restoreControl: unable to find tag " + tag));
|
raise(MACHINE, new Error("restoreControl: unable to find tag " + tag));
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -309,11 +380,11 @@
|
||||||
var lst = NULL;
|
var lst = NULL;
|
||||||
var i;
|
var i;
|
||||||
for (i = 0; i < length; i++) {
|
for (i = 0; i < length; i++) {
|
||||||
lst = makePair(MACHINE.e[MACHINE.e.length - depth - length + i],
|
lst = makePair(MACHINE.e[MACHINE.e.length - depth - length + i],
|
||||||
lst);
|
lst);
|
||||||
}
|
}
|
||||||
MACHINE.e.splice(MACHINE.e.length - depth - length,
|
MACHINE.e.splice(MACHINE.e.length - depth - length,
|
||||||
length,
|
length,
|
||||||
lst);
|
lst);
|
||||||
MACHINE.a = MACHINE.a - length + 1;
|
MACHINE.a = MACHINE.a - length + 1;
|
||||||
};
|
};
|
||||||
|
@ -347,8 +418,8 @@
|
||||||
if (control[i].marks.length !== 0) {
|
if (control[i].marks.length !== 0) {
|
||||||
kvLists.push(control[i].marks);
|
kvLists.push(control[i].marks);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tracedCalleeKey !== null &&
|
if (tracedCalleeKey !== null &&
|
||||||
control[i] instanceof CallFrame &&
|
control[i] instanceof CallFrame &&
|
||||||
control[i].p !== null) {
|
control[i].p !== null) {
|
||||||
kvLists.push([[tracedCalleeKey, control[i].p]]);
|
kvLists.push([[tracedCalleeKey, control[i].p]]);
|
||||||
|
@ -356,7 +427,7 @@
|
||||||
}
|
}
|
||||||
return new baselib.contmarks.ContinuationMarkSet(kvLists);
|
return new baselib.contmarks.ContinuationMarkSet(kvLists);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
MACHINE.a = oldArgcount;
|
pauseLock.acquire(
|
||||||
return scheduleTrampoline(MACHINE, f);
|
undefined,
|
||||||
|
function(pauseReleaseLock) {
|
||||||
|
MACHINE.a = oldArgcount;
|
||||||
|
MACHINE._trampoline(f, false, release);
|
||||||
|
pauseReleaseLock();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -419,18 +491,35 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// 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 thunk = initialJump;
|
var that = this;
|
||||||
var startTime = (new Date()).valueOf();
|
|
||||||
this.cbt = STACK_LIMIT_ESTIMATE;
|
|
||||||
this.params.numBouncesBeforeYield =
|
|
||||||
this.params.maxNumBouncesBeforeYield;
|
|
||||||
this.running = true;
|
|
||||||
|
|
||||||
while(true) {
|
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 startTime = (new Date()).valueOf();
|
||||||
|
that.cbt = STACK_LIMIT_ESTIMATE;
|
||||||
|
that.params.numBouncesBeforeYield =
|
||||||
|
that.params.maxNumBouncesBeforeYield;
|
||||||
|
that.running = 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
|
||||||
// during racket evaluation:
|
// during racket evaluation:
|
||||||
|
@ -452,9 +541,9 @@
|
||||||
// Everything else: otherwise, we send the exception value
|
// Everything else: otherwise, we send the exception value
|
||||||
// to the current error handler and exit.
|
// to the current error handler and exit.
|
||||||
// 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,48 +552,88 @@
|
||||||
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);
|
||||||
return;
|
release();
|
||||||
}
|
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
|
||||||
// that we get MACHINE.params.desiredYieldsPerSecond bounces per
|
// that we get MACHINE.params.desiredYieldsPerSecond bounces per
|
||||||
// second.
|
// second.
|
||||||
var DESIRED_DELAY_BETWEEN_BOUNCES =
|
var DESIRED_DELAY_BETWEEN_BOUNCES =
|
||||||
(1000 / MACHINE.params.desiredYieldsPerSecond);
|
(1000 / MACHINE.params.desiredYieldsPerSecond);
|
||||||
var ALPHA = 50;
|
var ALPHA = 50;
|
||||||
var delta = (ALPHA * ((DESIRED_DELAY_BETWEEN_BOUNCES -
|
var delta = (ALPHA * ((DESIRED_DELAY_BETWEEN_BOUNCES -
|
||||||
observedDelay) /
|
observedDelay) /
|
||||||
DESIRED_DELAY_BETWEEN_BOUNCES));
|
DESIRED_DELAY_BETWEEN_BOUNCES));
|
||||||
MACHINE.params.maxNumBouncesBeforeYield =
|
MACHINE.params.maxNumBouncesBeforeYield =
|
||||||
Math.max(MACHINE.params.maxNumBouncesBeforeYield + delta,
|
Math.max(MACHINE.params.maxNumBouncesBeforeYield + delta,
|
||||||
1);
|
1);
|
||||||
};
|
};
|
||||||
|
@ -538,7 +667,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -684,7 +813,7 @@
|
||||||
exports['installPrimitiveProcedure'] = installPrimitiveProcedure;
|
exports['installPrimitiveProcedure'] = installPrimitiveProcedure;
|
||||||
exports['makePrimitiveProcedure'] = makePrimitiveProcedure;
|
exports['makePrimitiveProcedure'] = makePrimitiveProcedure;
|
||||||
exports['Primitives'] = Primitives;
|
exports['Primitives'] = Primitives;
|
||||||
|
|
||||||
exports['ready'] = ready;
|
exports['ready'] = ready;
|
||||||
// Private: the runtime library will set this flag to true when
|
// Private: the runtime library will set this flag to true when
|
||||||
// the library has finished loading.
|
// the library has finished loading.
|
||||||
|
@ -699,7 +828,7 @@
|
||||||
exports['ModuleRecord'] = ModuleRecord;
|
exports['ModuleRecord'] = ModuleRecord;
|
||||||
exports['VariableReference'] = VariableReference;
|
exports['VariableReference'] = VariableReference;
|
||||||
exports['ContinuationPromptTag'] = ContinuationPromptTag;
|
exports['ContinuationPromptTag'] = ContinuationPromptTag;
|
||||||
exports['DEFAULT_CONTINUATION_PROMPT_TAG'] =
|
exports['DEFAULT_CONTINUATION_PROMPT_TAG'] =
|
||||||
DEFAULT_CONTINUATION_PROMPT_TAG;
|
DEFAULT_CONTINUATION_PROMPT_TAG;
|
||||||
exports['NULL'] = NULL;
|
exports['NULL'] = NULL;
|
||||||
exports['VOID'] = VOID;
|
exports['VOID'] = VOID;
|
||||||
|
|
|
@ -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,15 +1334,14 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
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));
|
|
||||||
};
|
};
|
||||||
f.racketArity = proc.racketArity;
|
f.racketArity = proc.racketArity;
|
||||||
return f;
|
return f;
|
||||||
|
|
|
@ -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]
|
||||||
|
@ -76,4 +78,21 @@
|
||||||
on-mock-location-change
|
on-mock-location-change
|
||||||
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