[JAEGER] Merge from tracemonkey.

This commit is contained in:
David Mandelin 2010-08-11 11:36:32 -07:00
commit a5e8e278f4
16 changed files with 10801 additions and 21 deletions

View File

@ -1,7 +1,7 @@
<html>
<script>
try {
top.SimpleTest.ok(!("existingprop" in location), "got a new location object in the iframe");
parent.SimpleTest.ok(!("existingprop" in location), "got a new location object in the iframe");
} catch (e) {
}

View File

@ -148,8 +148,8 @@ jsd_NewThreadState(JSDContext* jsdc, JSContext *cx )
* is not enabled for debugging, fail the entire thread state.
*/
JS_INIT_CLIST(&jsdthreadstate->links);
jsd_DestroyThreadState(jsdc, jsdthreadstate);
JS_EndRequest(jsdthreadstate->context);
jsd_DestroyThreadState(jsdc, jsdthreadstate);
return NULL;
}
}

View File

@ -1259,7 +1259,10 @@ JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp)
JS_PUBLIC_API(JSObject *)
JS_GetFrameThis(JSContext *cx, JSStackFrame *fp)
{
return fp->getThisObject(cx);
if (fp->isDummyFrame())
return NULL;
else
return fp->getThisObject(cx);
}
JS_PUBLIC_API(JSFunction *)

View File

@ -1192,21 +1192,27 @@ InvokeConstructor(JSContext *cx, const CallArgs &argsRef)
return false;
}
Class *clasp = &js_ObjectClass;
/*
* Call fast constructors without making the object first.
* The native will be able to make the right new object faster.
*/
if (obj2->isFunction()) {
JSFunction *fun = (JSFunction *) obj2->getPrivate();
JSFunction *fun = GET_FUNCTION_PRIVATE(cx, obj2);
if (fun->isFastConstructor()) {
args.thisv().setMagic(JS_FAST_CONSTRUCTOR);
FastNative fn = (FastNative)fun->u.n.native;
if (!fn(cx, args.argc(), args.base()))
return JS_FALSE;
JS_ASSERT(!args.callee().isPrimitive());
JS_ASSERT(!args.rval().isPrimitive());
return JS_TRUE;
}
/* Get the class, for natives that aren't fast constructors. */
if (!fun->isInterpreted() && fun->u.n.clasp)
clasp = fun->u.n.clasp;
}
Value protov;
@ -1215,13 +1221,6 @@ InvokeConstructor(JSContext *cx, const CallArgs &argsRef)
JSObject *proto = protov.isObjectOrNull() ? protov.toObjectOrNull() : NULL;
JSObject *parent = obj2->getParent();
Class *clasp = &js_ObjectClass;
if (obj2->getClass() == &js_FunctionClass) {
JSFunction *f = GET_FUNCTION_PRIVATE(cx, obj2);
if (!f->isInterpreted() && f->u.n.clasp)
clasp = f->u.n.clasp;
}
JSObject* obj = NewObject<WithProto::Class>(cx, clasp, proto, parent);
if (!obj)

View File

@ -69,7 +69,8 @@ enum JSFrameFlags {
JSFRAME_BAILING = 0x100, /* walking out of a method JIT'd frame */
JSFRAME_RECORDING = 0x200, /* recording a trace */
JSFRAME_BAILED_AT_RETURN = 0x400, /* bailed at JSOP_RETURN */
JSFRAME_DUMMY = 0x800, /* frame is a dummy frame */
JSFRAME_SPECIAL = JSFRAME_DEBUGGER | JSFRAME_EVAL
};
@ -244,7 +245,7 @@ struct JSStackFrame
return !!(flags & JSFRAME_FLOATING_GENERATOR);
}
bool isDummyFrame() const { return !script && !fun; }
bool isDummyFrame() const { return !!(flags & JSFRAME_DUMMY); }
private:
JSObject *computeThisObject(JSContext *cx);
@ -545,6 +546,7 @@ js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth);
inline JSObject *
JSStackFrame::getThisObject(JSContext *cx)
{
JS_ASSERT(!isDummyFrame());
return thisv.isPrimitive() ? computeThisObject(cx) : &thisv.toObject();
}

View File

@ -11482,9 +11482,12 @@ TraceRecorder::callNative(uintN argc, JSOp mode)
if (!clasp->isNative())
RETURN_STOP("new with non-native ops");
if (fun->flags & JSFUN_FAST_NATIVE_CTOR) {
if (fun->isFastConstructor()) {
vp[1].setMagic(JS_FAST_CONSTRUCTOR);
newobj_ins = INS_CONSTWORD(JS_FAST_CONSTRUCTOR);
newobj_ins = INS_CONST(JS_FAST_CONSTRUCTOR);
/* Treat this as a regular call, the constructor will behave correctly. */
mode = JSOP_CALL;
} else {
args[0] = INS_CONSTOBJ(funobj);
args[1] = INS_CONSTPTR(clasp);
@ -13541,10 +13544,11 @@ TraceRecorder::record_NativeCallComplete()
if (pendingSpecializedNative == IGNORE_NATIVE_CALL_COMPLETE_CALLBACK)
return ARECORD_CONTINUE;
jsbytecode* pc = cx->regs->pc;
#ifdef DEBUG
JS_ASSERT(pendingSpecializedNative);
jsbytecode* pc = cx->regs->pc;
JS_ASSERT(*pc == JSOP_CALL || *pc == JSOP_APPLY || *pc == JSOP_NEW || *pc == JSOP_SETPROP);
#endif
Value& v = stackval(-1);
LIns* v_ins = get(&v);
@ -13578,7 +13582,7 @@ TraceRecorder::record_NativeCallComplete()
* indicating the error status.
*/
if (*pc == JSOP_NEW) {
if (pendingSpecializedNative->flags & JSTN_CONSTRUCTOR) {
LIns *cond_ins;
LIns *x;

View File

@ -490,6 +490,7 @@ SetupFakeFrame(JSContext *cx, ExecuteFrameGuard &frame, JSFrameRegs &regs, JSObj
PodZero(fp); // fp->fun and fp->script are both NULL
fp->argv = vp + 2;
fp->scopeChain = obj->getGlobal();
fp->flags = JSFRAME_DUMMY;
regs.pc = NULL;
regs.sp = fp->slots();

View File

@ -2935,7 +2935,7 @@ split_create_inner(JSContext *cx, JSObject *outer)
cpx->outer = outer;
obj = JS_NewGlobalObject(cx, Jsvalify(&split_global_class));
if (!obj || !JS_SetParent(cx, obj, NULL)) {
if (!obj || !JS_SetPrivate(cx, obj, cpx)) {
JS_free(cx, cpx);
return NULL;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,883 @@
// Copyright 2008 the V8 project authors. All rights reserved.
// Copyright 1996 John Maloney and Mario Wolczko.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// This implementation of the DeltaBlue benchmark is derived
// from the Smalltalk implementation by John Maloney and Mario
// Wolczko. Some parts have been translated directly, whereas
// others have been modified more aggresively to make it feel
// more like a JavaScript program.
//var DeltaBlue = new BenchmarkSuite('DeltaBlue', 71104, [
// new Benchmark('DeltaBlue', deltaBlue)
//]);
/**
* A JavaScript implementation of the DeltaBlue constrain-solving
* algorithm, as described in:
*
* "The DeltaBlue Algorithm: An Incremental Constraint Hierarchy Solver"
* Bjorn N. Freeman-Benson and John Maloney
* January 1990 Communications of the ACM,
* also available as University of Washington TR 89-08-06.
*
* Beware: this benchmark is written in a grotesque style where
* the constraint model is built by side-effects from constructors.
* I've kept it this way to avoid deviating too much from the original
* implementation.
*/
function alert(msg) {
print(msg);
assertEq(false, true);
}
/* --- O b j e c t M o d e l --- */
Object.prototype.inheritsFrom = function (shuper) {
function Inheriter() { }
Inheriter.prototype = shuper.prototype;
this.prototype = new Inheriter();
this.superConstructor = shuper;
}
function OrderedCollection() {
this.elms = new Array();
}
OrderedCollection.prototype.add = function (elm) {
this.elms.push(elm);
}
OrderedCollection.prototype.at = function (index) {
return this.elms[index];
}
OrderedCollection.prototype.size = function () {
return this.elms.length;
}
OrderedCollection.prototype.removeFirst = function () {
return this.elms.pop();
}
OrderedCollection.prototype.remove = function (elm) {
var index = 0, skipped = 0;
for (var i = 0; i < this.elms.length; i++) {
var value = this.elms[i];
if (value != elm) {
this.elms[index] = value;
index++;
} else {
skipped++;
}
}
for (var i = 0; i < skipped; i++)
this.elms.pop();
}
/* --- *
* S t r e n g t h
* --- */
/**
* Strengths are used to measure the relative importance of constraints.
* New strengths may be inserted in the strength hierarchy without
* disrupting current constraints. Strengths cannot be created outside
* this class, so pointer comparison can be used for value comparison.
*/
function Strength(strengthValue, name) {
this.strengthValue = strengthValue;
this.name = name;
}
Strength.stronger = function (s1, s2) {
return s1.strengthValue < s2.strengthValue;
}
Strength.weaker = function (s1, s2) {
return s1.strengthValue > s2.strengthValue;
}
Strength.weakestOf = function (s1, s2) {
return this.weaker(s1, s2) ? s1 : s2;
}
Strength.strongest = function (s1, s2) {
return this.stronger(s1, s2) ? s1 : s2;
}
Strength.prototype.nextWeaker = function () {
switch (this.strengthValue) {
case 0: return Strength.WEAKEST;
case 1: return Strength.WEAK_DEFAULT;
case 2: return Strength.NORMAL;
case 3: return Strength.STRONG_DEFAULT;
case 4: return Strength.PREFERRED;
case 5: return Strength.REQUIRED;
}
}
// Strength constants.
Strength.REQUIRED = new Strength(0, "required");
Strength.STONG_PREFERRED = new Strength(1, "strongPreferred");
Strength.PREFERRED = new Strength(2, "preferred");
Strength.STRONG_DEFAULT = new Strength(3, "strongDefault");
Strength.NORMAL = new Strength(4, "normal");
Strength.WEAK_DEFAULT = new Strength(5, "weakDefault");
Strength.WEAKEST = new Strength(6, "weakest");
/* --- *
* C o n s t r a i n t
* --- */
/**
* An abstract class representing a system-maintainable relationship
* (or "constraint") between a set of variables. A constraint supplies
* a strength instance variable; concrete subclasses provide a means
* of storing the constrained variables and other information required
* to represent a constraint.
*/
function Constraint(strength) {
this.strength = strength;
}
/**
* Activate this constraint and attempt to satisfy it.
*/
Constraint.prototype.addConstraint = function () {
this.addToGraph();
planner.incrementalAdd(this);
}
/**
* Attempt to find a way to enforce this constraint. If successful,
* record the solution, perhaps modifying the current dataflow
* graph. Answer the constraint that this constraint overrides, if
* there is one, or nil, if there isn't.
* Assume: I am not already satisfied.
*/
Constraint.prototype.satisfy = function (mark) {
this.chooseMethod(mark);
if (!this.isSatisfied()) {
if (this.strength == Strength.REQUIRED)
alert("Could not satisfy a required constraint!");
return null;
}
this.markInputs(mark);
var out = this.output();
var overridden = out.determinedBy;
if (overridden != null) overridden.markUnsatisfied();
out.determinedBy = this;
if (!planner.addPropagate(this, mark))
alert("Cycle encountered");
out.mark = mark;
return overridden;
}
Constraint.prototype.destroyConstraint = function () {
if (this.isSatisfied()) planner.incrementalRemove(this);
else this.removeFromGraph();
}
/**
* Normal constraints are not input constraints. An input constraint
* is one that depends on external state, such as the mouse, the
* keybord, a clock, or some arbitraty piece of imperative code.
*/
Constraint.prototype.isInput = function () {
return false;
}
/* --- *
* U n a r y C o n s t r a i n t
* --- */
/**
* Abstract superclass for constraints having a single possible output
* variable.
*/
function UnaryConstraint(v, strength) {
UnaryConstraint.superConstructor.call(this, strength);
this.myOutput = v;
this.satisfied = false;
this.addConstraint();
}
UnaryConstraint.inheritsFrom(Constraint);
/**
* Adds this constraint to the constraint graph
*/
UnaryConstraint.prototype.addToGraph = function () {
this.myOutput.addConstraint(this);
this.satisfied = false;
}
/**
* Decides if this constraint can be satisfied and records that
* decision.
*/
UnaryConstraint.prototype.chooseMethod = function (mark) {
this.satisfied = (this.myOutput.mark != mark)
&& Strength.stronger(this.strength, this.myOutput.walkStrength);
}
/**
* Returns true if this constraint is satisfied in the current solution.
*/
UnaryConstraint.prototype.isSatisfied = function () {
return this.satisfied;
}
UnaryConstraint.prototype.markInputs = function (mark) {
// has no inputs
}
/**
* Returns the current output variable.
*/
UnaryConstraint.prototype.output = function () {
return this.myOutput;
}
/**
* Calculate the walkabout strength, the stay flag, and, if it is
* 'stay', the value for the current output of this constraint. Assume
* this constraint is satisfied.
*/
UnaryConstraint.prototype.recalculate = function () {
this.myOutput.walkStrength = this.strength;
this.myOutput.stay = !this.isInput();
if (this.myOutput.stay) this.execute(); // Stay optimization
}
/**
* Records that this constraint is unsatisfied
*/
UnaryConstraint.prototype.markUnsatisfied = function () {
this.satisfied = false;
}
UnaryConstraint.prototype.inputsKnown = function () {
return true;
}
UnaryConstraint.prototype.removeFromGraph = function () {
if (this.myOutput != null) this.myOutput.removeConstraint(this);
this.satisfied = false;
}
/* --- *
* S t a y C o n s t r a i n t
* --- */
/**
* Variables that should, with some level of preference, stay the same.
* Planners may exploit the fact that instances, if satisfied, will not
* change their output during plan execution. This is called "stay
* optimization".
*/
function StayConstraint(v, str) {
StayConstraint.superConstructor.call(this, v, str);
}
StayConstraint.inheritsFrom(UnaryConstraint);
StayConstraint.prototype.execute = function () {
// Stay constraints do nothing
}
/* --- *
* E d i t C o n s t r a i n t
* --- */
/**
* A unary input constraint used to mark a variable that the client
* wishes to change.
*/
function EditConstraint(v, str) {
EditConstraint.superConstructor.call(this, v, str);
}
EditConstraint.inheritsFrom(UnaryConstraint);
/**
* Edits indicate that a variable is to be changed by imperative code.
*/
EditConstraint.prototype.isInput = function () {
return true;
}
EditConstraint.prototype.execute = function () {
// Edit constraints do nothing
}
/* --- *
* B i n a r y C o n s t r a i n t
* --- */
var Direction = new Object();
Direction.NONE = 0;
Direction.FORWARD = 1;
Direction.BACKWARD = -1;
/**
* Abstract superclass for constraints having two possible output
* variables.
*/
function BinaryConstraint(var1, var2, strength) {
BinaryConstraint.superConstructor.call(this, strength);
this.v1 = var1;
this.v2 = var2;
this.direction = Direction.NONE;
this.addConstraint();
}
BinaryConstraint.inheritsFrom(Constraint);
/**
* Decides if this constratint can be satisfied and which way it
* should flow based on the relative strength of the variables related,
* and record that decision.
*/
BinaryConstraint.prototype.chooseMethod = function (mark) {
if (this.v1.mark == mark) {
this.direction = (this.v1.mark != mark && Strength.stronger(this.strength, this.v2.walkStrength))
? Direction.FORWARD
: Direction.NONE;
}
if (this.v2.mark == mark) {
this.direction = (this.v1.mark != mark && Strength.stronger(this.strength, this.v1.walkStrength))
? Direction.BACKWARD
: Direction.NONE;
}
if (Strength.weaker(this.v1.walkStrength, this.v2.walkStrength)) {
this.direction = Strength.stronger(this.strength, this.v1.walkStrength)
? Direction.BACKWARD
: Direction.NONE;
} else {
this.direction = Strength.stronger(this.strength, this.v2.walkStrength)
? Direction.FORWARD
: Direction.BACKWARD
}
}
/**
* Add this constraint to the constraint graph
*/
BinaryConstraint.prototype.addToGraph = function () {
this.v1.addConstraint(this);
this.v2.addConstraint(this);
this.direction = Direction.NONE;
}
/**
* Answer true if this constraint is satisfied in the current solution.
*/
BinaryConstraint.prototype.isSatisfied = function () {
return this.direction != Direction.NONE;
}
/**
* Mark the input variable with the given mark.
*/
BinaryConstraint.prototype.markInputs = function (mark) {
this.input().mark = mark;
}
/**
* Returns the current input variable
*/
BinaryConstraint.prototype.input = function () {
return (this.direction == Direction.FORWARD) ? this.v1 : this.v2;
}
/**
* Returns the current output variable
*/
BinaryConstraint.prototype.output = function () {
return (this.direction == Direction.FORWARD) ? this.v2 : this.v1;
}
/**
* Calculate the walkabout strength, the stay flag, and, if it is
* 'stay', the value for the current output of this
* constraint. Assume this constraint is satisfied.
*/
BinaryConstraint.prototype.recalculate = function () {
var ihn = this.input(), out = this.output();
out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength);
out.stay = ihn.stay;
if (out.stay) this.execute();
}
/**
* Record the fact that this constraint is unsatisfied.
*/
BinaryConstraint.prototype.markUnsatisfied = function () {
this.direction = Direction.NONE;
}
BinaryConstraint.prototype.inputsKnown = function (mark) {
var i = this.input();
return i.mark == mark || i.stay || i.determinedBy == null;
}
BinaryConstraint.prototype.removeFromGraph = function () {
if (this.v1 != null) this.v1.removeConstraint(this);
if (this.v2 != null) this.v2.removeConstraint(this);
this.direction = Direction.NONE;
}
/* --- *
* S c a l e C o n s t r a i n t
* --- */
/**
* Relates two variables by the linear scaling relationship: "v2 =
* (v1 * scale) + offset". Either v1 or v2 may be changed to maintain
* this relationship but the scale factor and offset are considered
* read-only.
*/
function ScaleConstraint(src, scale, offset, dest, strength) {
this.direction = Direction.NONE;
this.scale = scale;
this.offset = offset;
ScaleConstraint.superConstructor.call(this, src, dest, strength);
}
ScaleConstraint.inheritsFrom(BinaryConstraint);
/**
* Adds this constraint to the constraint graph.
*/
ScaleConstraint.prototype.addToGraph = function () {
ScaleConstraint.superConstructor.prototype.addToGraph.call(this);
this.scale.addConstraint(this);
this.offset.addConstraint(this);
}
ScaleConstraint.prototype.removeFromGraph = function () {
ScaleConstraint.superConstructor.prototype.removeFromGraph.call(this);
if (this.scale != null) this.scale.removeConstraint(this);
if (this.offset != null) this.offset.removeConstraint(this);
}
ScaleConstraint.prototype.markInputs = function (mark) {
ScaleConstraint.superConstructor.prototype.markInputs.call(this, mark);
this.scale.mark = this.offset.mark = mark;
}
/**
* Enforce this constraint. Assume that it is satisfied.
*/
ScaleConstraint.prototype.execute = function () {
if (this.direction == Direction.FORWARD) {
this.v2.value = this.v1.value * this.scale.value + this.offset.value;
} else {
this.v1.value = (this.v2.value - this.offset.value) / this.scale.value;
}
}
/**
* Calculate the walkabout strength, the stay flag, and, if it is
* 'stay', the value for the current output of this constraint. Assume
* this constraint is satisfied.
*/
ScaleConstraint.prototype.recalculate = function () {
var ihn = this.input(), out = this.output();
out.walkStrength = Strength.weakestOf(this.strength, ihn.walkStrength);
out.stay = ihn.stay && this.scale.stay && this.offset.stay;
if (out.stay) this.execute();
}
/* --- *
* E q u a l i t y C o n s t r a i n t
* --- */
/**
* Constrains two variables to have the same value.
*/
function EqualityConstraint(var1, var2, strength) {
EqualityConstraint.superConstructor.call(this, var1, var2, strength);
}
EqualityConstraint.inheritsFrom(BinaryConstraint);
/**
* Enforce this constraint. Assume that it is satisfied.
*/
EqualityConstraint.prototype.execute = function () {
this.output().value = this.input().value;
}
/* --- *
* V a r i a b l e
* --- */
/**
* A constrained variable. In addition to its value, it maintain the
* structure of the constraint graph, the current dataflow graph, and
* various parameters of interest to the DeltaBlue incremental
* constraint solver.
**/
function Variable(name, initialValue) {
this.value = initialValue || 0;
this.constraints = new OrderedCollection();
this.determinedBy = null;
this.mark = 0;
this.walkStrength = Strength.WEAKEST;
this.stay = true;
this.name = name;
}
/**
* Add the given constraint to the set of all constraints that refer
* this variable.
*/
Variable.prototype.addConstraint = function (c) {
this.constraints.add(c);
}
/**
* Removes all traces of c from this variable.
*/
Variable.prototype.removeConstraint = function (c) {
this.constraints.remove(c);
if (this.determinedBy == c) this.determinedBy = null;
}
/* --- *
* P l a n n e r
* --- */
/**
* The DeltaBlue planner
*/
function Planner() {
this.currentMark = 0;
}
/**
* Attempt to satisfy the given constraint and, if successful,
* incrementally update the dataflow graph. Details: If satifying
* the constraint is successful, it may override a weaker constraint
* on its output. The algorithm attempts to resatisfy that
* constraint using some other method. This process is repeated
* until either a) it reaches a variable that was not previously
* determined by any constraint or b) it reaches a constraint that
* is too weak to be satisfied using any of its methods. The
* variables of constraints that have been processed are marked with
* a unique mark value so that we know where we've been. This allows
* the algorithm to avoid getting into an infinite loop even if the
* constraint graph has an inadvertent cycle.
*/
Planner.prototype.incrementalAdd = function (c) {
var mark = this.newMark();
var overridden = c.satisfy(mark);
while (overridden != null)
overridden = overridden.satisfy(mark);
}
/**
* Entry point for retracting a constraint. Remove the given
* constraint and incrementally update the dataflow graph.
* Details: Retracting the given constraint may allow some currently
* unsatisfiable downstream constraint to be satisfied. We therefore collect
* a list of unsatisfied downstream constraints and attempt to
* satisfy each one in turn. This list is traversed by constraint
* strength, strongest first, as a heuristic for avoiding
* unnecessarily adding and then overriding weak constraints.
* Assume: c is satisfied.
*/
Planner.prototype.incrementalRemove = function (c) {
var out = c.output();
c.markUnsatisfied();
c.removeFromGraph();
var unsatisfied = this.removePropagateFrom(out);
var strength = Strength.REQUIRED;
do {
for (var i = 0; i < unsatisfied.size(); i++) {
var u = unsatisfied.at(i);
if (u.strength == strength)
this.incrementalAdd(u);
}
strength = strength.nextWeaker();
} while (strength != Strength.WEAKEST);
}
/**
* Select a previously unused mark value.
*/
Planner.prototype.newMark = function () {
return ++this.currentMark;
}
/**
* Extract a plan for resatisfaction starting from the given source
* constraints, usually a set of input constraints. This method
* assumes that stay optimization is desired; the plan will contain
* only constraints whose output variables are not stay. Constraints
* that do no computation, such as stay and edit constraints, are
* not included in the plan.
* Details: The outputs of a constraint are marked when it is added
* to the plan under construction. A constraint may be appended to
* the plan when all its input variables are known. A variable is
* known if either a) the variable is marked (indicating that has
* been computed by a constraint appearing earlier in the plan), b)
* the variable is 'stay' (i.e. it is a constant at plan execution
* time), or c) the variable is not determined by any
* constraint. The last provision is for past states of history
* variables, which are not stay but which are also not computed by
* any constraint.
* Assume: sources are all satisfied.
*/
Planner.prototype.makePlan = function (sources) {
var mark = this.newMark();
var plan = new Plan();
var todo = sources;
while (todo.size() > 0) {
var c = todo.removeFirst();
if (c.output().mark != mark && c.inputsKnown(mark)) {
plan.addConstraint(c);
c.output().mark = mark;
this.addConstraintsConsumingTo(c.output(), todo);
}
}
return plan;
}
/**
* Extract a plan for resatisfying starting from the output of the
* given constraints, usually a set of input constraints.
*/
Planner.prototype.extractPlanFromConstraints = function (constraints) {
var sources = new OrderedCollection();
for (var i = 0; i < constraints.size(); i++) {
var c = constraints.at(i);
if (c.isInput() && c.isSatisfied())
// not in plan already and eligible for inclusion
sources.add(c);
}
return this.makePlan(sources);
}
/**
* Recompute the walkabout strengths and stay flags of all variables
* downstream of the given constraint and recompute the actual
* values of all variables whose stay flag is true. If a cycle is
* detected, remove the given constraint and answer
* false. Otherwise, answer true.
* Details: Cycles are detected when a marked variable is
* encountered downstream of the given constraint. The sender is
* assumed to have marked the inputs of the given constraint with
* the given mark. Thus, encountering a marked node downstream of
* the output constraint means that there is a path from the
* constraint's output to one of its inputs.
*/
Planner.prototype.addPropagate = function (c, mark) {
var todo = new OrderedCollection();
todo.add(c);
while (todo.size() > 0) {
var d = todo.removeFirst();
if (d.output().mark == mark) {
this.incrementalRemove(c);
return false;
}
d.recalculate();
this.addConstraintsConsumingTo(d.output(), todo);
}
return true;
}
/**
* Update the walkabout strengths and stay flags of all variables
* downstream of the given constraint. Answer a collection of
* unsatisfied constraints sorted in order of decreasing strength.
*/
Planner.prototype.removePropagateFrom = function (out) {
out.determinedBy = null;
out.walkStrength = Strength.WEAKEST;
out.stay = true;
var unsatisfied = new OrderedCollection();
var todo = new OrderedCollection();
todo.add(out);
while (todo.size() > 0) {
var v = todo.removeFirst();
for (var i = 0; i < v.constraints.size(); i++) {
var c = v.constraints.at(i);
if (!c.isSatisfied())
unsatisfied.add(c);
}
var determining = v.determinedBy;
for (var i = 0; i < v.constraints.size(); i++) {
var next = v.constraints.at(i);
if (next != determining && next.isSatisfied()) {
next.recalculate();
todo.add(next.output());
}
}
}
return unsatisfied;
}
Planner.prototype.addConstraintsConsumingTo = function (v, coll) {
var determining = v.determinedBy;
var cc = v.constraints;
for (var i = 0; i < cc.size(); i++) {
var c = cc.at(i);
if (c != determining && c.isSatisfied())
coll.add(c);
}
}
/* --- *
* P l a n
* --- */
/**
* A Plan is an ordered list of constraints to be executed in sequence
* to resatisfy all currently satisfiable constraints in the face of
* one or more changing inputs.
*/
function Plan() {
this.v = new OrderedCollection();
}
Plan.prototype.addConstraint = function (c) {
this.v.add(c);
}
Plan.prototype.size = function () {
return this.v.size();
}
Plan.prototype.constraintAt = function (index) {
return this.v.at(index);
}
Plan.prototype.execute = function () {
for (var i = 0; i < this.size(); i++) {
var c = this.constraintAt(i);
c.execute();
}
}
/* --- *
* M a i n
* --- */
/**
* This is the standard DeltaBlue benchmark. A long chain of equality
* constraints is constructed with a stay constraint on one end. An
* edit constraint is then added to the opposite end and the time is
* measured for adding and removing this constraint, and extracting
* and executing a constraint satisfaction plan. There are two cases.
* In case 1, the added constraint is stronger than the stay
* constraint and values must propagate down the entire length of the
* chain. In case 2, the added constraint is weaker than the stay
* constraint so it cannot be accomodated. The cost in this case is,
* of course, very low. Typical situations lie somewhere between these
* two extremes.
*/
function chainTest(n) {
planner = new Planner();
var prev = null, first = null, last = null;
// Build chain of n equality constraints
for (var i = 0; i <= n; i++) {
var name = "v" + i;
var v = new Variable(name);
if (prev != null)
new EqualityConstraint(prev, v, Strength.REQUIRED);
if (i == 0) first = v;
if (i == n) last = v;
prev = v;
}
new StayConstraint(last, Strength.STRONG_DEFAULT);
var edit = new EditConstraint(first, Strength.PREFERRED);
var edits = new OrderedCollection();
edits.add(edit);
var plan = planner.extractPlanFromConstraints(edits);
for (var i = 0; i < 100; i++) {
first.value = i;
plan.execute();
assertEq(last.value, i);
}
}
/**
* This test constructs a two sets of variables related to each
* other by a simple linear transformation (scale and offset). The
* time is measured to change a variable on either side of the
* mapping and to change the scale and offset factors.
*/
function projectionTest(n) {
planner = new Planner();
var scale = new Variable("scale", 10);
var offset = new Variable("offset", 1000);
var src = null, dst = null;
var dests = new OrderedCollection();
for (var i = 0; i < n; i++) {
src = new Variable("src" + i, i);
dst = new Variable("dst" + i, i);
dests.add(dst);
new StayConstraint(src, Strength.NORMAL);
new ScaleConstraint(src, scale, offset, dst, Strength.REQUIRED);
}
change(src, 17);
assertEq(dst.value, 1170);
change(dst, 1050);
assertEq(src.value, 5);
change(scale, 5);
for (var i = 0; i < n - 1; i++) {
assertEq(dests.at(i).value, i * 5 + 1000);
}
change(offset, 2000);
for (var i = 0; i < n - 1; i++) {
assertEq(dests.at(i).value, i * 5 + 2000);
}
}
function change(v, newValue) {
var edit = new EditConstraint(v, Strength.PREFERRED);
var edits = new OrderedCollection();
edits.add(edit);
var plan = planner.extractPlanFromConstraints(edits);
for (var i = 0; i < 10; i++) {
v.value = newValue;
plan.execute();
}
edit.destroyConstraint();
}
// Global variable holding the current planner.
var planner = null;
function deltaBlue() {
chainTest(100);
projectionTest(100);
}
deltaBlue();

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,935 @@
// The ray tracer code in this file is written by Adam Burmister. It
// is available in its original form from:
//
// http://labs.flog.nz.co/raytracer/
//
// It has been modified slightly by Google to work as a standalone
// benchmark, but the all the computational code remains
// untouched. This file also contains a copy of parts of the Prototype
// JavaScript framework which is used by the ray tracer.
//var RayTrace = new BenchmarkSuite('RayTrace', 932666, [
// new Benchmark('RayTrace', renderScene)
//]);
// Variable used to hold a number that can be used to verify that
// the scene was ray traced correctly.
var checkNumber;
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// The following is a copy of parts of the Prototype JavaScript library:
// Prototype JavaScript framework, version 1.5.0
// (c) 2005-2007 Sam Stephenson
//
// Prototype is freely distributable under the terms of an MIT-style license.
// For details, see the Prototype web site: http://prototype.conio.net/
var Class = {
create: function() {
return function() {
this.initialize.apply(this, arguments);
}
}
};
Object.extend = function(destination, source) {
for (var property in source) {
destination[property] = source[property];
}
return destination;
};
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
// The rest of this file is the actual ray tracer written by Adam
// Burmister. It's a concatenation of the following files:
//
// flog/color.js
// flog/light.js
// flog/vector.js
// flog/ray.js
// flog/scene.js
// flog/material/basematerial.js
// flog/material/solid.js
// flog/material/chessboard.js
// flog/shape/baseshape.js
// flog/shape/sphere.js
// flog/shape/plane.js
// flog/intersectioninfo.js
// flog/camera.js
// flog/background.js
// flog/engine.js
/* Fake a Flog.* namespace */
if(typeof(Flog) == 'undefined') var Flog = {};
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
Flog.RayTracer.Color = Class.create();
Flog.RayTracer.Color.prototype = {
red : 0.0,
green : 0.0,
blue : 0.0,
initialize : function(r, g, b) {
if(!r) r = 0.0;
if(!g) g = 0.0;
if(!b) b = 0.0;
this.red = r;
this.green = g;
this.blue = b;
},
add : function(c1, c2){
var result = new Flog.RayTracer.Color(0,0,0);
result.red = c1.red + c2.red;
result.green = c1.green + c2.green;
result.blue = c1.blue + c2.blue;
return result;
},
addScalar: function(c1, s){
var result = new Flog.RayTracer.Color(0,0,0);
result.red = c1.red + s;
result.green = c1.green + s;
result.blue = c1.blue + s;
result.limit();
return result;
},
subtract: function(c1, c2){
var result = new Flog.RayTracer.Color(0,0,0);
result.red = c1.red - c2.red;
result.green = c1.green - c2.green;
result.blue = c1.blue - c2.blue;
return result;
},
multiply : function(c1, c2) {
var result = new Flog.RayTracer.Color(0,0,0);
result.red = c1.red * c2.red;
result.green = c1.green * c2.green;
result.blue = c1.blue * c2.blue;
return result;
},
multiplyScalar : function(c1, f) {
var result = new Flog.RayTracer.Color(0,0,0);
result.red = c1.red * f;
result.green = c1.green * f;
result.blue = c1.blue * f;
return result;
},
divideFactor : function(c1, f) {
var result = new Flog.RayTracer.Color(0,0,0);
result.red = c1.red / f;
result.green = c1.green / f;
result.blue = c1.blue / f;
return result;
},
limit: function(){
this.red = (this.red > 0.0) ? ( (this.red > 1.0) ? 1.0 : this.red ) : 0.0;
this.green = (this.green > 0.0) ? ( (this.green > 1.0) ? 1.0 : this.green ) : 0.0;
this.blue = (this.blue > 0.0) ? ( (this.blue > 1.0) ? 1.0 : this.blue ) : 0.0;
},
distance : function(color) {
var d = Math.abs(this.red - color.red) + Math.abs(this.green - color.green) + Math.abs(this.blue - color.blue);
return d;
},
blend: function(c1, c2, w){
var result = new Flog.RayTracer.Color(0,0,0);
result = Flog.RayTracer.Color.prototype.add(
Flog.RayTracer.Color.prototype.multiplyScalar(c1, 1 - w),
Flog.RayTracer.Color.prototype.multiplyScalar(c2, w)
);
return result;
},
brightness : function() {
var r = Math.floor(this.red*255);
var g = Math.floor(this.green*255);
var b = Math.floor(this.blue*255);
return (r * 77 + g * 150 + b * 29) >> 8;
},
toString : function () {
var r = Math.floor(this.red*255);
var g = Math.floor(this.green*255);
var b = Math.floor(this.blue*255);
return "rgb("+ r +","+ g +","+ b +")";
}
}
/* Fake a Flog.* namespace */
if(typeof(Flog) == 'undefined') var Flog = {};
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
Flog.RayTracer.Light = Class.create();
Flog.RayTracer.Light.prototype = {
position: null,
color: null,
intensity: 10.0,
initialize : function(pos, color, intensity) {
this.position = pos;
this.color = color;
this.intensity = (intensity ? intensity : 10.0);
},
getIntensity: function(distance){
if(distance >= intensity) return 0;
return Math.pow((intensity - distance) / strength, 0.2);
},
toString : function () {
return 'Light [' + this.position.x + ',' + this.position.y + ',' + this.position.z + ']';
}
}
/* Fake a Flog.* namespace */
if(typeof(Flog) == 'undefined') var Flog = {};
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
Flog.RayTracer.Vector = Class.create();
Flog.RayTracer.Vector.prototype = {
x : 0.0,
y : 0.0,
z : 0.0,
initialize : function(x, y, z) {
this.x = (x ? x : 0);
this.y = (y ? y : 0);
this.z = (z ? z : 0);
},
copy: function(vector){
this.x = vector.x;
this.y = vector.y;
this.z = vector.z;
},
normalize : function() {
var m = this.magnitude();
return new Flog.RayTracer.Vector(this.x / m, this.y / m, this.z / m);
},
magnitude : function() {
return Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z));
},
cross : function(w) {
return new Flog.RayTracer.Vector(
-this.z * w.y + this.y * w.z,
this.z * w.x - this.x * w.z,
-this.y * w.x + this.x * w.y);
},
dot : function(w) {
return this.x * w.x + this.y * w.y + this.z * w.z;
},
add : function(v, w) {
return new Flog.RayTracer.Vector(w.x + v.x, w.y + v.y, w.z + v.z);
},
subtract : function(v, w) {
if(!w || !v) throw 'Vectors must be defined [' + v + ',' + w + ']';
return new Flog.RayTracer.Vector(v.x - w.x, v.y - w.y, v.z - w.z);
},
multiplyVector : function(v, w) {
return new Flog.RayTracer.Vector(v.x * w.x, v.y * w.y, v.z * w.z);
},
multiplyScalar : function(v, w) {
return new Flog.RayTracer.Vector(v.x * w, v.y * w, v.z * w);
},
toString : function () {
return 'Vector [' + this.x + ',' + this.y + ',' + this.z + ']';
}
}
/* Fake a Flog.* namespace */
if(typeof(Flog) == 'undefined') var Flog = {};
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
Flog.RayTracer.Ray = Class.create();
Flog.RayTracer.Ray.prototype = {
position : null,
direction : null,
initialize : function(pos, dir) {
this.position = pos;
this.direction = dir;
},
toString : function () {
return 'Ray [' + this.position + ',' + this.direction + ']';
}
}
/* Fake a Flog.* namespace */
if(typeof(Flog) == 'undefined') var Flog = {};
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
Flog.RayTracer.Scene = Class.create();
Flog.RayTracer.Scene.prototype = {
camera : null,
shapes : [],
lights : [],
background : null,
initialize : function() {
this.camera = new Flog.RayTracer.Camera(
new Flog.RayTracer.Vector(0,0,-5),
new Flog.RayTracer.Vector(0,0,1),
new Flog.RayTracer.Vector(0,1,0)
);
this.shapes = new Array();
this.lights = new Array();
this.background = new Flog.RayTracer.Background(new Flog.RayTracer.Color(0,0,0.5), 0.2);
}
}
/* Fake a Flog.* namespace */
if(typeof(Flog) == 'undefined') var Flog = {};
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
if(typeof(Flog.RayTracer.Material) == 'undefined') Flog.RayTracer.Material = {};
Flog.RayTracer.Material.BaseMaterial = Class.create();
Flog.RayTracer.Material.BaseMaterial.prototype = {
gloss: 2.0, // [0...infinity] 0 = matt
transparency: 0.0, // 0=opaque
reflection: 0.0, // [0...infinity] 0 = no reflection
refraction: 0.50,
hasTexture: false,
initialize : function() {
},
getColor: function(u, v){
},
wrapUp: function(t){
t = t % 2.0;
if(t < -1) t += 2.0;
if(t >= 1) t -= 2.0;
return t;
},
toString : function () {
return 'Material [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
}
}
/* Fake a Flog.* namespace */
if(typeof(Flog) == 'undefined') var Flog = {};
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
Flog.RayTracer.Material.Solid = Class.create();
Flog.RayTracer.Material.Solid.prototype = Object.extend(
new Flog.RayTracer.Material.BaseMaterial(), {
initialize : function(color, reflection, refraction, transparency, gloss) {
this.color = color;
this.reflection = reflection;
this.transparency = transparency;
this.gloss = gloss;
this.hasTexture = false;
},
getColor: function(u, v){
return this.color;
},
toString : function () {
return 'SolidMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
}
}
);
/* Fake a Flog.* namespace */
if(typeof(Flog) == 'undefined') var Flog = {};
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
Flog.RayTracer.Material.Chessboard = Class.create();
Flog.RayTracer.Material.Chessboard.prototype = Object.extend(
new Flog.RayTracer.Material.BaseMaterial(), {
colorEven: null,
colorOdd: null,
density: 0.5,
initialize : function(colorEven, colorOdd, reflection, transparency, gloss, density) {
this.colorEven = colorEven;
this.colorOdd = colorOdd;
this.reflection = reflection;
this.transparency = transparency;
this.gloss = gloss;
this.density = density;
this.hasTexture = true;
},
getColor: function(u, v){
var t = this.wrapUp(u * this.density) * this.wrapUp(v * this.density);
if(t < 0.0)
return this.colorEven;
else
return this.colorOdd;
},
toString : function () {
return 'ChessMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
}
}
);
/* Fake a Flog.* namespace */
if(typeof(Flog) == 'undefined') var Flog = {};
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
Flog.RayTracer.Shape.BaseShape = Class.create();
Flog.RayTracer.Shape.BaseShape.prototype = {
position: null,
material: null,
initialize : function() {
this.position = new Vector(0,0,0);
this.material = new Flog.RayTracer.Material.SolidMaterial(
new Flog.RayTracer.Color(1,0,1),
0,
0,
0
);
},
toString : function () {
return 'Material [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
}
}
/* Fake a Flog.* namespace */
if(typeof(Flog) == 'undefined') var Flog = {};
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
Flog.RayTracer.Shape.Sphere = Class.create();
Flog.RayTracer.Shape.Sphere.prototype = {
initialize : function(pos, radius, material) {
this.radius = radius;
this.position = pos;
this.material = material;
},
intersect: function(ray){
var info = new Flog.RayTracer.IntersectionInfo();
info.shape = this;
var dst = Flog.RayTracer.Vector.prototype.subtract(ray.position, this.position);
var B = dst.dot(ray.direction);
var C = dst.dot(dst) - (this.radius * this.radius);
var D = (B * B) - C;
if(D > 0){ // intersection!
info.isHit = true;
info.distance = (-B) - Math.sqrt(D);
info.position = Flog.RayTracer.Vector.prototype.add(
ray.position,
Flog.RayTracer.Vector.prototype.multiplyScalar(
ray.direction,
info.distance
)
);
info.normal = Flog.RayTracer.Vector.prototype.subtract(
info.position,
this.position
).normalize();
info.color = this.material.getColor(0,0);
} else {
info.isHit = false;
}
return info;
},
toString : function () {
return 'Sphere [position=' + this.position + ', radius=' + this.radius + ']';
}
}
/* Fake a Flog.* namespace */
if(typeof(Flog) == 'undefined') var Flog = {};
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
Flog.RayTracer.Shape.Plane = Class.create();
Flog.RayTracer.Shape.Plane.prototype = {
d: 0.0,
initialize : function(pos, d, material) {
this.position = pos;
this.d = d;
this.material = material;
},
intersect: function(ray){
var info = new Flog.RayTracer.IntersectionInfo();
var Vd = this.position.dot(ray.direction);
if(Vd == 0) return info; // no intersection
var t = -(this.position.dot(ray.position) + this.d) / Vd;
if(t <= 0) return info;
info.shape = this;
info.isHit = true;
info.position = Flog.RayTracer.Vector.prototype.add(
ray.position,
Flog.RayTracer.Vector.prototype.multiplyScalar(
ray.direction,
t
)
);
info.normal = this.position;
info.distance = t;
if(this.material.hasTexture){
var vU = new Flog.RayTracer.Vector(this.position.y, this.position.z, -this.position.x);
var vV = vU.cross(this.position);
var u = info.position.dot(vU);
var v = info.position.dot(vV);
info.color = this.material.getColor(u,v);
} else {
info.color = this.material.getColor(0,0);
}
return info;
},
toString : function () {
return 'Plane [' + this.position + ', d=' + this.d + ']';
}
}
/* Fake a Flog.* namespace */
if(typeof(Flog) == 'undefined') var Flog = {};
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
Flog.RayTracer.IntersectionInfo = Class.create();
Flog.RayTracer.IntersectionInfo.prototype = {
isHit: false,
hitCount: 0,
shape: null,
position: null,
normal: null,
color: null,
distance: null,
initialize : function() {
this.color = new Flog.RayTracer.Color(0,0,0);
},
toString : function () {
return 'Intersection [' + this.position + ']';
}
}
/* Fake a Flog.* namespace */
if(typeof(Flog) == 'undefined') var Flog = {};
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
Flog.RayTracer.Camera = Class.create();
Flog.RayTracer.Camera.prototype = {
position: null,
lookAt: null,
equator: null,
up: null,
screen: null,
initialize : function(pos, lookAt, up) {
this.position = pos;
this.lookAt = lookAt;
this.up = up;
this.equator = lookAt.normalize().cross(this.up);
this.screen = Flog.RayTracer.Vector.prototype.add(this.position, this.lookAt);
},
getRay: function(vx, vy){
var pos = Flog.RayTracer.Vector.prototype.subtract(
this.screen,
Flog.RayTracer.Vector.prototype.subtract(
Flog.RayTracer.Vector.prototype.multiplyScalar(this.equator, vx),
Flog.RayTracer.Vector.prototype.multiplyScalar(this.up, vy)
)
);
pos.y = pos.y * -1;
var dir = Flog.RayTracer.Vector.prototype.subtract(
pos,
this.position
);
var ray = new Flog.RayTracer.Ray(pos, dir.normalize());
return ray;
},
toString : function () {
return 'Ray []';
}
}
/* Fake a Flog.* namespace */
if(typeof(Flog) == 'undefined') var Flog = {};
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
Flog.RayTracer.Background = Class.create();
Flog.RayTracer.Background.prototype = {
color : null,
ambience : 0.0,
initialize : function(color, ambience) {
this.color = color;
this.ambience = ambience;
}
}
/* Fake a Flog.* namespace */
if(typeof(Flog) == 'undefined') var Flog = {};
if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
Flog.RayTracer.Engine = Class.create();
Flog.RayTracer.Engine.prototype = {
canvas: null, /* 2d context we can render to */
initialize: function(options){
this.options = Object.extend({
canvasHeight: 100,
canvasWidth: 100,
pixelWidth: 2,
pixelHeight: 2,
renderDiffuse: false,
renderShadows: false,
renderHighlights: false,
renderReflections: false,
rayDepth: 2
}, options || {});
this.options.canvasHeight /= this.options.pixelHeight;
this.options.canvasWidth /= this.options.pixelWidth;
/* TODO: dynamically include other scripts */
},
setPixel: function(x, y, color){
var pxW, pxH;
pxW = this.options.pixelWidth;
pxH = this.options.pixelHeight;
if (this.canvas) {
this.canvas.fillStyle = color.toString();
this.canvas.fillRect (x * pxW, y * pxH, pxW, pxH);
} else {
if (x === y) {
checkNumber += color.brightness();
}
// print(x * pxW, y * pxH, pxW, pxH);
}
},
renderScene: function(scene, canvas){
checkNumber = 0;
/* Get canvas */
if (canvas) {
this.canvas = canvas.getContext("2d");
} else {
this.canvas = null;
}
var canvasHeight = this.options.canvasHeight;
var canvasWidth = this.options.canvasWidth;
for(var y=0; y < canvasHeight; y++){
for(var x=0; x < canvasWidth; x++){
var yp = y * 1.0 / canvasHeight * 2 - 1;
var xp = x * 1.0 / canvasWidth * 2 - 1;
var ray = scene.camera.getRay(xp, yp);
var color = this.getPixelColor(ray, scene);
this.setPixel(x, y, color);
}
}
assertEq(checkNumber, 2321);
},
getPixelColor: function(ray, scene){
var info = this.testIntersection(ray, scene, null);
if(info.isHit){
var color = this.rayTrace(info, ray, scene, 0);
return color;
}
return scene.background.color;
},
testIntersection: function(ray, scene, exclude){
var hits = 0;
var best = new Flog.RayTracer.IntersectionInfo();
best.distance = 2000;
for(var i=0; i<scene.shapes.length; i++){
var shape = scene.shapes[i];
if(shape != exclude){
var info = shape.intersect(ray);
if(info.isHit && info.distance >= 0 && info.distance < best.distance){
best = info;
hits++;
}
}
}
best.hitCount = hits;
return best;
},
getReflectionRay: function(P,N,V){
var c1 = -N.dot(V);
var R1 = Flog.RayTracer.Vector.prototype.add(
Flog.RayTracer.Vector.prototype.multiplyScalar(N, 2*c1),
V
);
return new Flog.RayTracer.Ray(P, R1);
},
rayTrace: function(info, ray, scene, depth){
// Calc ambient
var color = Flog.RayTracer.Color.prototype.multiplyScalar(info.color, scene.background.ambience);
var oldColor = color;
var shininess = Math.pow(10, info.shape.material.gloss + 1);
for(var i=0; i<scene.lights.length; i++){
var light = scene.lights[i];
// Calc diffuse lighting
var v = Flog.RayTracer.Vector.prototype.subtract(
light.position,
info.position
).normalize();
if(this.options.renderDiffuse){
var L = v.dot(info.normal);
if(L > 0.0){
color = Flog.RayTracer.Color.prototype.add(
color,
Flog.RayTracer.Color.prototype.multiply(
info.color,
Flog.RayTracer.Color.prototype.multiplyScalar(
light.color,
L
)
)
);
}
}
// The greater the depth the more accurate the colours, but
// this is exponentially (!) expensive
if(depth <= this.options.rayDepth){
// calculate reflection ray
if(this.options.renderReflections && info.shape.material.reflection > 0)
{
var reflectionRay = this.getReflectionRay(info.position, info.normal, ray.direction);
var refl = this.testIntersection(reflectionRay, scene, info.shape);
if (refl.isHit && refl.distance > 0){
refl.color = this.rayTrace(refl, reflectionRay, scene, depth + 1);
} else {
refl.color = scene.background.color;
}
color = Flog.RayTracer.Color.prototype.blend(
color,
refl.color,
info.shape.material.reflection
);
}
// Refraction
/* TODO */
}
/* Render shadows and highlights */
var shadowInfo = new Flog.RayTracer.IntersectionInfo();
if(this.options.renderShadows){
var shadowRay = new Flog.RayTracer.Ray(info.position, v);
shadowInfo = this.testIntersection(shadowRay, scene, info.shape);
if(shadowInfo.isHit && shadowInfo.shape != info.shape /*&& shadowInfo.shape.type != 'PLANE'*/){
var vA = Flog.RayTracer.Color.prototype.multiplyScalar(color, 0.5);
var dB = (0.5 * Math.pow(shadowInfo.shape.material.transparency, 0.5));
color = Flog.RayTracer.Color.prototype.addScalar(vA,dB);
}
}
// Phong specular highlights
if(this.options.renderHighlights && !shadowInfo.isHit && info.shape.material.gloss > 0){
var Lv = Flog.RayTracer.Vector.prototype.subtract(
info.shape.position,
light.position
).normalize();
var E = Flog.RayTracer.Vector.prototype.subtract(
scene.camera.position,
info.shape.position
).normalize();
var H = Flog.RayTracer.Vector.prototype.subtract(
E,
Lv
).normalize();
var glossWeight = Math.pow(Math.max(info.normal.dot(H), 0), shininess);
color = Flog.RayTracer.Color.prototype.add(
Flog.RayTracer.Color.prototype.multiplyScalar(light.color, glossWeight),
color
);
}
}
color.limit();
return color;
}
};
function renderScene(){
var scene = new Flog.RayTracer.Scene();
scene.camera = new Flog.RayTracer.Camera(
new Flog.RayTracer.Vector(0, 0, -15),
new Flog.RayTracer.Vector(-0.2, 0, 5),
new Flog.RayTracer.Vector(0, 1, 0)
);
scene.background = new Flog.RayTracer.Background(
new Flog.RayTracer.Color(0.5, 0.5, 0.5),
0.4
);
var sphere = new Flog.RayTracer.Shape.Sphere(
new Flog.RayTracer.Vector(-1.5, 1.5, 2),
1.5,
new Flog.RayTracer.Material.Solid(
new Flog.RayTracer.Color(0,0.5,0.5),
0.3,
0.0,
0.0,
2.0
)
);
var sphere1 = new Flog.RayTracer.Shape.Sphere(
new Flog.RayTracer.Vector(1, 0.25, 1),
0.5,
new Flog.RayTracer.Material.Solid(
new Flog.RayTracer.Color(0.9,0.9,0.9),
0.1,
0.0,
0.0,
1.5
)
);
var plane = new Flog.RayTracer.Shape.Plane(
new Flog.RayTracer.Vector(0.1, 0.9, -0.5).normalize(),
1.2,
new Flog.RayTracer.Material.Chessboard(
new Flog.RayTracer.Color(1,1,1),
new Flog.RayTracer.Color(0,0,0),
0.2,
0.0,
1.0,
0.7
)
);
scene.shapes.push(plane);
scene.shapes.push(sphere);
scene.shapes.push(sphere1);
var light = new Flog.RayTracer.Light(
new Flog.RayTracer.Vector(5, 10, -1),
new Flog.RayTracer.Color(0.8, 0.8, 0.8)
);
var light1 = new Flog.RayTracer.Light(
new Flog.RayTracer.Vector(-3, 5, -15),
new Flog.RayTracer.Color(0.8, 0.8, 0.8),
100
);
scene.lights.push(light);
scene.lights.push(light1);
var imageWidth = 100; // $F('imageWidth');
var imageHeight = 100; // $F('imageHeight');
var pixelSize = "5,5".split(','); // $F('pixelSize').split(',');
var renderDiffuse = true; // $F('renderDiffuse');
var renderShadows = true; // $F('renderShadows');
var renderHighlights = true; // $F('renderHighlights');
var renderReflections = true; // $F('renderReflections');
var rayDepth = 2;//$F('rayDepth');
var raytracer = new Flog.RayTracer.Engine(
{
canvasWidth: imageWidth,
canvasHeight: imageHeight,
pixelWidth: pixelSize[0],
pixelHeight: pixelSize[1],
"renderDiffuse": renderDiffuse,
"renderHighlights": renderHighlights,
"renderShadows": renderShadows,
"renderReflections": renderReflections,
"rayDepth": rayDepth
}
);
raytracer.renderScene(scene, null, 0);
}
renderScene();

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,536 @@
// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This is a JavaScript implementation of the Richards
// benchmark from:
//
// http://www.cl.cam.ac.uk/~mr10/Bench.html
//
// The benchmark was originally implemented in BCPL by
// Martin Richards.
//var Richards = new BenchmarkSuite('Richards', 34886, [
// new Benchmark("Richards", runRichards)
//]);
/**
* The Richards benchmark simulates the task dispatcher of an
* operating system.
**/
function runRichards() {
var scheduler = new Scheduler();
scheduler.addIdleTask(ID_IDLE, 0, null, COUNT);
var queue = new Packet(null, ID_WORKER, KIND_WORK);
queue = new Packet(queue, ID_WORKER, KIND_WORK);
scheduler.addWorkerTask(ID_WORKER, 1000, queue);
queue = new Packet(null, ID_DEVICE_A, KIND_DEVICE);
queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE);
queue = new Packet(queue, ID_DEVICE_A, KIND_DEVICE);
scheduler.addHandlerTask(ID_HANDLER_A, 2000, queue);
queue = new Packet(null, ID_DEVICE_B, KIND_DEVICE);
queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE);
queue = new Packet(queue, ID_DEVICE_B, KIND_DEVICE);
scheduler.addHandlerTask(ID_HANDLER_B, 3000, queue);
scheduler.addDeviceTask(ID_DEVICE_A, 4000, null);
scheduler.addDeviceTask(ID_DEVICE_B, 5000, null);
scheduler.schedule();
assertEq(scheduler.queueCount, EXPECTED_QUEUE_COUNT);
assertEq(scheduler.holdCount, EXPECTED_HOLD_COUNT);
}
var COUNT = 1000;
/**
* These two constants specify how many times a packet is queued and
* how many times a task is put on hold in a correct run of richards.
* They don't have any meaning a such but are characteristic of a
* correct run so if the actual queue or hold count is different from
* the expected there must be a bug in the implementation.
**/
var EXPECTED_QUEUE_COUNT = 2322;
var EXPECTED_HOLD_COUNT = 928;
/**
* A scheduler can be used to schedule a set of tasks based on their relative
* priorities. Scheduling is done by maintaining a list of task control blocks
* which holds tasks and the data queue they are processing.
* @constructor
*/
function Scheduler() {
this.queueCount = 0;
this.holdCount = 0;
this.blocks = new Array(NUMBER_OF_IDS);
this.list = null;
this.currentTcb = null;
this.currentId = null;
}
var ID_IDLE = 0;
var ID_WORKER = 1;
var ID_HANDLER_A = 2;
var ID_HANDLER_B = 3;
var ID_DEVICE_A = 4;
var ID_DEVICE_B = 5;
var NUMBER_OF_IDS = 6;
var KIND_DEVICE = 0;
var KIND_WORK = 1;
/**
* Add an idle task to this scheduler.
* @param {int} id the identity of the task
* @param {int} priority the task's priority
* @param {Packet} queue the queue of work to be processed by the task
* @param {int} count the number of times to schedule the task
*/
Scheduler.prototype.addIdleTask = function (id, priority, queue, count) {
this.addRunningTask(id, priority, queue, new IdleTask(this, 1, count));
};
/**
* Add a work task to this scheduler.
* @param {int} id the identity of the task
* @param {int} priority the task's priority
* @param {Packet} queue the queue of work to be processed by the task
*/
Scheduler.prototype.addWorkerTask = function (id, priority, queue) {
this.addTask(id, priority, queue, new WorkerTask(this, ID_HANDLER_A, 0));
};
/**
* Add a handler task to this scheduler.
* @param {int} id the identity of the task
* @param {int} priority the task's priority
* @param {Packet} queue the queue of work to be processed by the task
*/
Scheduler.prototype.addHandlerTask = function (id, priority, queue) {
this.addTask(id, priority, queue, new HandlerTask(this));
};
/**
* Add a handler task to this scheduler.
* @param {int} id the identity of the task
* @param {int} priority the task's priority
* @param {Packet} queue the queue of work to be processed by the task
*/
Scheduler.prototype.addDeviceTask = function (id, priority, queue) {
this.addTask(id, priority, queue, new DeviceTask(this))
};
/**
* Add the specified task and mark it as running.
* @param {int} id the identity of the task
* @param {int} priority the task's priority
* @param {Packet} queue the queue of work to be processed by the task
* @param {Task} task the task to add
*/
Scheduler.prototype.addRunningTask = function (id, priority, queue, task) {
this.addTask(id, priority, queue, task);
this.currentTcb.setRunning();
};
/**
* Add the specified task to this scheduler.
* @param {int} id the identity of the task
* @param {int} priority the task's priority
* @param {Packet} queue the queue of work to be processed by the task
* @param {Task} task the task to add
*/
Scheduler.prototype.addTask = function (id, priority, queue, task) {
this.currentTcb = new TaskControlBlock(this.list, id, priority, queue, task);
this.list = this.currentTcb;
this.blocks[id] = this.currentTcb;
};
/**
* Execute the tasks managed by this scheduler.
*/
Scheduler.prototype.schedule = function () {
this.currentTcb = this.list;
while (this.currentTcb != null) {
if (this.currentTcb.isHeldOrSuspended()) {
this.currentTcb = this.currentTcb.link;
} else {
this.currentId = this.currentTcb.id;
this.currentTcb = this.currentTcb.run();
}
}
};
/**
* Release a task that is currently blocked and return the next block to run.
* @param {int} id the id of the task to suspend
*/
Scheduler.prototype.release = function (id) {
var tcb = this.blocks[id];
if (tcb == null) return tcb;
tcb.markAsNotHeld();
if (tcb.priority > this.currentTcb.priority) {
return tcb;
} else {
return this.currentTcb;
}
};
/**
* Block the currently executing task and return the next task control block
* to run. The blocked task will not be made runnable until it is explicitly
* released, even if new work is added to it.
*/
Scheduler.prototype.holdCurrent = function () {
this.holdCount++;
this.currentTcb.markAsHeld();
return this.currentTcb.link;
};
/**
* Suspend the currently executing task and return the next task control block
* to run. If new work is added to the suspended task it will be made runnable.
*/
Scheduler.prototype.suspendCurrent = function () {
this.currentTcb.markAsSuspended();
return this.currentTcb;
};
/**
* Add the specified packet to the end of the worklist used by the task
* associated with the packet and make the task runnable if it is currently
* suspended.
* @param {Packet} packet the packet to add
*/
Scheduler.prototype.queue = function (packet) {
var t = this.blocks[packet.id];
if (t == null) return t;
this.queueCount++;
packet.link = null;
packet.id = this.currentId;
return t.checkPriorityAdd(this.currentTcb, packet);
};
/**
* A task control block manages a task and the queue of work packages associated
* with it.
* @param {TaskControlBlock} link the preceding block in the linked block list
* @param {int} id the id of this block
* @param {int} priority the priority of this block
* @param {Packet} queue the queue of packages to be processed by the task
* @param {Task} task the task
* @constructor
*/
function TaskControlBlock(link, id, priority, queue, task) {
this.link = link;
this.id = id;
this.priority = priority;
this.queue = queue;
this.task = task;
if (queue == null) {
this.state = STATE_SUSPENDED;
} else {
this.state = STATE_SUSPENDED_RUNNABLE;
}
}
/**
* The task is running and is currently scheduled.
*/
var STATE_RUNNING = 0;
/**
* The task has packets left to process.
*/
var STATE_RUNNABLE = 1;
/**
* The task is not currently running. The task is not blocked as such and may
* be started by the scheduler.
*/
var STATE_SUSPENDED = 2;
/**
* The task is blocked and cannot be run until it is explicitly released.
*/
var STATE_HELD = 4;
var STATE_SUSPENDED_RUNNABLE = STATE_SUSPENDED | STATE_RUNNABLE;
var STATE_NOT_HELD = ~STATE_HELD;
TaskControlBlock.prototype.setRunning = function () {
this.state = STATE_RUNNING;
};
TaskControlBlock.prototype.markAsNotHeld = function () {
this.state = this.state & STATE_NOT_HELD;
};
TaskControlBlock.prototype.markAsHeld = function () {
this.state = this.state | STATE_HELD;
};
TaskControlBlock.prototype.isHeldOrSuspended = function () {
return (this.state & STATE_HELD) != 0 || (this.state == STATE_SUSPENDED);
};
TaskControlBlock.prototype.markAsSuspended = function () {
this.state = this.state | STATE_SUSPENDED;
};
TaskControlBlock.prototype.markAsRunnable = function () {
this.state = this.state | STATE_RUNNABLE;
};
/**
* Runs this task, if it is ready to be run, and returns the next task to run.
*/
TaskControlBlock.prototype.run = function () {
var packet;
if (this.state == STATE_SUSPENDED_RUNNABLE) {
packet = this.queue;
this.queue = packet.link;
if (this.queue == null) {
this.state = STATE_RUNNING;
} else {
this.state = STATE_RUNNABLE;
}
} else {
packet = null;
}
return this.task.run(packet);
};
/**
* Adds a packet to the worklist of this block's task, marks this as runnable if
* necessary, and returns the next runnable object to run (the one
* with the highest priority).
*/
TaskControlBlock.prototype.checkPriorityAdd = function (task, packet) {
if (this.queue == null) {
this.queue = packet;
this.markAsRunnable();
if (this.priority > task.priority) return this;
} else {
this.queue = packet.addTo(this.queue);
}
return task;
};
TaskControlBlock.prototype.toString = function () {
return "tcb { " + this.task + "@" + this.state + " }";
};
/**
* An idle task doesn't do any work itself but cycles control between the two
* device tasks.
* @param {Scheduler} scheduler the scheduler that manages this task
* @param {int} v1 a seed value that controls how the device tasks are scheduled
* @param {int} count the number of times this task should be scheduled
* @constructor
*/
function IdleTask(scheduler, v1, count) {
this.scheduler = scheduler;
this.v1 = v1;
this.count = count;
}
IdleTask.prototype.run = function (packet) {
this.count--;
if (this.count == 0) return this.scheduler.holdCurrent();
if ((this.v1 & 1) == 0) {
this.v1 = this.v1 >> 1;
return this.scheduler.release(ID_DEVICE_A);
} else {
this.v1 = (this.v1 >> 1) ^ 0xD008;
return this.scheduler.release(ID_DEVICE_B);
}
};
IdleTask.prototype.toString = function () {
return "IdleTask"
};
/**
* A task that suspends itself after each time it has been run to simulate
* waiting for data from an external device.
* @param {Scheduler} scheduler the scheduler that manages this task
* @constructor
*/
function DeviceTask(scheduler) {
this.scheduler = scheduler;
this.v1 = null;
}
DeviceTask.prototype.run = function (packet) {
if (packet == null) {
if (this.v1 == null) return this.scheduler.suspendCurrent();
var v = this.v1;
this.v1 = null;
return this.scheduler.queue(v);
} else {
this.v1 = packet;
return this.scheduler.holdCurrent();
}
};
DeviceTask.prototype.toString = function () {
return "DeviceTask";
};
/**
* A task that manipulates work packets.
* @param {Scheduler} scheduler the scheduler that manages this task
* @param {int} v1 a seed used to specify how work packets are manipulated
* @param {int} v2 another seed used to specify how work packets are manipulated
* @constructor
*/
function WorkerTask(scheduler, v1, v2) {
this.scheduler = scheduler;
this.v1 = v1;
this.v2 = v2;
}
WorkerTask.prototype.run = function (packet) {
if (packet == null) {
return this.scheduler.suspendCurrent();
} else {
if (this.v1 == ID_HANDLER_A) {
this.v1 = ID_HANDLER_B;
} else {
this.v1 = ID_HANDLER_A;
}
packet.id = this.v1;
packet.a1 = 0;
for (var i = 0; i < DATA_SIZE; i++) {
this.v2++;
if (this.v2 > 26) this.v2 = 1;
packet.a2[i] = this.v2;
}
return this.scheduler.queue(packet);
}
};
WorkerTask.prototype.toString = function () {
return "WorkerTask";
};
/**
* A task that manipulates work packets and then suspends itself.
* @param {Scheduler} scheduler the scheduler that manages this task
* @constructor
*/
function HandlerTask(scheduler) {
this.scheduler = scheduler;
this.v1 = null;
this.v2 = null;
}
HandlerTask.prototype.run = function (packet) {
if (packet != null) {
if (packet.kind == KIND_WORK) {
this.v1 = packet.addTo(this.v1);
} else {
this.v2 = packet.addTo(this.v2);
}
}
if (this.v1 != null) {
var count = this.v1.a1;
var v;
if (count < DATA_SIZE) {
if (this.v2 != null) {
v = this.v2;
this.v2 = this.v2.link;
v.a1 = this.v1.a2[count];
this.v1.a1 = count + 1;
return this.scheduler.queue(v);
}
} else {
v = this.v1;
this.v1 = this.v1.link;
return this.scheduler.queue(v);
}
}
return this.scheduler.suspendCurrent();
};
HandlerTask.prototype.toString = function () {
return "HandlerTask";
};
/* --- *
* P a c k e t
* --- */
var DATA_SIZE = 4;
/**
* A simple package of data that is manipulated by the tasks. The exact layout
* of the payload data carried by a packet is not importaint, and neither is the
* nature of the work performed on packets by the tasks.
*
* Besides carrying data, packets form linked lists and are hence used both as
* data and worklists.
* @param {Packet} link the tail of the linked list of packets
* @param {int} id an ID for this packet
* @param {int} kind the type of this packet
* @constructor
*/
function Packet(link, id, kind) {
this.link = link;
this.id = id;
this.kind = kind;
this.a1 = 0;
this.a2 = new Array(DATA_SIZE);
}
/**
* Add this packet to the end of a worklist, and return the worklist.
* @param {Packet} queue the worklist to add this packet to
*/
Packet.prototype.addTo = function (queue) {
this.link = null;
if (queue == null) return this;
var peek, next = queue;
while ((peek = next.link) != null)
next = peek;
next.link = this;
return queue;
};
Packet.prototype.toString = function () {
return "Packet";
};
runRichards();

View File

@ -0,0 +1,393 @@
// Copyright 2009 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This benchmark is based on a JavaScript log processing module used
// by the V8 profiler to generate execution time profiles for runs of
// JavaScript applications, and it effectively measures how fast the
// JavaScript engine is at allocating nodes and reclaiming the memory
// used for old nodes. Because of the way splay trees work, the engine
// also has to deal with a lot of changes to the large tree object
// graph.
//var Splay = new BenchmarkSuite('Splay', 126125, [
// new Benchmark("Splay", SplayRun, SplaySetup, SplayTearDown)
//]);
// This is the best random number generator available to mankind ;)
var MyMath = {
seed: 49734321,
random: function() {
// Robert Jenkins' 32 bit integer hash function.
this.seed = ((this.seed + 0x7ed55d16) + (this.seed << 12)) & 0xffffffff;
this.seed = ((this.seed ^ 0xc761c23c) ^ (this.seed >>> 19)) & 0xffffffff;
this.seed = ((this.seed + 0x165667b1) + (this.seed << 5)) & 0xffffffff;
this.seed = ((this.seed + 0xd3a2646c) ^ (this.seed << 9)) & 0xffffffff;
this.seed = ((this.seed + 0xfd7046c5) + (this.seed << 3)) & 0xffffffff;
this.seed = ((this.seed ^ 0xb55a4f09) ^ (this.seed >>> 16)) & 0xffffffff;
return (this.seed & 0xfffffff) / 0x10000000;
},
};
// Configuration.
var kSplayTreeSize = 8000;
var kSplayTreeModifications = 80;
var kSplayTreePayloadDepth = 5;
var splayTree = null;
function GeneratePayloadTree(depth, key) {
if (depth == 0) {
return {
array : [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ],
string : 'String for key ' + key + ' in leaf node'
};
} else {
return {
left: GeneratePayloadTree(depth - 1, key),
right: GeneratePayloadTree(depth - 1, key)
};
}
}
function GenerateKey() {
// The benchmark framework guarantees that Math.random is
// deterministic; see base.js.
// base.js isn't pulled in for trace-tests
return MyMath.random();
}
function InsertNewNode() {
// Insert new node with a unique key.
var key;
do {
key = GenerateKey();
} while (splayTree.find(key) != null);
splayTree.insert(key, GeneratePayloadTree(kSplayTreePayloadDepth, key));
return key;
}
function SplaySetup() {
splayTree = new SplayTree();
for (var i = 0; i < kSplayTreeSize; i++) InsertNewNode();
}
function SplayTearDown() {
// Allow the garbage collector to reclaim the memory
// used by the splay tree no matter how we exit the
// tear down function.
var keys = splayTree.exportKeys();
splayTree = null;
// Verify that the splay tree has the right size.
var length = keys.length;
assertEq(length, kSplayTreeSize);
// Verify that the splay tree has sorted, unique keys.
for (var i = 0; i < length - 1; i++) {
assertEq(keys[i] < keys[i + 1], true);
}
}
function SplayRun() {
// Replace a few nodes in the splay tree.
for (var i = 0; i < kSplayTreeModifications; i++) {
var key = InsertNewNode();
var greatest = splayTree.findGreatestLessThan(key);
if (greatest == null) splayTree.remove(key);
else splayTree.remove(greatest.key);
}
}
/**
* Constructs a Splay tree. A splay tree is a self-balancing binary
* search tree with the additional property that recently accessed
* elements are quick to access again. It performs basic operations
* such as insertion, look-up and removal in O(log(n)) amortized time.
*
* @constructor
*/
function SplayTree() {
};
/**
* Pointer to the root node of the tree.
*
* @type {SplayTree.Node}
* @private
*/
SplayTree.prototype.root_ = null;
/**
* @return {boolean} Whether the tree is empty.
*/
SplayTree.prototype.isEmpty = function() {
return !this.root_;
};
/**
* Inserts a node into the tree with the specified key and value if
* the tree does not already contain a node with the specified key. If
* the value is inserted, it becomes the root of the tree.
*
* @param {number} key Key to insert into the tree.
* @param {*} value Value to insert into the tree.
*/
SplayTree.prototype.insert = function(key, value) {
if (this.isEmpty()) {
this.root_ = new SplayTree.Node(key, value);
return;
}
// Splay on the key to move the last node on the search path for
// the key to the root of the tree.
this.splay_(key);
if (this.root_.key == key) {
return;
}
var node = new SplayTree.Node(key, value);
if (key > this.root_.key) {
node.left = this.root_;
node.right = this.root_.right;
this.root_.right = null;
} else {
node.right = this.root_;
node.left = this.root_.left;
this.root_.left = null;
}
this.root_ = node;
};
/**
* Removes a node with the specified key from the tree if the tree
* contains a node with this key. The removed node is returned. If the
* key is not found, an exception is thrown.
*
* @param {number} key Key to find and remove from the tree.
* @return {SplayTree.Node} The removed node.
*/
SplayTree.prototype.remove = function(key) {
if (this.isEmpty()) {
throw Error('Key not found: ' + key);
}
this.splay_(key);
if (this.root_.key != key) {
throw Error('Key not found: ' + key);
}
var removed = this.root_;
if (!this.root_.left) {
this.root_ = this.root_.right;
} else {
var right = this.root_.right;
this.root_ = this.root_.left;
// Splay to make sure that the new root has an empty right child.
this.splay_(key);
// Insert the original right child as the right child of the new
// root.
this.root_.right = right;
}
return removed;
};
/**
* Returns the node having the specified key or null if the tree doesn't contain
* a node with the specified key.
*
* @param {number} key Key to find in the tree.
* @return {SplayTree.Node} Node having the specified key.
*/
SplayTree.prototype.find = function(key) {
if (this.isEmpty()) {
return null;
}
this.splay_(key);
return this.root_.key == key ? this.root_ : null;
};
/**
* @return {SplayTree.Node} Node having the maximum key value that
* is less or equal to the specified key value.
*/
SplayTree.prototype.findGreatestLessThan = function(key) {
if (this.isEmpty()) {
return null;
}
// Splay on the key to move the node with the given key or the last
// node on the search path to the top of the tree.
this.splay_(key);
// Now the result is either the root node or the greatest node in
// the left subtree.
if (this.root_.key <= key) {
return this.root_;
} else if (this.root_.left) {
return this.findMax(this.root_.left);
} else {
return null;
}
};
/**
* @return {Array<*>} An array containing all the keys of tree's nodes.
*/
SplayTree.prototype.exportKeys = function() {
var result = [];
if (!this.isEmpty()) {
this.root_.traverse_(function(node) { result.push(node.key); });
}
return result;
};
/**
* Perform the splay operation for the given key. Moves the node with
* the given key to the top of the tree. If no node has the given
* key, the last node on the search path is moved to the top of the
* tree. This is the simplified top-down splaying algorithm from:
* "Self-adjusting Binary Search Trees" by Sleator and Tarjan
*
* @param {number} key Key to splay the tree on.
* @private
*/
SplayTree.prototype.splay_ = function(key) {
if (this.isEmpty()) {
return;
}
// Create a dummy node. The use of the dummy node is a bit
// counter-intuitive: The right child of the dummy node will hold
// the L tree of the algorithm. The left child of the dummy node
// will hold the R tree of the algorithm. Using a dummy node, left
// and right will always be nodes and we avoid special cases.
var dummy, left, right;
dummy = left = right = new SplayTree.Node(null, null);
var current = this.root_;
while (true) {
if (key < current.key) {
if (!current.left) {
break;
}
if (key < current.left.key) {
// Rotate right.
var tmp = current.left;
current.left = tmp.right;
tmp.right = current;
current = tmp;
if (!current.left) {
break;
}
}
// Link right.
right.left = current;
right = current;
current = current.left;
} else if (key > current.key) {
if (!current.right) {
break;
}
if (key > current.right.key) {
// Rotate left.
var tmp = current.right;
current.right = tmp.left;
tmp.left = current;
current = tmp;
if (!current.right) {
break;
}
}
// Link left.
left.right = current;
left = current;
current = current.right;
} else {
break;
}
}
// Assemble.
left.right = current.left;
right.left = current.right;
current.left = dummy.right;
current.right = dummy.left;
this.root_ = current;
};
/**
* Constructs a Splay tree node.
*
* @param {number} key Key.
* @param {*} value Value.
*/
SplayTree.Node = function(key, value) {
this.key = key;
this.value = value;
};
/**
* @type {SplayTree.Node}
*/
SplayTree.Node.prototype.left = null;
/**
* @type {SplayTree.Node}
*/
SplayTree.Node.prototype.right = null;
/**
* Performs an ordered traversal of the subtree starting at
* this SplayTree.Node.
*
* @param {function(SplayTree.Node)} f Visitor function.
* @private
*/
SplayTree.Node.prototype.traverse_ = function(f) {
var current = this;
while (current) {
var left = current.left;
if (left) left.traverse_(f);
f(current);
current = current.right;
}
};
SplaySetup();
SplayRun();
SplayTearDown();

View File

@ -81,7 +81,6 @@ _TEST_FILES = \
test_bug445810.html \
test_bug449781.html \
test_bug450930.xhtml \
test_bug458898.html \
test_bug465448.xul \
test_bug469170.html \
test_bug471126.html \
@ -110,8 +109,10 @@ _TEST_FILES = \
$(NULL)
# Tests for bugs 441782, 467672 and 570378 don't pass reliably on Windows, because of bug 469208
# Test for bug 458898 doesn't pass on Windows 7. See bug 562955.
ifeq (,$(filter windows,$(MOZ_WIDGET_TOOLKIT)))
_TEST_FILES += \
test_bug458898.html \
bidi_numeral_test.js \
test_bug332655-1.html \
test_bug332655-2.html \