whalesong/js-assembler/runtime-src/runtime.js
Danny Yoo 3ed2d19eab adding expectations for what happens for module-scoping test.
fixing up the namespace stuff so it goes through getters and setters
trying to add the necessary to the il, but running into typed racket issues
corrected compilation of toplevelref so it works more correctly on module
variables.
2012-02-26 22:59:37 -05:00

1239 lines
41 KiB
JavaScript

/*jslint browser: true, undef: true, unparam: true, sub: true, vars: true, white: true, plusplus: true, maxerr: 50, indent: 4 */
// runtime.js: the main runtime library for whalesong.
//
// All of the values here are namespaced under "plt.runtime".
/*global $*/
(function(plt, baselib) {
'use strict';
var runtime = {};
plt.runtime = runtime;
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// We try to isolate the effect of external modules: all the identifiers we
// pull from external modules should be listed here, and should otherwise not
// show up outside this section!
var isNumber = baselib.numbers.isNumber;
var isNatural = baselib.numbers.isNatural;
var isReal = baselib.numbers.isReal;
var isPair = baselib.lists.isPair;
var isCaarPair = function(x) { return isPair(x) && isPair(x.first); };
var isList = baselib.lists.isList;
var isVector = baselib.vectors.isVector;
var isString = baselib.strings.isString;
var isSymbol = baselib.symbols.isSymbol;
var isPath = baselib.paths.isPath;
var equals = baselib.equality.equals;
var NULL = baselib.lists.EMPTY;
var VOID = baselib.constants.VOID_VALUE;
var NEGATIVE_ZERO = baselib.numbers.negative_zero;
var INF = baselib.numbers.inf;
var NEGATIVE_INF = baselib.numbers.negative_inf;
var NAN = baselib.numbers.nan;
var makeFloat = baselib.numbers.makeFloat;
var makeRational = baselib.numbers.makeRational;
var makeBignum = baselib.numbers.makeBignum;
var makeComplex = baselib.numbers.makeComplex;
var makeSymbol = baselib.symbols.makeSymbol;
var makePath = baselib.paths.makePath;
var makeBytes = baselib.bytes.makeBytes;
var makeBytesFromBase64 = baselib.bytes.makeBytesFromBase64;
var makeBox = baselib.boxes.makeBox;
var isBox = baselib.boxes.isBox;
var makeVector = baselib.vectors.makeVector;
var makeList = baselib.lists.makeList;
var makePair = baselib.lists.makePair;
var makeChar = baselib.chars.makeChar;
var makeStructureType = baselib.structs.makeStructureType;
var Struct = baselib.structs.Struct;
var StructType = baselib.structs.StructType;
var Closure = baselib.functions.Closure;
var finalizeClosureCall = baselib.functions.finalizeClosureCall;
var makePrimitiveProcedure = baselib.functions.makePrimitiveProcedure;
var makeClosure = baselib.functions.makeClosure;
var ContinuationPromptTag = baselib.contmarks.ContinuationPromptTag;
// Other helpers
var heir = baselib.heir;
var makeClassPredicate = baselib.makeClassPredicate;
var toDomNode = baselib.format.toDomNode;
var toWrittenString = baselib.format.toWrittenString;
var toDisplayedString = baselib.format.toDisplayedString;
// Frame structures.
var Frame = baselib.frames.Frame;
var CallFrame = baselib.frames.CallFrame;
var PromptFrame = baselib.frames.PromptFrame;
// Module structure
var ModuleRecord = baselib.modules.ModuleRecord;
// Ports
var isOutputPort = baselib.ports.isOutputPort;
var StandardOutputPort = baselib.ports.StandardOutputPort;
var StandardErrorPort = baselib.ports.StandardErrorPort;
var StandardInputPort = baselib.ports.StandardInputPort;
var isOutputStringPort = baselib.ports.isOutputStringPort;
// Exceptions and error handling.
var raise = baselib.exceptions.raise;
var raiseUnboundToplevelError = baselib.exceptions.raiseUnboundToplevelError;
var raiseArgumentTypeError = baselib.exceptions.raiseArgumentTypeError;
var raiseContextExpectedValuesError = baselib.exceptions.raiseContextExpectedValuesError;
var raiseArityMismatchError = baselib.exceptions.raiseArityMismatchError;
var raiseOperatorApplicationError = baselib.exceptions.raiseOperatorApplicationError;
var raiseOperatorIsNotPrimitiveProcedure = baselib.exceptions.raiseOperatorIsNotPrimitiveProcedure;
var raiseUnimplementedPrimitiveError = baselib.exceptions.raiseUnimplementedPrimitiveError;
var ArityAtLeast = baselib.arity.ArityAtLeast;
var makeArityAtLeast = baselib.arity.makeArityAtLeast;
var isArityMatching = baselib.arity.isArityMatching;
var testArgument = baselib.check.testArgument;
var testArity = baselib.check.testArity;
var makeCheckArgumentType = baselib.check.makeCheckArgumentType;
var Primitives = baselib.primitives.Primitives;
var installPrimitiveProcedure = baselib.primitives.installPrimitiveProcedure;
// This value used to be dynamically determined, but something on iOS5
// breaks badly when I try this.
// We're very conservative now.
var STACK_LIMIT_ESTIMATE = 200;
//////////////////////////////////////////////////////////////////////
var defaultCurrentPrintImplementation = function (MACHINE) {
if(--MACHINE.cbt < 0) {
throw defaultCurrentPrintImplementation;
}
var oldArgcount = MACHINE.a;
var elt = MACHINE.e[MACHINE.e.length - 1];
var outputPort =
MACHINE.params.currentOutputPort;
if (elt !== VOID) {
outputPort.writeDomNode(
MACHINE,
toDomNode(elt, MACHINE.params['print-mode']));
outputPort.writeDomNode(MACHINE, toDomNode("\n", 'display'));
}
MACHINE.a = oldArgcount;
return finalizeClosureCall(MACHINE, VOID);
};
var defaultCurrentPrint = makeClosure(
"default-printer",
1,
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
var Machine = function() {
this.cbt = STACK_LIMIT_ESTIMATE; // calls before trampoline
this.v = undefined; // value register
this.p = undefined; // procedure register
this.a = undefined; // argument count
this.e = []; // environment
this.c = []; // control: Arrayof (U Frame CallFrame PromptFrame)
this.running = false;
this.modules = {}; // String -> ModuleRecord
this.mainModules = []; // Arrayof String
this.params = {
// print-as-expression: boolean
'print-as-expression' : false,
// print-mode: (one-of "write" "print" "constructor")
'print-mode' : 'write',
// currentDisplayer: DomNode -> Void
// currentDisplayer is responsible for displaying to the browser.
'currentDisplayer': function(MACHINE, domNode) {
$(domNode).appendTo(document.body);
},
// currentErrorDisplayer: DomNode -> Void
// currentErrorDisplayer is responsible for displaying errors to the browser.
'currentErrorDisplayer': function(MACHINE, domNode) {
$(domNode).appendTo(document.body);
},
'currentInspector': baselib.inspectors.DEFAULT_INSPECTOR,
'currentOutputPort': new StandardOutputPort(),
'currentErrorPort': new StandardErrorPort(),
'currentInputPort': new StandardInputPort(),
'currentSuccessHandler': function(MACHINE) {},
'currentErrorHandler': function(MACHINE, exn) {
MACHINE.params.currentErrorDisplayer(
MACHINE,
toDomNode(exn, MACHINE.params['print-mode']));
},
'currentNamespace': { get: function() {},
set : function() {},
hasKey : function() { return false; }
},
// These parameters control how often
// control yields back to the browser
// for response. The implementation is a
// simple PID controller.
//
// To tune this, adjust desiredYieldsPerSecond.
// Do no touch numBouncesBeforeYield or
// maxNumBouncesBeforeYield, because those
// are adjusted automatically by the
// recomputeMaxNumBouncesBeforeYield
// procedure.
'desiredYieldsPerSecond': 5,
'numBouncesBeforeYield': 2000, // self-adjusting
'maxNumBouncesBeforeYield': 2000, // self-adjusting
'currentPrint': defaultCurrentPrint
};
this.primitives = Primitives;
this.exclusiveLock = new ExclusiveLock();
};
// Try to get the continuation mark key used for procedure application tracing.
var getTracedAppKey = function(MACHINE) {
if (MACHINE.modules['whalesong/lang/private/traced-app.rkt']) {
return MACHINE.modules['whalesong/lang/private/traced-app.rkt'].getNamespace().get('traced-app-key') || 'traced-app-key';
}
return undefined;
};
var getTracedCalleeKey = function(MACHINE) {
if (MACHINE.modules['whalesong/lang/private/traced-app.rkt']) {
return MACHINE.modules['whalesong/lang/private/traced-app.rkt'].getNamespace().get('traced-callee-key') || 'traced-callee-key';
}
return undefined;
};
// captureControl implements the continuation-capturing part of
// call/cc. It grabs the control frames up to (but not including) the
// prompt tagged by the given tag.
Machine.prototype.captureControl = function(skip, tag) {
var MACHINE = this;
var i;
for (i = MACHINE.c.length - 1 - skip; i >= 0; i--) {
if (MACHINE.c[i].tag === tag) {
return MACHINE.c.slice(i + 1,
MACHINE.c.length - skip);
}
}
raise(MACHINE, new Error("captureControl: unable to find tag " + tag));
};
// restoreControl clears the control stack (up to, but not including the
// 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
// top of the environment.
Machine.prototype.restoreControl = function(tag) {
var MACHINE = this;
var i;
for (i = MACHINE.c.length - 1; i >= 0; i--) {
if (MACHINE.c[i].tag === tag) {
MACHINE.c =
MACHINE.c.slice(0, i+1).concat(
MACHINE.e[MACHINE.e.length - 1]);
return;
}
}
raise(MACHINE, new Error("restoreControl: unable to find tag " + tag));
};
// Splices the list argument in the environment. Adjusts MACHINE.a
// appropriately.
Machine.prototype.spliceListIntoStack = function(depth) {
var MACHINE = this;
var lst = MACHINE.e[MACHINE.e.length - 1 - depth];
var vals = [];
while(lst !== NULL) {
vals.push(lst.first);
lst = lst.rest;
}
vals.reverse();
MACHINE.e.splice.apply(MACHINE.e,
[MACHINE.e.length - 1 - depth, 1].concat(vals));
MACHINE.a = MACHINE.a + vals.length - 1;
};
// Unsplices a list from the MACHINE stack.
Machine.prototype.unspliceRestFromStack = function(depth, length) {
var MACHINE = this;
var lst = NULL;
var i;
for (i = 0; i < length; i++) {
lst = makePair(MACHINE.e[MACHINE.e.length - depth - length + i],
lst);
}
MACHINE.e.splice(MACHINE.e.length - depth - length,
length,
lst);
MACHINE.a = MACHINE.a - length + 1;
};
// Save the continuation mark on the top control frame.
Machine.prototype.installContinuationMarkEntry = function(key, value) {
var frame = this.c[this.c.length - 1];
var marks = frame.getMarks();
var i;
var l = marks.length;
for (i = 0; i < l; i++) {
if (key === marks[i][0]) {
marks[i][1] = value;
return;
}
}
marks.push([key, value]);
};
Machine.prototype.captureContinuationMarks = function(promptTag) {
var kvLists = [];
var i;
var control = this.c;
var tracedCalleeKey = getTracedCalleeKey(this);
for (i = control.length-1; i >= 0; i--) {
if (promptTag !== null &&
control[i] instanceof PromptFrame && control[i].tag === promptTag) {
break;
}
if (control[i].getMarks().length !== 0) {
kvLists.push(control[i].getMarks());
}
if (tracedCalleeKey !== null &&
control[i] instanceof CallFrame &&
control[i].p !== null) {
kvLists.push([[tracedCalleeKey, control[i].p]]);
}
}
return new baselib.contmarks.ContinuationMarkSet(kvLists);
};
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// The toplevel trampoline.
//
//
// trampoline: MACHINE (MACHINE -> void) -> void
//
// All evaluation in Racketland happens in the context of this
// trampoline.
//
var recomputeMaxNumBouncesBeforeYield;
var scheduleTrampoline = function(MACHINE, f, release) {
setTimeout(
function() {
MACHINE._trampoline(f, false, release);
},
0);
};
// Creates a restarting function, that reschedules f in a context
// with the old argcount in place.
// Meant to be used only by the trampoline.
var makeRestartFunction = function(MACHINE, release, pauseLock) {
var oldArgcount = MACHINE.a;
return function(f) {
pauseLock.acquire(
undefined,
function(pauseReleaseLock) {
MACHINE.a = oldArgcount;
MACHINE._trampoline(f, false, release);
pauseReleaseLock();
});
};
};
// These are exception values that are treated specially in the context
// of the trampoline.
var HaltError = function(onHalt) {
// onHalt: MACHINE -> void
this.onHalt = onHalt || function(MACHINE) {};
};
var Pause = function(onPause) {
// onPause: MACHINE -> void
this.onPause = onPause || function(MACHINE) {};
};
var PAUSE = function(onPause) {
throw(new Pause(onPause));
};
// 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) {
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 startTime = (new Date()).valueOf();
that.cbt = STACK_LIMIT_ESTIMATE;
that.params.numBouncesBeforeYield =
that.params.maxNumBouncesBeforeYield;
that.running = true;
while(true) {
try {
thunk(that);
break;
} catch (e) {
// There are a few kinds of things that can get thrown
// during racket evaluation:
//
// functions: this gets thrown if the Racket code
// realizes that the number of bounces has grown too
// large. The thrown function represents a restarter
// function. The running flag remains true.
//
// Pause: causes the machine evaluation to pause, with
// the expectation that it will restart momentarily.
// The running flag on the machine will remain true.
//
// HaltError: causes evaluation to immediately halt.
// We schedule the onHalt function of the HaltError to
// call afterwards. The running flag on the machine
// is set to false.
//
// Everything else: otherwise, we send the exception value
// to the current error handler and exit.
// The running flag is set to false.
if (typeof(e) === 'function') {
thunk = e;
that.cbt = STACK_LIMIT_ESTIMATE;
// If we're running an a model that prohibits
// jumping off the trampoline, continue.
if (noJumpingOff) {
continue;
}
if (that.params.numBouncesBeforeYield-- < 0) {
recomputeMaxNumBouncesBeforeYield(
that,
(new Date()).valueOf() - startTime);
scheduleTrampoline(that, thunk, release);
return;
}
} else if (e instanceof Pause) {
var pauseLock = new ExclusiveLock();
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;
} else if (e instanceof HaltError) {
that.running = false;
e.onHalt(that);
release();
return;
} else {
// General error condition: just exit out
// of the trampoline and call the current error handler.
that.running = false;
that.params.currentErrorHandler(that, e);
release();
return;
}
}
}
that.running = false;
that.params.currentSuccessHandler(that);
release();
return;
};
// recomputeGas: state number -> number
recomputeMaxNumBouncesBeforeYield = function(MACHINE, observedDelay) {
// We'd like to see a delay of DESIRED_DELAY_BETWEEN_BOUNCES so
// that we get MACHINE.params.desiredYieldsPerSecond bounces per
// second.
var DESIRED_DELAY_BETWEEN_BOUNCES =
(1000 / MACHINE.params.desiredYieldsPerSecond);
var ALPHA = 50;
var delta = (ALPHA * ((DESIRED_DELAY_BETWEEN_BOUNCES -
observedDelay) /
DESIRED_DELAY_BETWEEN_BOUNCES));
MACHINE.params.maxNumBouncesBeforeYield =
Math.max(Math.floor(MACHINE.params.maxNumBouncesBeforeYield + delta),
1);
};
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// There is a single, distinguished default continuation prompt tag
// that's used to wrap around toplevel prompts.
var DEFAULT_CONTINUATION_PROMPT_TAG =
baselib.contmarks.DEFAULT_CONTINUATION_PROMPT_TAG;
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
var VariableReference = function(prefix, pos) {
this.prefix = prefix;
this.pos = pos;
};
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// Implementation of the ready function. This will fire off when
// setReadyTrue is called.
var ready, setReadyTrue, setReadyFalse;
(function() {
var runtimeIsReady = true;
var readyWaiters = [];
var notifyWaiter = function(w) {
w();
};
ready = function(f) {
if (runtimeIsReady) {
notifyWaiter(f);
} else {
readyWaiters.push(f);
}
};
setReadyTrue = function() {
runtimeIsReady = true;
while(runtimeIsReady && readyWaiters.length > 0) {
notifyWaiter(readyWaiters.shift());
}
};
setReadyFalse = function() {
runtimeIsReady = false;
};
}());
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// Executes all programs that have been labeled as a main module
var invokeMains = function(machine, succ, fail) {
runtime.ready(function () {
if (window.console && window.console.log) {
window.console.log("invoking main modules");
}
setReadyFalse();
machine = machine || runtime.currentMachine;
succ = succ || function() {};
fail = fail || function() {};
var mainModules = machine.mainModules.slice();
var loop = function() {
if (mainModules.length > 0) {
var nextModule = mainModules.shift();
nextModule.invoke(machine, loop, fail);
} else {
setReadyTrue();
succ();
}
};
setTimeout(loop, 0);
});
};
// Looks up a name in any of the machine's main modules.
var lookupInMains = function(name, machine) {
var i;
machine = machine || runtime.currentMachine;
for (i = 0; i < machine.mainModules.length; i++) {
var ns = machine.mainModules[i].getNamespace();
if(ns.hasKey(name)) {
return ns.get(name);
}
}
};
var checkClosureAndArity = function(M) {
if(!(M.p instanceof Closure)){
raiseOperatorApplicationError(M,M.p);
}
if(!isArityMatching(M.p.racketArity,M.a)) {
raiseArityMismatchError(M,M.p,M.a);
}
};
var checkPrimitiveArity = function(M) {
if(!isArityMatching(M.p.racketArity,M.a)) {
raiseArityMismatchError(M,M.p,M.a);
}
};
//////////////////////////////////////////////////////////////////////
// Superinstructions to try to reduce code size.
var si_context_expected = function(n) {
if (n === 1) { return si_context_expected_1; }
var f = function(M) { raiseContextExpectedValuesError(M, n); };
return f;
};
var si_context_expected_1 = function(M) { raiseContextExpectedValuesError(M, 1); }
// A block that omits the multiple values returned on the stack and
// continues on with the target function f.
var si_pop_multiple_values_and_continue = function(target) {
var f = function(M) {
if(--M.cbt<0) { throw f; }
M.e.length -= (M.a-1);
return target(M);
};
return f;
};
//////////////////////////////////////////////////////////////////////
var checkedIsZero = function(M, n) {
if (typeof(n) === 'number') { return n===0; }
return plt.baselib.numbers.equals(
testArgument(M, 'number', isNumber, n, 0, 'zero?'),
0);
};
var checkedAdd1 = function(M, n) {
if (typeof(n) === 'number' && n < 9e15) { return n+1; }
return plt.baselib.numbers.add(
testArgument(M, 'number', isNumber, n, 0, 'add1'),
1);
};
var checkedSub1 = function(M, n) {
if (typeof(n) === 'number' && n > -9e15) { return n-1; }
return plt.baselib.numbers.subtract(
testArgument(M, 'number', isNumber, n, 0, 'sub1'),
1);
};
var checkedNegate = function(M, n) {
if (typeof(n) === 'number') { return -n; }
return plt.baselib.numbers.subtract(
0,
testArgument(M, 'number', isNumber, n, 0, '-'));
};
var checkedAdd = function(M, x, y) {
var sum, i;
// fast path optimization: binop addition on fixnums
if (arguments.length === 3) {
if (typeof(x) === 'number' && typeof(y) === 'number') {
sum = x + y;
if (sum < -9e15 || sum > 9e15) {
return plt.baselib.numbers.add(x, y);
}
return sum;
} else {
return plt.baselib.numbers.add(x, y);
}
}
// Secondary path: if everything is a fixnum...
sum = 0;
for (i = 1; i < arguments.length; i++) {
if (typeof(arguments[i]) === 'number') {
sum += arguments[i];
if (sum < -9e15 || sum > 9e15) {
return checkedAddSlowPath(M, Array.prototype.slice.call(arguments, 1));
}
} else {
return checkedAddSlowPath(M, Array.prototype.slice.call(arguments, 1));
}
}
return sum;
};
var checkedAddSlowPath = function(M, args) {
var i;
var sum = 0;
for (i = 0; i < args.length; i++) {
if (! isNumber(args[i])) {
raiseArgumentTypeError(M, '+', 'number', i, args[i]);
}
sum = plt.baselib.numbers.add(sum, args[i]);
}
return sum;
};
var checkedMul = function(M, x, y) {
var prod, i;
// fast path optimization: binop addition on fixnums
if (arguments.length === 3) {
if (typeof(x) === 'number' && typeof(y) === 'number') {
prod = x * y;
if (prod < -9e15 || prod > 9e15) {
return plt.baselib.numbers.multiply(x, y);
}
return prod;
} else {
return plt.baselib.numbers.multiply(x, y);
}
}
// Secondary path: if everything is a fixnum...
prod = 1;
for (i = 1; i < arguments.length; i++) {
if (typeof(arguments[i]) === 'number') {
prod *= arguments[i];
if (prod < -9e15 || prod > 9e15) {
return checkedMulSlowPath(M, Array.prototype.slice.call(arguments, 1));
}
} else {
return checkedMulSlowPath(M, Array.prototype.slice.call(arguments, 1));
}
}
return prod;
};
var checkedMulSlowPath = function(M, args) {
var i, prod;
if (! isNumber(args[0])) {
raiseArgumentTypeError(M, '*', 'number', 0, args[0]);
}
var prod = args[0];
for (i = 1; i < args.length; i++) {
if (! isNumber(args[i])) {
raiseArgumentTypeError(M, '*', 'number', i, args[i]);
}
prod = plt.baselib.numbers.multiply(prod, args[i]);
}
return prod;
};
var checkedSub = function(M, x, y) {
// Assumption: at least two arguments to subtract.
var sum;
// fast path optimization: binop subtraction on fixnums
if (arguments.length === 3) {
if (typeof(x) === 'number' && typeof(y) === 'number') {
sum = x - y;
if (sum < -9e15 || sum > 9e15) {
return checkedSubSlowPath(M, Array.prototype.slice.call(arguments, 1));
}
return sum;
} else {
return plt.baselib.numbers.subtract(x, y);
}
}
return checkedSubSlowPath(M, Array.prototype.slice.call(arguments, 1));
};
var checkedSubSlowPath = function(M, args) {
var i;
if (! isNumber(args[0])) {
raiseArgumentTypeError(M, '-', 'number', 0, args[0]);
}
var sum = args[0];
for (i = 1; i < args.length; i++) {
if (! isNumber(args[i])) {
raiseArgumentTypeError(M, '-', 'number', i, args[i]);
}
sum = plt.baselib.numbers.subtract(sum, args[i]);
}
return sum;
};
var checkedGreaterThan = function(M, x, y) {
// fast path optimization: binop comparison on fixnums
if (arguments.length === 3) {
if (typeof(x) === 'number' && typeof(y) === 'number') {
return x > y;
} else {
return plt.baselib.numbers.greaterThan(x, y);
}
}
return checkedGreaterThanSlowPath(M, Array.prototype.slice.call(arguments, 1));
};
var checkedGreaterThanSlowPath = function(M, args) {
var i;
if (! isNumber(args[0])) {
raiseArgumentTypeError(M, '>', 'number', 0, args[0]);
}
for (i = 1; i < args.length ; i++) {
if (! isNumber(args[i])) {
raiseArgumentTypeError(M, '>', 'number', i, args[i]);
}
if (! plt.baselib.numbers.greaterThan(args[i-1], args[i])) {
return false;
}
}
return true;
};
var checkedNumEquals = function(M, x, y) {
// Assumption: at least two arguments to compare
var i;
// fast path optimization: binop comparison on fixnums
if (arguments.length === 3) {
if (typeof(x) === 'number' && typeof(y) === 'number') {
return x === y;
} else {
return plt.baselib.numbers.equals(x, y);
}
}
if (typeof(arguments[1]) !== 'number') {
return checkedNumEqualsSlowPath(M, Array.prototype.slice.call(arguments, 1));
}
var n = arguments[1];
for (i = 2; i < arguments.length; i++) {
if (typeof(arguments[i]) === 'number') {
if (n !== arguments[i]) { return false; }
} else {
return checkedNumEqualsSlowPath(M, Array.prototype.slice.call(arguments, 1));
}
}
return true;
};
var checkedNumEqualsSlowPath = function(M, args) {
var i;
if (! isNumber(args[0])) {
raiseArgumentTypeError(M, '=', 'number', 0, args[0]);
}
var n = args[0];
for (i = 1; i < args.length; i++) {
if (! isNumber(args[i])) {
raiseArgumentTypeError(M, '=', 'number', i, args[i]);
}
if (! plt.baselib.numbers.equals(n, args[i])) {
return false;
}
}
return true;
};
var checkedCar = function(M, v) {
if (isPair(v)) { return v.first; }
raiseArgumentTypeError(M, 'car', 'pair', 0, v);
};
var checkedCdr = function(M, v) {
if (isPair(v)) { return v.rest; }
raiseArgumentTypeError(M, 'cdr', 'pair', 0, v);
};
var checkedVectorRef = function(M, vec, i) {
var expectedTypeName;
if (isVector(vec)) {
if (typeof(i) === 'number') {
if (i >= 0 && i < vec.elts.length) {
return vec.elts[i];
}
} else if (isNumber(i)) {
i = baselib.numbers.toFixnum(i);
if (i >= 0 && i < vec.elts.length) {
return vec.elts[i];
}
}
expectedTypeName = baselib.format.format('natural between 0 and ~a',
[vec.elts.length]);
raiseArgumentTypeError(M, 'vector-ref', expectedTypeName, 1, i);
} else {
raiseArgumentTypeError(M, 'vector-ref', 'vector', 0, vec);
}
};
var checkedVectorSet = function(M, vec, i, val) {
var expectedTypeName;
if (isVector(vec)) {
if (typeof(i) === 'number') {
if (i >= 0 && i < vec.elts.length) {
vec.elts[i] = val;
return VOID;
}
} else if (isNumber(i)) {
i = baselib.numbers.toFixnum(i);
if (i >= 0 && i < vec.elts.length) {
vec.elts[i] = val;
return VOID;
}
}
expectedTypeName = baselib.format.format('natural between 0 and ~a',
[vec.elts.length]);
raiseArgumentTypeError(M, 'vector-set!', expectedTypeName, 1, i);
} else {
raiseArgumentTypeError(M, 'vector-set!', 'vector', 0, vec);
}
};
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
// Exports
var exports = runtime;
exports['currentMachine'] = new Machine();
exports['invokeMains'] = invokeMains;
exports['lookupInMains'] = lookupInMains;
// installing new primitives
exports['installPrimitiveProcedure'] = installPrimitiveProcedure;
exports['makePrimitiveProcedure'] = makePrimitiveProcedure;
exports['Primitives'] = Primitives;
exports['ready'] = ready;
// Private: the runtime library will set this flag to true when
// the library has finished loading.
exports['setReadyTrue'] = setReadyTrue;
exports['setReadyFalse'] = setReadyFalse;
exports['Machine'] = Machine;
exports['Frame'] = Frame;
exports['CallFrame'] = CallFrame;
exports['PromptFrame'] = PromptFrame;
exports['Closure'] = Closure;
exports['ModuleRecord'] = ModuleRecord;
exports['VariableReference'] = VariableReference;
exports['ContinuationPromptTag'] = ContinuationPromptTag;
exports['DEFAULT_CONTINUATION_PROMPT_TAG'] =
DEFAULT_CONTINUATION_PROMPT_TAG;
exports['NULL'] = NULL;
exports['VOID'] = VOID;
exports['NEGATIVE_ZERO'] = NEGATIVE_ZERO;
exports['INF'] = INF;
exports['NEGATIVE_INF'] = NEGATIVE_INF;
exports['NAN'] = NAN;
exports['testArgument'] = testArgument;
exports['testArity'] = testArity;
exports['makeCheckArgumentType'] = makeCheckArgumentType;
exports['raise'] = raise;
exports['raiseUnboundToplevelError'] = raiseUnboundToplevelError;
exports['raiseArgumentTypeError'] = raiseArgumentTypeError;
exports['raiseContextExpectedValuesError'] = raiseContextExpectedValuesError;
exports['raiseArityMismatchError'] = raiseArityMismatchError;
exports['raiseOperatorApplicationError'] = raiseOperatorApplicationError;
exports['raiseOperatorIsNotPrimitiveProcedure'] = raiseOperatorIsNotPrimitiveProcedure;
exports['raiseUnimplementedPrimitiveError'] = raiseUnimplementedPrimitiveError;
exports['finalizeClosureCall'] = finalizeClosureCall;
//////////////////////////////////////////////////////////////////////
// Type constructors
// numbers
exports['makeList'] = makeList;
exports['makePair'] = makePair;
exports['makeChar'] = makeChar;
exports['makeVector'] = makeVector;
exports['makeBox'] = makeBox;
exports['makeFloat'] = makeFloat;
exports['makeRational'] = makeRational;
exports['makeBignum'] = makeBignum;
exports['makeComplex'] = makeComplex;
exports['makeSymbol'] = makeSymbol;
exports['makePath'] = makePath;
exports['makeBytes'] = makeBytes;
exports['makeBytesFromBase64'] = makeBytesFromBase64;
exports['checkPair'] = baselib.check.checkPair;
exports['checkNumber'] = baselib.check.checkNumber;
exports['checkString'] = baselib.check.checkString;
// Type predicates
exports['isPair'] = isPair;
exports['isCaarPair'] = isCaarPair;
exports['isList'] = isList;
exports['isVector'] = isVector;
exports['isOutputPort'] = isOutputPort;
exports['isOutputStringPort'] = isOutputStringPort;
exports['isBox'] = isBox;
exports['isString'] = isString;
exports['isSymbol'] = isSymbol;
exports['isPath'] = isPath;
exports['isNumber'] = isNumber;
exports['isNatural'] = isNatural;
exports['isReal'] = isReal;
exports['isProcedure'] = plt.baselib.functions.isProcedure;
exports['equals'] = equals;
exports['toDomNode'] = toDomNode;
exports['toWrittenString'] = toWrittenString;
exports['toDisplayedString'] = toDisplayedString;
exports['ArityAtLeast'] = ArityAtLeast;
exports['makeArityAtLeast'] = makeArityAtLeast;
exports['isArityMatching'] = isArityMatching;
exports['heir'] = heir;
exports['makeClassPredicate'] = makeClassPredicate;
exports['PAUSE'] = PAUSE;
exports['HaltError'] = HaltError;
exports['makeStructureType'] = makeStructureType;
exports['Struct'] = Struct;
exports['StructType'] = StructType;
exports['getTracedAppKey'] = getTracedAppKey;
exports['getTracedCalleeKey'] = getTracedCalleeKey;
exports['si_context_expected'] = si_context_expected;
exports['si_context_expected_1'] = si_context_expected_1;
exports['checkClosureAndArity'] = checkClosureAndArity;
exports['checkPrimitiveArity'] = checkPrimitiveArity;
exports['checkedIsZero'] = checkedIsZero;
exports['checkedAdd1'] = checkedAdd1;
exports['checkedSub1'] = checkedSub1;
exports['checkedNegate'] = checkedNegate;
exports['checkedAdd'] = checkedAdd;
exports['checkedAddSlowPath'] = checkedAddSlowPath;
exports['checkedMul'] = checkedMul;
exports['checkedMulSlowPath'] = checkedMulSlowPath;
exports['checkedSub'] = checkedSub;
exports['checkedSubSlowPath'] = checkedSubSlowPath;
exports['checkedNumEquals'] = checkedNumEquals;
exports['checkedNumEqualsSlowPath'] = checkedNumEqualsSlowPath;
exports['checkedGreaterThan'] = checkedGreaterThan;
exports['checkedGreaterThanSlowPath'] = checkedGreaterThanSlowPath;
exports['checkedCar'] = checkedCar;
exports['checkedCdr'] = checkedCdr;
exports['checkedVectorRef'] = checkedVectorRef;
exports['checkedVectorSet'] = checkedVectorSet;
}(this.plt, this.plt.baselib));