<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[202446] trunk/PerformanceTests</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/202446">202446</a></dd>
<dt>Author</dt> <dd>fpizlo@apple.com</dd>
<dt>Date</dt> <dd>2016-06-24 13:59:16 -0700 (Fri, 24 Jun 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Add a ES6 generator benchmark
https://bugs.webkit.org/show_bug.cgi?id=159101

Rubber stamped by Keith Miller.
        
This adds a Basic interpreter loosely based on ECMA-55:
        
http://www.ecma-international.org/publications/files/ECMA-ST-WITHDRAWN/ECMA-55,%201st%20Edition,%20January%201978.pdf
        
It includes a lexer that is a generator, a parser that uses regular expressions, and an AST
walk interpreter where the walking functions for statements are generators that call each
other with yield*. This enables the interpreter to look like an AST walk even though it can
yield at INPUT and PRINT statements.
        
This also uses for-of, classes, Map, and WeakMap. It also uses deprecated-but-awesome RegExp
features like RegExp.lastMatch and RegExp.rightContext. I did it that way because this is
how I've always written lexers in dynamic languages; see offlineasm's lex() method in
parser.rb for example.
        
The benchmark runs a handful of simple Basic programs. The longest-running one computes
prime numbers.
        
Includes a command-line and web harness. On my machine it runs in 2-3 seconds.

* Basic: Added.
* Basic/ast.js: Added.
(Basic.NumberApply):
(Basic.Variable):
(Basic.Const):
(Basic.NumberPow):
(Basic.NumberMul):
(Basic.NumberDiv):
(Basic.NumberNeg):
(Basic.NumberAdd):
(Basic.NumberSub):
(Basic.StringVar):
(Basic.Equals):
(Basic.NotEquals):
(Basic.LessThan):
(Basic.GreaterThan):
(Basic.LessEqual):
(Basic.GreaterEqual):
(Basic.GoTo):
(Basic.GoSub):
(Basic.Def):
(Basic.Let):
(Basic.If):
(Basic.Return):
(Basic.Stop):
(Basic.On):
(sideState.shouldStop):
(Basic.For):
(Basic.Next):
(Basic.Print):
(Basic.Input):
(Basic.Read):
(Basic.Restore):
(Basic.Dim):
(Basic.Randomize):
(Basic.End):
(Basic.Program):
* Basic/basic.js: Added.
(prepare):
(simulate):
* Basic/benchmark.js: Added.
(Benchmark):
(Benchmark.prototype.runIteration.expect):
(Benchmark.prototype.runIteration):
(runBenchmark):
* Basic/caseless_map.js: Added.
(CaselessMap):
* Basic/lexer.js: Added.
(lex.consumeWhitespace):
(lex.consume):
(lex):
* Basic/lexer_test.js: Added.
* Basic/number.js: Added.
(NumberValue):
(NumberValue.prototype.get value):
(NumberValue.prototype.apply):
(NumberValue.prototype.leftApply):
(NumberValue.prototype.assign):
(NumberArray.):
(NumberArray):
(NumberArray.prototype.apply):
(NumberArray.prototype.leftApply):
(NumberFunction):
(NumberFunction.prototype.apply):
(NumberFunction.prototype.leftApply):
(NativeFunction):
(NativeFunction.prototype.apply):
(NativeFunction.prototype.leftApply):
* Basic/parser.js: Added.
(parse):
(parse.pushToken):
(parse.peekToken):
(parse.consumeKind):
(parse.consumeToken):
(parse.parseVariable):
(parse.parseNumericExpression.parsePrimary):
(parse.parseNumericExpression.parseFactor):
(parse.parseNumericExpression.parseTerm):
(parse.parseNumericExpression):
(parse.parseConstant):
(parse.parseStringExpression):
(parse.isStringExpression):
(parse.parseRelationalExpression):
(parse.parseNonNegativeInteger):
(parse.parseGoToStatement):
(parse.parseGoSubStatement):
(parse.parseStatement):
(parse.parseStatements):
* Basic/random.js: Added.
(createRNG):
(createRNGWithFixedSeed):
(createRNGWithRandomSeed):
* Basic/state.js: Added.
(State):
(State.prototype.getValue):
(State.prototype.getSideState):
(State.prototype.abort):
(State.prototype.validate):
* Basic/test.html: Added.
* Basic/test.js: Added.
* Basic/util.js: Added.
(this.performance.performance.now.currentTime):
(else.this.preciseTime.currentTime):
(else.currentTime):
* Skipped: Make sure that we don't run Basic yet.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkPerformanceTestsChangeLog">trunk/PerformanceTests/ChangeLog</a></li>
<li><a href="#trunkPerformanceTestsSkipped">trunk/PerformanceTests/Skipped</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li>trunk/PerformanceTests/Basic/</li>
<li><a href="#trunkPerformanceTestsBasicastjs">trunk/PerformanceTests/Basic/ast.js</a></li>
<li><a href="#trunkPerformanceTestsBasicbasicjs">trunk/PerformanceTests/Basic/basic.js</a></li>
<li><a href="#trunkPerformanceTestsBasicbenchmarkjs">trunk/PerformanceTests/Basic/benchmark.js</a></li>
<li><a href="#trunkPerformanceTestsBasiccaseless_mapjs">trunk/PerformanceTests/Basic/caseless_map.js</a></li>
<li><a href="#trunkPerformanceTestsBasiclexerjs">trunk/PerformanceTests/Basic/lexer.js</a></li>
<li><a href="#trunkPerformanceTestsBasicnumberjs">trunk/PerformanceTests/Basic/number.js</a></li>
<li><a href="#trunkPerformanceTestsBasicparserjs">trunk/PerformanceTests/Basic/parser.js</a></li>
<li><a href="#trunkPerformanceTestsBasicrandomjs">trunk/PerformanceTests/Basic/random.js</a></li>
<li><a href="#trunkPerformanceTestsBasicstatejs">trunk/PerformanceTests/Basic/state.js</a></li>
<li><a href="#trunkPerformanceTestsBasictesthtml">trunk/PerformanceTests/Basic/test.html</a></li>
<li><a href="#trunkPerformanceTestsBasictestjs">trunk/PerformanceTests/Basic/test.js</a></li>
<li><a href="#trunkPerformanceTestsBasicutiljs">trunk/PerformanceTests/Basic/util.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkPerformanceTestsBasicastjs"></a>
<div class="addfile"><h4>Added: trunk/PerformanceTests/Basic/ast.js (0 => 202446)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/PerformanceTests/Basic/ast.js                                (rev 0)
+++ trunk/PerformanceTests/Basic/ast.js        2016-06-24 20:59:16 UTC (rev 202446)
</span><span class="lines">@@ -0,0 +1,273 @@
</span><ins>+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. 
+ */
+&quot;use strict&quot;;
+
+const Basic = {};
+
+Basic.NumberApply = function(state)
+{
+    // I'd call this arguments but we're in strict mode.
+    let parameters = this.parameters.map(value =&gt; value.evaluate(state));
+    
+    return state.getValue(this.name, parameters.length).apply(state, parameters);
+};
+
+Basic.Variable = function(state)
+{
+    let parameters = this.parameters.map(value =&gt; value.evaluate(state));
+    
+    return state.getValue(this.name, parameters.length).leftApply(state, parameters);
+}
+
+Basic.Const = function(state)
+{
+    return this.value;
+}
+
+Basic.NumberPow = function(state)
+{
+    return Math.pow(this.left.evaluate(state), this.right.evaluate(state));
+}
+
+Basic.NumberMul = function(state)
+{
+    return this.left.evaluate(state) * this.right.evaluate(state);
+}
+
+Basic.NumberDiv = function(state)
+{
+    return this.left.evaluate(state) / this.right.evaluate(state);
+}
+
+Basic.NumberNeg = function(state)
+{
+    return -this.term.evaluate(state);
+}
+
+Basic.NumberAdd = function(state)
+{
+    return this.left.evaluate(state) + this.right.evaluate(state);
+}
+
+Basic.NumberSub = function(state)
+{
+    return this.left.evaluate(state) - this.right.evaluate(state);
+}
+
+Basic.StringVar = function(state)
+{
+    let value = state.stringValues.get(this.name);
+    if (value == null)
+        state.abort(&quot;Could not find string variable &quot; + this.name);
+    return value;
+}
+
+Basic.Equals = function(state)
+{
+    return this.left.evaluate(state) == this.right.evaluate(state);
+}
+
+Basic.NotEquals = function(state)
+{
+    return this.left.evaluate(state) != this.right.evaluate(state);
+}
+
+Basic.LessThan = function(state)
+{
+    return this.left.evaluate(state) &lt; this.right.evaluate(state);
+}
+
+Basic.GreaterThan = function(state)
+{
+    return this.left.evaluate(state) &gt; this.right.evaluate(state);
+}
+
+Basic.LessEqual = function(state)
+{
+    return this.left.evaluate(state) &lt;= this.right.evaluate(state);
+}
+
+Basic.GreaterEqual = function(state)
+{
+    return this.left.evaluate(state) &gt;= this.right.evaluate(state);
+}
+
+Basic.GoTo = function*(state)
+{
+    state.nextLineNumber = this.target;
+}
+
+Basic.GoSub = function*(state)
+{
+    state.subStack.push(state.nextLineNumber);
+    state.nextLineNumber = this.target;
+}
+
+Basic.Def = function*(state)
+{
+    state.validate(!state.values.has(this.name), &quot;Cannot redefine function&quot;);
+    state.values.set(this.name, new NumberFunction(this.parameters, this.expression));
+}
+
+Basic.Let = function*(state)
+{
+    this.variable.evaluate(state).assign(this.expression.evaluate(state));
+}
+
+Basic.If = function*(state)
+{
+    if (this.condition.evaluate(state))
+        state.nextLineNumber = this.target;
+}
+
+Basic.Return = function*(state)
+{
+    this.validate(state.subStack.length, &quot;Not in a subroutine&quot;);
+    this.nextLineNumber = state.subStack.pop();
+}
+
+Basic.Stop = function*(state)
+{
+    state.nextLineNumber = null;
+}
+
+Basic.On = function*(state)
+{
+    let index = this.expression.evaluate(state);
+    if (!(index &gt;= 1) || !(index &lt;= this.targets.length))
+        state.abort(&quot;Index out of bounds: &quot; + index);
+    this.nextLineNumber = this.targets[Math.floor(index)];
+}
+
+Basic.For = function*(state)
+{
+    let sideState = state.getSideState(this);
+    sideState.variable = state.getValue(this.variable, 0).leftApply(state, []);
+    sideState.initialValue = this.initial.evaluate(state);
+    sideState.limitValue = this.limit.evaluate(state);
+    sideState.stepValue = this.step.evaluate(state);
+    sideState.variable.assign(sideState.initialValue);
+    sideState.shouldStop = function() {
+        return (sideState.variable.value - sideState.limitValue) * Math.sign(sideState.stepValue) &gt; 0;
+    };
+    if (sideState.shouldStop())
+        this.nextLineNumber = this.target.lineNumber + 1;
+}
+
+Basic.Next = function*(state)
+{
+    let sideState = state.getSideState(this.target);
+    sideState.variable.assign(sideState.variable.value + sideState.stepValue);
+    if (sideState.shouldStop())
+        return;
+    state.nextLineNumber = this.target.lineNumber + 1;
+}
+
+Basic.Next.isBlockEnd = true;
+
+Basic.Print = function*(state)
+{
+    let string = &quot;&quot;;
+    for (let item of this.items) {
+        switch (item.kind) {
+        case &quot;comma&quot;:
+            while (string.length % 14)
+                string += &quot; &quot;;
+            break;
+        case &quot;tab&quot;: {
+            let value = item.value.evaluate(state);
+            value = Math.max(Math.round(value), 1);
+            while (string.length % value)
+                string += &quot; &quot;;
+            break;
+        }
+        case &quot;string&quot;:
+        case &quot;number&quot;:
+            string += item.value.evaluate(state);
+            break;
+        default:
+            throw new Error(&quot;Bad item kind: &quot; + item.kind);
+        }
+    }
+    
+    yield {kind: &quot;output&quot;, string};
+}
+
+Basic.Input = function*(state)
+{
+    let results = yield {kind: &quot;input&quot;, numItems: this.items.length};
+    state.validate(results != null &amp;&amp; results.length == this.items.length, &quot;Input did not get the right number of items&quot;);
+    for (let i = 0; i &lt; results.length; ++i)
+        this.items[i].evaluate(state).assign(results[i]);
+}
+
+Basic.Read = function*(state)
+{
+    for (let item of this.items) {
+        state.validate(state.dataIndex &lt; state.program.data.length, &quot;Attempting to read past the end of data&quot;);
+        item.assign(state.program.data[state.dataIndex++]);
+    }
+}
+
+Basic.Restore = function*(state)
+{
+    state.dataIndex = 0;
+}
+
+Basic.Dim = function*(state)
+{
+    for (let item of this.items) {
+        state.validate(!state.values.has(item.name), &quot;Variable &quot; + item.name + &quot; already exists&quot;);
+        state.validate(item.bounds.length, &quot;Dim statement is for arrays&quot;);
+        state.values.set(item.name, new NumberArray(item.bounds.map(bound =&gt; bound + 1)));
+    }
+}
+
+Basic.Randomize = function*(state)
+{
+    state.rng = createRNGWithRandomSeed();
+}
+
+Basic.End = function*(state)
+{
+    state.nextLineNumber = null;
+}
+
+Basic.End.isBlockEnd = true;
+
+Basic.Program = function*(state)
+{
+    state.validate(state.program == this, &quot;State must match program&quot;);
+    let maxLineNumber = Math.max(...this.statements.keys());
+    while (state.nextLineNumber != null) {
+        state.validate(state.nextLineNumber &lt;= maxLineNumber, &quot;Went out of bounds of the program&quot;);
+        let statement = this.statements.get(state.nextLineNumber++);
+        if (statement == null || statement.process == null)
+            continue;
+        state.statement = statement;
+        yield* statement.process(state);
+    }
+}
+
</ins></span></pre></div>
<a id="trunkPerformanceTestsBasicbasicjs"></a>
<div class="addfile"><h4>Added: trunk/PerformanceTests/Basic/basic.js (0 => 202446)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/PerformanceTests/Basic/basic.js                                (rev 0)
+++ trunk/PerformanceTests/Basic/basic.js        2016-06-24 20:59:16 UTC (rev 202446)
</span><span class="lines">@@ -0,0 +1,53 @@
</span><ins>+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. 
+ */
+&quot;use strict&quot;;
+
+function prepare(string)
+{
+    let program = parse(lex(string)).program();
+    return program.process(new State(program));
+}
+
+function simulate(program, inputs = [])
+{
+    let result = &quot;&quot;;
+    let args = [];
+    for (;;) {
+        let next = program.next(...args);
+        args = [];
+        if (next.done)
+            break;
+        if (next.value.kind == &quot;output&quot;) {
+            result += next.value.string + &quot;\n&quot;;
+            continue;
+        }
+        if (next.value.kind == &quot;input&quot;) {
+            args = inputs.splice(0, next.value.numItems);
+            continue;
+        }
+    }
+    return result;
+}
+
</ins></span></pre></div>
<a id="trunkPerformanceTestsBasicbenchmarkjs"></a>
<div class="addfile"><h4>Added: trunk/PerformanceTests/Basic/benchmark.js (0 => 202446)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/PerformanceTests/Basic/benchmark.js                                (rev 0)
+++ trunk/PerformanceTests/Basic/benchmark.js        2016-06-24 20:59:16 UTC (rev 202446)
</span><span class="lines">@@ -0,0 +1,66 @@
</span><ins>+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. 
+ */
+&quot;use strict&quot;;
+
+class Benchmark {
+    constructor(verbose)
+    {
+        this._verbose = verbose;
+    }
+    
+    runIteration()
+    {
+        function expect(program, expected, ...inputs)
+        {
+            let result = simulate(prepare(program, inputs));
+            if (result != expected)
+                throw new Error(&quot;Program &quot; + JSON.stringify(program) + &quot; with inputs &quot; + JSON.stringify(inputs) + &quot; produced &quot; + JSON.stringify(result) + &quot; but we expected &quot; + JSON.stringify(expected));
+        }
+        
+        expect(&quot;10 print \&quot;hello, world!\&quot;\n20 end&quot;, &quot;hello, world!\n&quot;);
+        expect(&quot;10 let x = 0\n20 let x = x + 1\n30 print x\n40 if x &lt; 10 then 20\n50 end&quot;, &quot;1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n&quot;);
+        expect(&quot;10 print int(rnd * 100)\n20 end\n&quot;, &quot;98\n&quot;);
+        expect(&quot;10 let value = int(rnd * 2000)\n20 print value\n30 if value &lt;&gt; 100 then 10\n40 end&quot;, &quot;1974\n697\n1126\n1998\n1658\n264\n1650\n1677\n226\n117\n492\n861\n877\n1969\n38\n1039\n197\n1261\n1102\n1522\n916\n1683\n1943\n1835\n476\n1898\n939\n176\n966\n908\n474\n614\n1326\n564\n1916\n728\n524\n162\n1303\n758\n832\n1279\n1856\n1876\n982\n6\n1613\n1781\n681\n1238\n494\n1583\n1953\n788\n1026\n347\n1116\n1465\n514\n583\n463\n1970\n1573\n412\n1256\n1453\n838\n1538\n1984\n1598\n209\n411\n1700\n546\n861\n91\n132\n884\n378\n693\n11\n433\n1719\n860\n164\n472\n231\n1786\n806\n811\n106\n1697\n118\n980\n890\n1199\n227\n1667\n1933\n1903\n1390\n1595\n923\n1746\n39\n1361\n117\n1297\n923\n901\n1180\n818\n1444\n269\n933\n327\n1744\n1082\n1527\n1260\n622\n528\n318\n856\n296\n1796\n1574\n585\n1871\n111\n827\n1725\n1320\n1868\n1695\n1914\n216\n63\n1847\n156\n671\n893\n127\n1867\n811\n279\n913\n310\n814\n907\n1363\n1624\n1670\n478\n714\n436\n355\n1484\n1628\n1208\n80
 0\n611\n917\n829\n830\n273\n1791\n340\n214\n992\n1444\n442\n1555\n144\n1194\n282\n180\n1228\n1251\n1883\n678\n1555\n347\n72\n1661\n1828\n1090\n1183\n957\n1685\n930\n475\n103\n759\n1725\n1902\n1662\n1587\n61\n614\n863\n1418\n321\n1050\n505\n1622\n1425\n803\n589\n1511\n1098\n1051\n1554\n1898\n27\n747\n813\n1544\n332\n728\n1363\n771\n759\n1145\n1098\n1991\n385\n230\n520\n1369\n1840\n1285\n1562\n1845\n102\n760\n1874\n748\n361\n575\n277\n1661\n1764\n1117\n332\n757\n1766\n1722\n143\n474\n1507\n1294\n1180\n1578\n904\n845\n321\n496\n1911\n1784\n1116\n938\n1591\n1403\n1374\n533\n1085\n452\n708\n1096\n1634\n522\n564\n1397\n1357\n980\n978\n1760\n1088\n1361\n1184\n314\n1242\n217\n133\n1187\n1723\n646\n605\n591\n46\n135\n1420\n1821\n1147\n1211\n61\n244\n1307\n1551\n449\n1122\n1336\n140\n880\n22\n1155\n1326\n590\n1499\n1376\n112\n1771\n1897\n1071\n938\n1685\n1963\n1203\n1296\n804\n1275\n453\n1387\n482\n1262\n1883\n1381\n418\n1417\n1222\n1208\n1263\n632\n450\n1422\n1285\n1408\n644\n665\n27
 5\n363\n1012\n165\n354\n80\n609\n291\n1661\n1724\n117\n407\n59\n906\n1224\n136\n855\n1275\n1468\n482\n1537\n1283\n1784\n1568\n1832\n452\n867\n1546\n1467\n800\n45\n1225\n1890\n465\n1372\n47\n1608\n193\n1345\n1847\n1059\n1788\n518\n52\n1052\n1003\n1210\n1135\n1433\n519\n1558\n39\n1249\n1017\n39\n1713\n1449\n1245\n1354\n82\n1140\n916\n1595\n838\n607\n389\n1270\n821\n247\n1692\n1305\n1211\n1960\n429\n1703\n1635\n575\n1618\n1490\n1495\n682\n1256\n964\n420\n1520\n1429\n1997\n396\n382\n856\n1182\n296\n1295\n298\n1892\n990\n711\n934\n1939\n1339\n682\n1631\n1533\n742\n1520\n1281\n1332\n1042\n656\n1576\n1253\n1608\n375\n169\n14\n414\n1586\n1562\n1508\n1245\n303\n715\n1053\n340\n915\n160\n1796\n111\n925\n1872\n735\n350\n107\n1913\n1653\n987\n825\n1893\n1601\n460\n1228\n1526\n1613\n1359\n1854\n1352\n542\n665\n109\n1874\n467\n533\n1188\n1629\n851\n630\n1060\n1530\n1853\n743\n765\n126\n1540\n1411\n858\n1741\n284\n299\n577\n1848\n1495\n283\n1886\n284\n129\n1077\n1245\n1364\n1505\n176\n1012
 \n1663\n1306\n1586\n410\n315\n660\n256\n1102\n1289\n1292\n939\n762\n601\n1140\n574\n1851\n44\n560\n1948\n1142\n1787\n947\n948\n280\n1210\n1139\n1072\n1033\n92\n1244\n1589\n1079\n22\n1514\n163\n157\n1742\n1058\n514\n196\n1858\n565\n354\n1413\n792\n183\n526\n1724\n1007\n158\n1229\n1802\n99\n1514\n708\n1276\n1802\n1564\n1387\n1235\n1132\n715\n1584\n617\n1664\n1559\n1625\n1037\n601\n1175\n1713\n107\n88\n384\n1634\n904\n1835\n1472\n212\n1145\n443\n1617\n866\n1963\n937\n1917\n855\n1215\n1867\n520\n892\n1483\n1898\n1747\n1441\n289\n1609\n328\n566\n271\n458\n1616\n843\n1107\n507\n1090\n854\n1094\n806\n166\n408\n661\n334\n230\n1917\n1323\n927\n1912\n673\n311\n952\n1783\n1549\n1714\n1500\n450\n1498\n530\n442\n607\n609\n1226\n370\n1769\n1815\n788\n536\n293\n115\n947\n290\n1764\n243\n1219\n1851\n289\n599\n1528\n150\n1859\n297\n279\n1542\n1719\n1910\n551\n401\n952\n1764\n946\n1835\n647\n1309\n271\n275\n70\n129\n1518\n972\n1164\n816\n1125\n575\n588\n1456\n1154\n290\n1681\n1133\n561\n343\n
 1360\n1035\n1158\n1365\n744\n781\n58\n531\n271\n1612\n1774\n28\n1480\n1312\n1855\n666\n1574\n613\n42\n456\n351\n727\n1503\n1115\n333\n1972\n822\n1575\n848\n1087\n1262\n1671\n710\n460\n1816\n287\n172\n492\n1079\n582\n1236\n1756\n1792\n1095\n1205\n1894\n22\n1930\n1529\n1547\n1383\n1768\n364\n1108\n1972\n287\n200\n230\n1335\n187\n486\n1722\n20\n963\n792\n1114\n633\n1862\n1433\n829\n737\n215\n1570\n378\n1677\n944\n1301\n1160\n500\n150\n886\n1337\n662\n1062\n290\n460\n592\n1867\n872\n155\n1613\n1913\n1548\n1847\n855\n1702\n952\n1894\n587\n1813\n1021\n21\n654\n254\n910\n1696\n1606\n679\n1222\n696\n1319\n368\n447\n549\n905\n1194\n189\n1766\n616\n278\n1418\n1965\n872\n998\n1268\n1673\n1647\n1163\n533\n1650\n1849\n1124\n1252\n1412\n703\n944\n468\n1485\n1352\n681\n864\n1432\n1771\n497\n956\n1794\n363\n1099\n1804\n457\n1227\n1487\n446\n1993\n1576\n272\n709\n1810\n330\n876\n1107\n1187\n122\n1625\n472\n676\n314\n1257\n1509\n350\n741\n366\n33\n536\n293\n1663\n1039\n1527\n126\n923\n1937\n1
 767\n1302\n1510\n1518\n1343\n91\n1551\n1614\n1687\n1748\n137\n75\n738\n1977\n751\n237\n313\n566\n24\n202\n889\n1716\n1460\n129\n1760\n1597\n96\n1057\n1323\n1188\n1373\n537\n955\n65\n1679\n1441\n1315\n398\n647\n1470\n1335\n617\n331\n796\n129\n1635\n1497\n836\n855\n1472\n1828\n568\n862\n690\n1370\n1657\n819\n45\n420\n258\n1980\n672\n615\n358\n852\n1148\n1897\n1306\n1092\n1405\n719\n1752\n1456\n1338\n332\n351\n479\n747\n249\n1977\n1671\n1061\n1685\n306\n254\n1060\n764\n420\n1139\n1452\n426\n835\n929\n1424\n1336\n697\n191\n1697\n1897\n644\n546\n982\n359\n1201\n1095\n1623\n1947\n215\n10\n855\n297\n551\n1037\n945\n396\n211\n1059\n423\n1521\n1770\n203\n1828\n879\n1179\n1912\n1028\n1416\n1845\n698\n715\n1857\n817\n50\n473\n1122\n126\n70\n1773\n40\n1970\n1311\n826\n355\n1921\n23\n526\n1717\n1397\n1932\n1075\n1652\n997\n1039\n1481\n779\n415\n49\n1330\n317\n1701\n690\n245\n1824\n639\n799\n1240\n422\n344\n1639\n20\n546\n912\n1930\n1368\n1541\n1109\n369\n66\n1564\n444\n1928\n1963\n1899\n
 744\n1593\n1702\n100\n&quot;);
+
+        expect(&quot;10 dim a(2000)\n20 for i = 2 to 2000\n30 let a(i) = 1\n40 next i\n50 for i = 2 to sqr(2000)\n60 if a(i) = 0 then 100\n70 for j = i ^ 2 to 2000 step i\n80 let a(j) = 0\n90 next j\n100 next i\n110 for i = 2 to 2000\n120 if a(i) = 0 then 140\n130 print i\n140 next i\n150 end\n&quot;, &quot;2\n3\n5\n7\n11\n13\n17\n19\n23\n29\n31\n37\n41\n43\n47\n53\n59\n61\n67\n71\n73\n79\n83\n89\n97\n101\n103\n107\n109\n113\n127\n131\n137\n139\n149\n151\n157\n163\n167\n173\n179\n181\n191\n193\n197\n199\n211\n223\n227\n229\n233\n239\n241\n251\n257\n263\n269\n271\n277\n281\n283\n293\n307\n311\n313\n317\n331\n337\n347\n349\n353\n359\n367\n373\n379\n383\n389\n397\n401\n409\n419\n421\n431\n433\n439\n443\n449\n457\n461\n463\n467\n479\n487\n491\n499\n503\n509\n521\n523\n541\n547\n557\n563\n569\n571\n577\n587\n593\n599\n601\n607\n613\n617\n619\n631\n641\n643\n647\n653\n659\n661\n673\n677\n683\n691\n701\n709\n719\n727\n733\n739\n743\n751\n757\n761\n769\n773\n787\n797\n809\n811\n821\
 n823\n827\n829\n839\n853\n857\n859\n863\n877\n881\n883\n887\n907\n911\n919\n929\n937\n941\n947\n953\n967\n971\n977\n983\n991\n997\n1009\n1013\n1019\n1021\n1031\n1033\n1039\n1049\n1051\n1061\n1063\n1069\n1087\n1091\n1093\n1097\n1103\n1109\n1117\n1123\n1129\n1151\n1153\n1163\n1171\n1181\n1187\n1193\n1201\n1213\n1217\n1223\n1229\n1231\n1237\n1249\n1259\n1277\n1279\n1283\n1289\n1291\n1297\n1301\n1303\n1307\n1319\n1321\n1327\n1361\n1367\n1373\n1381\n1399\n1409\n1423\n1427\n1429\n1433\n1439\n1447\n1451\n1453\n1459\n1471\n1481\n1483\n1487\n1489\n1493\n1499\n1511\n1523\n1531\n1543\n1549\n1553\n1559\n1567\n1571\n1579\n1583\n1597\n1601\n1607\n1609\n1613\n1619\n1621\n1627\n1637\n1657\n1663\n1667\n1669\n1693\n1697\n1699\n1709\n1721\n1723\n1733\n1741\n1747\n1753\n1759\n1777\n1783\n1787\n1789\n1801\n1811\n1823\n1831\n1847\n1861\n1867\n1871\n1873\n1877\n1879\n1889\n1901\n1907\n1913\n1931\n1933\n1949\n1951\n1973\n1979\n1987\n1993\n1997\n1999\n&quot;);
+    }
+}
+    
+function runBenchmark()
+{
+    const verbose = 0;
+    const numIterations = 150;
+    
+    let before = currentTime();
+    
+    let benchmark = new Benchmark(verbose);
+    
+    for (let iteration = 0; iteration &lt; numIterations; ++iteration)
+        benchmark.runIteration();
+    
+    let after = currentTime();
+    return after - before;
+}
+
</ins></span></pre></div>
<a id="trunkPerformanceTestsBasiccaseless_mapjs"></a>
<div class="addfile"><h4>Added: trunk/PerformanceTests/Basic/caseless_map.js (0 => 202446)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/PerformanceTests/Basic/caseless_map.js                                (rev 0)
+++ trunk/PerformanceTests/Basic/caseless_map.js        2016-06-24 20:59:16 UTC (rev 202446)
</span><span class="lines">@@ -0,0 +1,56 @@
</span><ins>+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. 
+ */
+&quot;use strict&quot;;
+
+class CaselessMap {
+    constructor(otherMap)
+    {
+        if (otherMap == null)
+            this._map = new Map();
+        else
+            this._map = new Map(otherMap._map);
+    }
+    
+    set(key, value)
+    {
+        this._map.set(key.toLowerCase(), value);
+    }
+    
+    has(key)
+    {
+        return this._map.has(key.toLowerCase());
+    }
+    
+    get(key)
+    {
+        return this._map.get(key.toLowerCase());
+    }
+
+    [Symbol.iterator]()
+    {
+        return this._map[Symbol.iterator]();
+    }
+}
+
</ins></span></pre></div>
<a id="trunkPerformanceTestsBasiclexerjs"></a>
<div class="addfile"><h4>Added: trunk/PerformanceTests/Basic/lexer.js (0 => 202446)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/PerformanceTests/Basic/lexer.js                                (rev 0)
+++ trunk/PerformanceTests/Basic/lexer.js        2016-06-24 20:59:16 UTC (rev 202446)
</span><span class="lines">@@ -0,0 +1,97 @@
</span><ins>+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. 
+ */
+&quot;use strict&quot;;
+
+// Loosely based on ECMA 55 sections 4-8, but loosened to allow for modern conventions, like
+// multi-character variable names. But this doesn't go too far - in particular, this doesn't do
+// unicode, because that would require more thought.
+function* lex(string)
+{
+    let sourceLineNumber = 0;
+    for (let line of string.split(&quot;\n&quot;)) {
+        ++sourceLineNumber;
+        
+        function consumeWhitespace()
+        {
+            if (/^\s+/.test(line))
+                line = RegExp.rightContext;
+        }
+   
+        function consume(kind)
+        {
+            line = RegExp.rightContext;
+            return {kind, string: RegExp.lastMatch, sourceLineNumber, userLineNumber};
+        }
+        
+        const isIdentifier = /^[a-z_]([a-z0-9_]*)/i;
+        const isNumber = /^(([0-9]+(\.([0-9]*))?)|(\.[0-9]+)(e([+-]?)([0-9]+))?)/i;
+        const isString = /^\&quot;([^\&quot;]|(\&quot;\&quot;))*\&quot;/;
+        const isKeyword = /^((base)|(data)|(def)|(dim)|(end)|(for)|(go)|(gosub)|(goto)|(if)|(input)|(let)|(next)|(on)|(option)|(print)|(randomize)|(read)|(restore)|(return)|(step)|(stop)|(sub)|(then)|(to))/i;
+        const isOperator = /^(-|\+|\*|\/|\^|\(|\)|(&lt;[&gt;=]?)|(&gt;=?)|=|,|\$|;)/;
+        const isRem = /^rem\s.*/;
+        
+        consumeWhitespace();
+        
+        if (!/^[0-9]+/.test(line))
+            throw new Error(&quot;At line &quot; + sourceLineNumber + &quot;: Expect line number: &quot; + line);
+        let userLineNumber = +RegExp.lastMatch;
+        line = RegExp.rightContext;
+        yield {kind: &quot;userLineNumber&quot;, string: RegExp.lastMatch, sourceLineNumber, userLineNumber};
+        
+        consumeWhitespace();
+        
+        while (line.length) {
+            if (isKeyword.test(line))
+                yield consume(&quot;keyword&quot;);
+            else if (isIdentifier.test(line))
+                yield consume(&quot;identifier&quot;);
+            else if (isNumber.test(line)) {
+                let token = consume(&quot;number&quot;);
+                token.value = +token.string;
+                yield token;
+            } else if (isString.test(line)) {
+                let token = consume(&quot;string&quot;);
+                token.value = &quot;&quot;;
+                for (let i = 1; i &lt; token.string.length - 1; ++i) {
+                    let char = token.string.charAt(i);
+                    if (char == &quot;\&quot;&quot;)
+                        i++;
+                    token.value += char;
+                }
+                yield token;
+            } else if (isOperator.test(line))
+                yield consume(&quot;operator&quot;);
+            else if (isRem.test(line))
+                yield consume(&quot;remark&quot;);
+            else
+                throw new Error(&quot;At line &quot; + sourceLineNumber + &quot;: Cannot lex token: &quot; + line);
+            consumeWhitespace();
+        }
+        
+        // Note: this is necessary for the parser, which may look-ahead without checking if we're
+        // done. Fortunately, it won't look-ahead past a newLine.
+        yield {kind: &quot;newLine&quot;, string:&quot;\n&quot;, sourceLineNumber, userLineNumber};
+    }
+}
</ins></span></pre></div>
<a id="trunkPerformanceTestsBasicnumberjs"></a>
<div class="addfile"><h4>Added: trunk/PerformanceTests/Basic/number.js (0 => 202446)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/PerformanceTests/Basic/number.js                                (rev 0)
+++ trunk/PerformanceTests/Basic/number.js        2016-06-24 20:59:16 UTC (rev 202446)
</span><span class="lines">@@ -0,0 +1,137 @@
</span><ins>+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. 
+ */
+&quot;use strict&quot;;
+
+class NumberValue {
+    constructor(value = 0)
+    {
+        this._value = value;
+    }
+    
+    get value() { return this._value; }
+    
+    apply(state, parameters)
+    {
+        state.validate(parameters.length == 0, &quot;Should not pass arguments to simple numeric variables&quot;);
+        return this._value;
+    }
+    
+    leftApply(state, parameters)
+    {
+        state.validate(parameters.length == 0, &quot;Should not pass arguments to simple numeric variables&quot;);
+        return this;
+    }
+    
+    assign(value)
+    {
+        this._value = value;
+    }
+}
+
+class NumberArray {
+    constructor(dim = [11])
+    {
+        function allocateDim(index)
+        {
+            let result = new Array(dim[index]);
+            if (index + 1 &lt; dim.length) {
+                for (let i = 0; i &lt; dim[index]; ++i)
+                    result[i] = allocateDim(index + 1);
+            } else {
+                for (let i = 0; i &lt; dim[index]; ++i)
+                    result[i] = new NumberValue();
+            }
+            return result;
+        }
+        
+        this._array = allocateDim(0);
+        this._dim = dim;
+    }
+    
+    apply(state, parameters)
+    {
+        return this.leftApply(state, parameters).apply(state, []);
+    }
+    
+    leftApply(state, parameters)
+    {
+        if (this._dim.length != parameters.length)
+            state.abort(&quot;Expected &quot; + this._dim.length + &quot; arguments but &quot; + parameters.length + &quot; were passed.&quot;);
+        let result = this._array;
+        for (var i = 0; i &lt; parameters.length; ++i) {
+            let index = Math.floor(parameters[i]);
+            if (!(index &gt;= state.program.base) || !(index &lt; result.length))
+                state.abort(&quot;Index out of bounds: &quot; + index);
+            result = result[index];
+        }
+        return result;
+    }
+}
+
+class NumberFunction {
+    constructor(parameters, code)
+    {
+        this._parameters = parameters;
+        this._code = code;
+    }
+    
+    apply(state, parameters)
+    {
+        if (this._parameters.length != parameters.length)
+            state.abort(&quot;Expected &quot; + this._parameters.length + &quot; arguments but &quot; + parameters.length + &quot; were passed&quot;);
+        let oldValues = state.values;
+        state.values = new Map(oldValues);
+        for (let i = 0; i &lt; parameters.length; ++i)
+            state.values.set(this._parameters[i], parameters[i]);
+        let result = this.code.evaluate(state);
+        state.values = oldValues;
+        return result;
+    }
+    
+    leftApply(state, parameters)
+    {
+        state.abort(&quot;Cannot use a function as an lvalue&quot;);
+    }
+}
+
+class NativeFunction {
+    constructor(callback)
+    {
+        this._callback = callback;
+    }
+    
+    apply(state, parameters)
+    {
+        if (this._callback.length != parameters.length)
+            state.abort(&quot;Expected &quot; + this._callback.length + &quot; arguments but &quot; + parameters.length + &quot; were passed&quot;);
+        return this._callback(...parameters);
+    }
+    
+    leftApply(state, parameters)
+    {
+        state.abort(&quot;Cannot use a native function as an lvalue&quot;);
+    }
+}
+
</ins></span></pre></div>
<a id="trunkPerformanceTestsBasicparserjs"></a>
<div class="addfile"><h4>Added: trunk/PerformanceTests/Basic/parser.js (0 => 202446)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/PerformanceTests/Basic/parser.js                                (rev 0)
+++ trunk/PerformanceTests/Basic/parser.js        2016-06-24 20:59:16 UTC (rev 202446)
</span><span class="lines">@@ -0,0 +1,558 @@
</span><ins>+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. 
+ */
+&quot;use strict&quot;;
+
+function parse(tokenizer)
+{
+    let program;
+    
+    let pushBackBuffer = [];
+    
+    function nextToken()
+    {
+        if (pushBackBuffer.length)
+            return pushBackBuffer.pop();
+        let result = tokenizer.next();
+        if (result.done)
+            return {kind: &quot;endOfFile&quot;, string: &quot;&lt;end of file&gt;&quot;};
+        return result.value;
+    }
+    
+    function pushToken(token)
+    {
+        pushBackBuffer.push(token);
+    }
+    
+    function peekToken()
+    {
+        let result = nextToken();
+        pushToken(result);
+        return result;
+    }
+    
+    function consumeKind(kind)
+    {
+        let token = nextToken();
+        if (token.kind != kind) {
+            throw new Error(&quot;At &quot; + token.sourceLineNumber + &quot;: expected &quot; + kind + &quot; but got: &quot; + token.string);
+        }
+        return token;
+    }
+    
+    function consumeToken(string)
+    {
+        let token = nextToken();
+        if (token.string.toLowerCase() != string.toLowerCase())
+            throw new Error(&quot;At &quot; + token.sourceLineNumber + &quot;: expected &quot; + string + &quot; but got: &quot; + token.string);
+        return token;
+    }
+    
+    function parseVariable()
+    {
+        let name = consumeKind(&quot;identifier&quot;).string;
+        let result = {evaluate: Basic.Variable, name, parameters: []};
+        if (peekToken().string == &quot;(&quot;) {
+            do {
+                nextToken();
+                result.parameters.push(parseNumericExpression());
+            } while (peekToken().string == &quot;,&quot;);
+            consumeToken(&quot;)&quot;);
+        }
+        return result;
+    }
+    
+    function parseNumericExpression()
+    {
+        function parsePrimary()
+        {
+            let token = nextToken();
+            switch (token.kind) {
+            case &quot;identifier&quot;: {
+                let result = {evaluate: Basic.NumberApply, name: token.string, parameters: []};
+                if (peekToken().string == &quot;(&quot;) {
+                    do {
+                        nextToken();
+                        result.parameters.push(parseNumericExpression());
+                    } while (peekToken().string == &quot;,&quot;);
+                    consumeToken(&quot;)&quot;);
+                }
+                return result;
+            }
+                
+            case &quot;number&quot;:
+                return {evaluate: Basic.Const, value: token.value};
+                
+            case &quot;operator&quot;:
+                switch (token.string) {
+                case &quot;(&quot;: {
+                    let result = parseNumericExpression();
+                    consumeToken(&quot;)&quot;);
+                    return result;
+                } }
+                break;
+            }
+            throw new Error(&quot;At &quot; + token.sourceLineNumber + &quot;: expected identifier, number, or (, but got: &quot; + token.string);
+        }
+        
+        function parseFactor()
+        {
+            let primary = parsePrimary();
+            
+            let ok = true;
+            while (ok) {
+                switch (peekToken().string) {
+                case &quot;^&quot;:
+                    nextToken();
+                    primary = {evaluate: Basic.NumberPow, left: primary, right: parsePrimary()};
+                    break;
+                default:
+                    ok = false;
+                    break;
+                }
+            }
+            
+            return primary;
+        }
+        
+        function parseTerm()
+        {
+            let factor = parseFactor();
+            
+            let ok = true;
+            while (ok) {
+                switch (peekToken().string) {
+                case &quot;*&quot;:
+                    nextToken();
+                    factor = {evaluate: Basic.NumberMul, left: factor, right: parseFactor()};
+                    break;
+                case &quot;/&quot;:
+                    nextToken();
+                    factor = {evaluate: Basic.NumberDiv, left: factor, right: parseFactor()};
+                    break;
+                default:
+                    ok = false;
+                    break;
+                }
+            }
+            
+            return factor;
+        }
+        
+        // Only the leading term in Basic can have a sign.
+        let negate = false;
+        switch (peekToken().string) {
+        case &quot;+&quot;:
+            nextToken();
+            break;
+        case &quot;-&quot;:
+            negate = true;
+            nextToken()
+            break;
+        }
+        
+        let term = parseTerm();
+        if (negate)
+            term = {evaluate: Basic.NumberNeg, term: term};
+        
+        let ok = true;
+        while (ok) {
+            switch (peekToken().string) {
+            case &quot;+&quot;:
+                nextToken();
+                term = {evaluate: Basic.NumberAdd, left: term, right: parseTerm()};
+                break;
+            case &quot;-&quot;:
+                nextToken();
+                term = {evaluate: Basic.NumberSub, left: term, right: parseTerm()};
+                break;
+            default:
+                ok = false;
+                break;
+            }
+        }
+        
+        return term;
+    }
+    
+    function parseConstant()
+    {
+        switch (peekToken().string) {
+        case &quot;+&quot;:
+            nextToken();
+            return consumeKind(&quot;number&quot;).value;
+        case &quot;-&quot;:
+            nextToken();
+            return -consumeKind(&quot;number&quot;).value;
+        default:
+            if (isStringExpression())
+                return consumeKind(&quot;string&quot;).value;
+            return consumeKind(&quot;number&quot;).value;
+        }
+    }
+    
+    function parseStringExpression()
+    {
+        let token = nextToken();
+        switch (token.kind) {
+        case &quot;string&quot;:
+            return {evaluate: Basic.Const, value: token.value};
+        case &quot;identifier&quot;:
+            consumeToken(&quot;$&quot;);
+            return {evaluate: Basic.StringVar, name: token.string};
+        default:
+            throw new Error(&quot;At &quot; + token.sourceLineNumber + &quot;: expected string expression but got &quot; + token.string);
+        }
+    }
+    
+    function isStringExpression()
+    {
+        // A string expression must start with a string variable or a string constant.
+        let token = nextToken();
+        if (token.kind == &quot;string&quot;) {
+            pushToken(token);
+            return true;
+        }
+        if (token.kind == &quot;identifier&quot;) {
+            let result = peekToken().string == &quot;$&quot;;
+            pushToken(token);
+            return result;
+        }
+        pushToken(token);
+        return false;
+    }
+    
+    function parseRelationalExpression()
+    {
+        if (isStringExpression()) {
+            let left = parseStringExpression();
+            let operator = nextToken();
+            let evaluate;
+            switch (operator.string) {
+            case &quot;=&quot;:
+                evaluate = Basic.Equals;
+                break;
+            case &quot;&lt;&gt;&quot;:
+                evaluate = Basic.NotEquals;
+                break;
+            default:
+                throw new Error(&quot;At &quot; + operator.sourceLineNumber + &quot;: expected a string comparison operator but got: &quot; + operator.string);
+            }
+            return {evaluate, left, right: parseStringExpression()};
+        }
+        
+        let left = parseNumericExpression();
+        let operator = nextToken();
+        let evaluate;
+        switch (operator.string) {
+        case &quot;=&quot;:
+            evaluate = Basic.Equals;
+            break;
+        case &quot;&lt;&gt;&quot;:
+            evaluate = Basic.NotEquals;
+            break;
+        case &quot;&lt;&quot;:
+            evaluate = Basic.LessThan;
+            break;
+        case &quot;&gt;&quot;:
+            evaluate = Basic.GreaterThan;
+            break;
+        case &quot;&lt;=&quot;:
+            evaluate = Basic.LessEqual;
+            break;
+        case &quot;&gt;=&quot;:
+            evaluate = Basic.GreaterEqual;
+            break;
+        default:
+            throw new Error(&quot;At &quot; + operator.sourceLineNumber + &quot;: expected a numeric comparison operator but got: &quot; + operator.string);
+        }
+        return {evaluate, left, right: parseNumericExpression()};
+    }
+    
+    function parseNonNegativeInteger()
+    {
+        let token = nextToken();
+        if (!/^[0-9]+$/.test(token.string))
+            throw new Error(&quot;At &quot;, token.sourceLineNumber + &quot;: expected a line number but got: &quot; + token.string);
+        return token.value;
+    }
+    
+    function parseGoToStatement()
+    {
+        statement.kind = Basic.GoTo;
+        statement.target = parseNonNegativeInteger();
+    }
+    
+    function parseGoSubStatement()
+    {
+        statement.kind = Basic.GoSub;
+        statement.target = parseNonNegativeInteger();
+    }
+    
+    function parseStatement()
+    {
+        let statement = {};
+        statement.lineNumber = consumeKind(&quot;userLineNumber&quot;).userLineNumber;
+        program.statements.set(statement.lineNumber, statement);
+        
+        let command = nextToken();
+        statement.sourceLineNumber = command.sourceLineNumber;
+        switch (command.kind) {
+        case &quot;keyword&quot;:
+            switch (command.string.toLowerCase()) {
+            case &quot;def&quot;:
+                statement.process = Basic.Def;
+                statement.name = consumeKind(&quot;identifier&quot;);
+                statement.parameters = [];
+                if (peekToken().string == &quot;(&quot;) {
+                    do {
+                        nextToken();
+                        statement.parameters.push(consumeKind(&quot;identifier&quot;));
+                    } while (peekToken().string == &quot;,&quot;);
+                }
+                statement.expression = parseNumericExpression();
+                break;
+            case &quot;let&quot;:
+                statement.process = Basic.Let;
+                statement.variable = parseVariable();
+                consumeToken(&quot;=&quot;);
+                if (statement.process == Basic.Let)
+                    statement.expression = parseNumericExpression();
+                else
+                    statement.expression = parseStringExpression();
+                break;
+            case &quot;go&quot;: {
+                let next = nextToken();
+                if (next.string == &quot;to&quot;)
+                    parseGoToStatement();
+                else if (next.string == &quot;sub&quot;)
+                    parseGoSubStatement();
+                else
+                    throw new Error(&quot;At &quot; + next.sourceLineNumber + &quot;: expected to or sub but got: &quot; + next.string);
+                break;
+            }
+            case &quot;goto&quot;:
+                parseGoToStatement();
+                break;
+            case &quot;gosub&quot;:
+                parseGoSubStatement();
+                break;
+            case &quot;if&quot;:
+                statement.process = Basic.If;
+                statement.condition = parseRelationalExpression();
+                consumeToken(&quot;then&quot;);
+                statement.target = parseNonNegativeInteger();
+                break;
+            case &quot;return&quot;:
+                statement.process = Basic.Return;
+                break;
+            case &quot;stop&quot;:
+                statement.process = Basic.Stop;
+                break;
+            case &quot;on&quot;:
+                statement.process = Basic.On;
+                statement.expression = parseNumericExpression();
+                if (peekToken().string == &quot;go&quot;) {
+                    consumeToken(&quot;go&quot;);
+                    consumeToken(&quot;to&quot;);
+                } else
+                    consumeToken(&quot;goto&quot;);
+                statement.targets = [];
+                for (;;) {
+                    statement.targets.push(parseNonNegativeInteger());
+                    if (peekToken().string != &quot;,&quot;)
+                        break;
+                    nextToken();
+                }
+                break;
+            case &quot;for&quot;:
+                statement.process = Basic.For;
+                statement.variable = consumeKind(&quot;identifier&quot;).string;
+                consumeToken(&quot;=&quot;);
+                statement.initial = parseNumericExpression();
+                consumeToken(&quot;to&quot;);
+                statement.limit = parseNumericExpression();
+                if (peekToken().string == &quot;step&quot;) {
+                    nextToken();
+                    statement.step = parseNumericExpression();
+                } else
+                    statement.step = {evaluate: Basic.Const, value: 1};
+                consumeKind(&quot;newLine&quot;);
+                let lastStatement = parseStatements();
+                if (lastStatement.process != Basic.Next)
+                    throw new Error(&quot;At &quot; + lastStatement.sourceLineNumber + &quot;: expected next statement&quot;);
+                if (lastStatement.variable != statement.variable)
+                    throw new Error(&quot;At &quot; + lastStatement.sourceLineNumber + &quot;: expected next for &quot; + statement.variable + &quot; but got &quot; + lastStatement.variable);
+                lastStatement.target = statement;
+                statement.target = lastStatement;
+                return statement;
+            case &quot;next&quot;:
+                statement.process = Basic.Next;
+                statement.variable = consumeKind(&quot;identifier&quot;).string;
+                break;
+            case &quot;print&quot;: {
+                statement.process = Basic.Print;
+                statement.items = [];
+                let ok = true;
+                while (ok) {
+                    switch (peekToken().string) {
+                    case &quot;,&quot;:
+                        nextToken();
+                        statement.items.push({kind: &quot;comma&quot;});
+                        break;
+                    case &quot;;&quot;:
+                        nextToken();
+                        break;
+                    case &quot;tab&quot;:
+                        nextToken();
+                        consumeToken(&quot;(&quot;);
+                        statement.items.push({kind: &quot;tab&quot;, value: parseNumericExpression()});
+                        break;
+                    case &quot;\n&quot;:
+                        ok = false;
+                        break;
+                    default:
+                        if (isStringExpression()) {
+                            statement.items.push({kind: &quot;string&quot;, value: parseStringExpression()});
+                            break;
+                        }
+                        statement.items.push({kind: &quot;number&quot;, value: parseNumericExpression()});
+                        break;
+                    }
+                }
+                break;
+            }
+            case &quot;input&quot;:
+                statement.process = Basic.Input;
+                statement.items = [];
+                for (;;) {
+                    stament.items.push(parseVariable());
+                    if (peekToken().string != &quot;,&quot;)
+                        break;
+                    nextToken();
+                }
+                break;
+            case &quot;read&quot;:
+                statement.process = Basic.Read;
+                statement.items = [];
+                for (;;) {
+                    stament.items.push(parseVariable());
+                    if (peekToken().string != &quot;,&quot;)
+                        break;
+                    nextToken();
+                }
+                break;
+            case &quot;restore&quot;:
+                statement.process = Basic.Restore;
+                break;
+            case &quot;data&quot;:
+                for (;;) {
+                    program.data.push(parseConstant());
+                    if (peekToken().string != &quot;,&quot;)
+                        break;
+                    nextToken();
+                }
+                break;
+            case &quot;dim&quot;:
+                statement.process = Basic.Dim;
+                statement.items = [];
+                for (;;) {
+                    let name = consumeKind(&quot;identifier&quot;).string;
+                    consumeToken(&quot;(&quot;);
+                    let bounds = [];
+                    bounds.push(parseNonNegativeInteger());
+                    if (peekToken().string == &quot;,&quot;) {
+                        nextToken();
+                        bounds.push(parseNonNegativeInteger());
+                    }
+                    consumeToken(&quot;)&quot;);
+                    statement.items.push({name, bounds});
+                    
+                    if (peekToken().string != &quot;,&quot;)
+                        break;
+                    consumeToken(&quot;,&quot;);
+                }
+                break;
+            case &quot;option&quot;: {
+                consumeToken(&quot;base&quot;);
+                let base = parseNonNegativeInteger();
+                if (base != 0 &amp;&amp; base != 1)
+                    throw new Error(&quot;At &quot; + command.sourceLineNumber + &quot;: unexpected base: &quot; + base);
+                program.base = base;
+                break;
+            }
+            case &quot;randomize&quot;:
+                statement.process = Basic.Randomize;
+                break;
+            case &quot;end&quot;:
+                statement.process = Basic.End;
+                break;
+            default:
+                throw new Error(&quot;At &quot; + command.sourceLineNumber + &quot;: unexpected command but got: &quot; + command.string);
+            }
+            break;
+        case &quot;remark&quot;:
+            // Just ignore it.
+            break;
+        default:
+            throw new Error(&quot;At &quot; + command.sourceLineNumber + &quot;: expected command but got: &quot; + command.string + &quot; (of kind &quot; + command.kind + &quot;)&quot;);
+        }
+        
+        consumeKind(&quot;newLine&quot;);
+        return statement;
+    }
+    
+    function parseStatements()
+    {
+        let statement;
+        do {
+            statement = parseStatement();
+        } while (!statement.process || !statement.process.isBlockEnd);
+        return statement;
+    }
+    
+    return {
+        program()
+        {
+            program = {
+                process: Basic.Program,
+                statements: new Map(),
+                data: [],
+                base: 0
+            };
+            let lastStatement = parseStatements(program.statements);
+            if (lastStatement.process != Basic.End)
+                throw new Error(&quot;At &quot; + lastStatement.sourceLineNumber + &quot;: expected end&quot;);
+            
+            return program;
+        },
+        
+        statement(program_)
+        {
+            program = program_;
+            return parseStatement();
+        }
+    };
+}
+
</ins></span></pre></div>
<a id="trunkPerformanceTestsBasicrandomjs"></a>
<div class="addfile"><h4>Added: trunk/PerformanceTests/Basic/random.js (0 => 202446)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/PerformanceTests/Basic/random.js                                (rev 0)
+++ trunk/PerformanceTests/Basic/random.js        2016-06-24 20:59:16 UTC (rev 202446)
</span><span class="lines">@@ -0,0 +1,54 @@
</span><ins>+// Copyright 2013 the V8 project authors. All rights reserved.
+// Copyright (C) 2015-2016 Apple Inc. 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
+// &quot;AS IS&quot; 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.
+&quot;use strict&quot;;
+
+// This is based on Octane's RNG.
+function createRNG(seed)
+{
+    return function() {
+        // Robert Jenkins' 32 bit integer hash function.
+        seed = ((seed + 0x7ed55d16) + (seed &lt;&lt; 12))  &amp; 0xffffffff;
+        seed = ((seed ^ 0xc761c23c) ^ (seed &gt;&gt;&gt; 19)) &amp; 0xffffffff;
+        seed = ((seed + 0x165667b1) + (seed &lt;&lt; 5))   &amp; 0xffffffff;
+        seed = ((seed + 0xd3a2646c) ^ (seed &lt;&lt; 9))   &amp; 0xffffffff;
+        seed = ((seed + 0xfd7046c5) + (seed &lt;&lt; 3))   &amp; 0xffffffff;
+        seed = ((seed ^ 0xb55a4f09) ^ (seed &gt;&gt;&gt; 16)) &amp; 0xffffffff;
+        return (seed &amp; 0xfffffff) / 0x10000000;
+    };
+}
+
+function createRNGWithFixedSeed()
+{
+    // This uses Octane's initial seed.
+    return createRNG(49734321);
+}
+
+function createRNGWithRandomSeed()
+{
+    return createRNG((Math.random() * 4294967296) | 0);
+}
</ins></span></pre></div>
<a id="trunkPerformanceTestsBasicstatejs"></a>
<div class="addfile"><h4>Added: trunk/PerformanceTests/Basic/state.js (0 => 202446)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/PerformanceTests/Basic/state.js                                (rev 0)
+++ trunk/PerformanceTests/Basic/state.js        2016-06-24 20:59:16 UTC (rev 202446)
</span><span class="lines">@@ -0,0 +1,98 @@
</span><ins>+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. 
+ */
+&quot;use strict&quot;;
+
+class State {
+    constructor(program)
+    {
+        this.values = new CaselessMap();
+        this.stringValues = new CaselessMap();
+        this.sideState = new WeakMap();
+        this.statement = null;
+        this.nextLineNumber = 0;
+        this.subStack = [];
+        this.dataIndex = 0;
+        this.program = program;
+        this.rng = createRNGWithFixedSeed();
+        
+        let addNative = (name, callback) =&gt; {
+            this.values.set(name, new NativeFunction(callback));
+        };
+        
+        addNative(&quot;abs&quot;, x =&gt; Math.abs(x));
+        addNative(&quot;atn&quot;, x =&gt; Math.atan(x));
+        addNative(&quot;cos&quot;, x =&gt; Math.cos(x));
+        addNative(&quot;exp&quot;, x =&gt; Math.exp(x));
+        addNative(&quot;int&quot;, x =&gt; Math.floor(x));
+        addNative(&quot;log&quot;, x =&gt; Math.log(x));
+        addNative(&quot;rnd&quot;, () =&gt; this.rng());
+        addNative(&quot;sgn&quot;, x =&gt; Math.sign(x));
+        addNative(&quot;sin&quot;, x =&gt; Math.sin(x));
+        addNative(&quot;sqr&quot;, x =&gt; Math.sqrt(x));
+        addNative(&quot;tan&quot;, x =&gt; Math.tan(x));
+    }
+    
+    getValue(name, numParameters)
+    {
+        if (this.values.has(name))
+            return this.values.get(name);
+
+        let result;
+        if (numParameters == 0)
+            result = new NumberValue();
+        else {
+            let dim = [];
+            while (numParameters--)
+                dim.push(11);
+            result = new NumberArray(dim);
+        }
+        this.values.set(name, result);
+        return result;
+    }
+    
+    getSideState(key)
+    {
+        if (!this.sideState.has(key)) {
+            let result = {};
+            this.sideState.set(key, result);
+            return result;
+        }
+        return this.sideState.get(key);
+    }
+    
+    abort(text)
+    {
+        if (!this.statement)
+            throw new Error(&quot;At beginning of execution: &quot; + text);
+        throw new Error(&quot;At &quot; + this.statement.sourceLineNumber + &quot;: &quot; + text);
+    }
+    
+    validate(predicate, text)
+    {
+        if (!predicate)
+            this.abort(text);
+    }
+}
+
</ins></span></pre></div>
<a id="trunkPerformanceTestsBasictesthtml"></a>
<div class="addfile"><h4>Added: trunk/PerformanceTests/Basic/test.html (0 => 202446)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/PerformanceTests/Basic/test.html                                (rev 0)
+++ trunk/PerformanceTests/Basic/test.html        2016-06-24 20:59:16 UTC (rev 202446)
</span><span class="lines">@@ -0,0 +1,34 @@
</span><ins>+&lt;html&gt;
+&lt;head&gt;
+&lt;title&gt;Basic&lt;/title&gt;
+&lt;script src=&quot;ast.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;basic.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;caseless_map.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;lexer.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;number.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;parser.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;random.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;state.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;util.js&quot;&gt;&lt;/script&gt;
+&lt;script src=&quot;benchmark.js&quot;&gt;&lt;/script&gt;
+&lt;script&gt;
+    function runTest() {
+        try {
+            var result = runBenchmark();
+            document.getElementById(&quot;result-summary&quot;).innerHTML = &quot;That took &quot; + result + &quot; ms.&quot;;
+        } catch (e) {
+            document.getElementById(&quot;result-summary&quot;).innerHTML = &quot;Failed: &quot; + e;
+        }
+    }
+&lt;/script&gt;
+&lt;/head&gt;
+&lt;body&gt;
+&lt;h1&gt;Basic&lt;/h1&gt;
+&lt;p&gt;
+  &lt;div id=&quot;result-summary&quot;&gt;&lt;/div&gt;
+  &lt;div&gt;&lt;a href=&quot;javascript:runTest()&quot;&gt;Start Test&lt;/a&gt;&lt;/div&gt;
+&lt;/p&gt;
+&lt;/body&gt;
+&lt;/html&gt;
+
+
</ins></span></pre></div>
<a id="trunkPerformanceTestsBasictestjs"></a>
<div class="addfile"><h4>Added: trunk/PerformanceTests/Basic/test.js (0 => 202446)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/PerformanceTests/Basic/test.js                                (rev 0)
+++ trunk/PerformanceTests/Basic/test.js        2016-06-24 20:59:16 UTC (rev 202446)
</span><span class="lines">@@ -0,0 +1,16 @@
</span><ins>+&quot;use strict&quot;;
+
+load(&quot;ast.js&quot;);
+load(&quot;basic.js&quot;);
+load(&quot;caseless_map.js&quot;);
+load(&quot;lexer.js&quot;);
+load(&quot;number.js&quot;);
+load(&quot;parser.js&quot;);
+load(&quot;random.js&quot;);
+load(&quot;state.js&quot;);
+load(&quot;util.js&quot;);
+
+load(&quot;benchmark.js&quot;);
+
+let result = runBenchmark();
+print(&quot;That took &quot; + result + &quot; ms.&quot;);
</ins></span></pre></div>
<a id="trunkPerformanceTestsBasicutiljs"></a>
<div class="addfile"><h4>Added: trunk/PerformanceTests/Basic/util.js (0 => 202446)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/PerformanceTests/Basic/util.js                                (rev 0)
+++ trunk/PerformanceTests/Basic/util.js        2016-06-24 20:59:16 UTC (rev 202446)
</span><span class="lines">@@ -0,0 +1,34 @@
</span><ins>+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. 
+ */
+&quot;use strict&quot;;
+
+let currentTime;
+if (this.performance &amp;&amp; performance.now)
+    currentTime = function() { return performance.now() };
+else if (this.preciseTime)
+    currentTime = function() { return preciseTime() * 1000; };
+else
+    currentTime = function() { return +new Date(); };
+
</ins></span></pre></div>
<a id="trunkPerformanceTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/PerformanceTests/ChangeLog (202445 => 202446)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/PerformanceTests/ChangeLog        2016-06-24 20:53:09 UTC (rev 202445)
+++ trunk/PerformanceTests/ChangeLog        2016-06-24 20:59:16 UTC (rev 202446)
</span><span class="lines">@@ -1,3 +1,135 @@
</span><ins>+2016-06-24  Filip Pizlo  &lt;fpizlo@apple.com&gt;
+
+        Add a ES6 generator benchmark
+        https://bugs.webkit.org/show_bug.cgi?id=159101
+
+        Rubber stamped by Keith Miller.
+        
+        This adds a Basic interpreter loosely based on ECMA-55:
+        
+        http://www.ecma-international.org/publications/files/ECMA-ST-WITHDRAWN/ECMA-55,%201st%20Edition,%20January%201978.pdf
+        
+        It includes a lexer that is a generator, a parser that uses regular expressions, and an AST
+        walk interpreter where the walking functions for statements are generators that call each
+        other with yield*. This enables the interpreter to look like an AST walk even though it can
+        yield at INPUT and PRINT statements.
+        
+        This also uses for-of, classes, Map, and WeakMap. It also uses deprecated-but-awesome RegExp
+        features like RegExp.lastMatch and RegExp.rightContext. I did it that way because this is
+        how I've always written lexers in dynamic languages; see offlineasm's lex() method in
+        parser.rb for example.
+        
+        The benchmark runs a handful of simple Basic programs. The longest-running one computes
+        prime numbers.
+        
+        Includes a command-line and web harness. On my machine it runs in 2-3 seconds.
+
+        * Basic: Added.
+        * Basic/ast.js: Added.
+        (Basic.NumberApply):
+        (Basic.Variable):
+        (Basic.Const):
+        (Basic.NumberPow):
+        (Basic.NumberMul):
+        (Basic.NumberDiv):
+        (Basic.NumberNeg):
+        (Basic.NumberAdd):
+        (Basic.NumberSub):
+        (Basic.StringVar):
+        (Basic.Equals):
+        (Basic.NotEquals):
+        (Basic.LessThan):
+        (Basic.GreaterThan):
+        (Basic.LessEqual):
+        (Basic.GreaterEqual):
+        (Basic.GoTo):
+        (Basic.GoSub):
+        (Basic.Def):
+        (Basic.Let):
+        (Basic.If):
+        (Basic.Return):
+        (Basic.Stop):
+        (Basic.On):
+        (sideState.shouldStop):
+        (Basic.For):
+        (Basic.Next):
+        (Basic.Print):
+        (Basic.Input):
+        (Basic.Read):
+        (Basic.Restore):
+        (Basic.Dim):
+        (Basic.Randomize):
+        (Basic.End):
+        (Basic.Program):
+        * Basic/basic.js: Added.
+        (prepare):
+        (simulate):
+        * Basic/benchmark.js: Added.
+        (Benchmark):
+        (Benchmark.prototype.runIteration.expect):
+        (Benchmark.prototype.runIteration):
+        (runBenchmark):
+        * Basic/caseless_map.js: Added.
+        (CaselessMap):
+        * Basic/lexer.js: Added.
+        (lex.consumeWhitespace):
+        (lex.consume):
+        (lex):
+        * Basic/lexer_test.js: Added.
+        * Basic/number.js: Added.
+        (NumberValue):
+        (NumberValue.prototype.get value):
+        (NumberValue.prototype.apply):
+        (NumberValue.prototype.leftApply):
+        (NumberValue.prototype.assign):
+        (NumberArray.):
+        (NumberArray):
+        (NumberArray.prototype.apply):
+        (NumberArray.prototype.leftApply):
+        (NumberFunction):
+        (NumberFunction.prototype.apply):
+        (NumberFunction.prototype.leftApply):
+        (NativeFunction):
+        (NativeFunction.prototype.apply):
+        (NativeFunction.prototype.leftApply):
+        * Basic/parser.js: Added.
+        (parse):
+        (parse.pushToken):
+        (parse.peekToken):
+        (parse.consumeKind):
+        (parse.consumeToken):
+        (parse.parseVariable):
+        (parse.parseNumericExpression.parsePrimary):
+        (parse.parseNumericExpression.parseFactor):
+        (parse.parseNumericExpression.parseTerm):
+        (parse.parseNumericExpression):
+        (parse.parseConstant):
+        (parse.parseStringExpression):
+        (parse.isStringExpression):
+        (parse.parseRelationalExpression):
+        (parse.parseNonNegativeInteger):
+        (parse.parseGoToStatement):
+        (parse.parseGoSubStatement):
+        (parse.parseStatement):
+        (parse.parseStatements):
+        * Basic/random.js: Added.
+        (createRNG):
+        (createRNGWithFixedSeed):
+        (createRNGWithRandomSeed):
+        * Basic/state.js: Added.
+        (State):
+        (State.prototype.getValue):
+        (State.prototype.getSideState):
+        (State.prototype.abort):
+        (State.prototype.validate):
+        * Basic/test.html: Added.
+        * Basic/test.js: Added.
+        * Basic/util.js: Added.
+        (this.performance.performance.now.currentTime):
+        (else.this.preciseTime.currentTime):
+        (else.currentTime):
+        * Skipped: Make sure that we don't run Basic yet.
+
</ins><span class="cx"> 2016-06-21  Jon Lee  &lt;jonlee@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Update canvas size when benchmark begins
</span></span></pre></div>
<a id="trunkPerformanceTestsSkipped"></a>
<div class="modfile"><h4>Modified: trunk/PerformanceTests/Skipped (202445 => 202446)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/PerformanceTests/Skipped        2016-06-24 20:53:09 UTC (rev 202445)
+++ trunk/PerformanceTests/Skipped        2016-06-24 20:59:16 UTC (rev 202446)
</span><span class="lines">@@ -97,6 +97,7 @@
</span><span class="cx"> Animometer
</span><span class="cx"> JSBench
</span><span class="cx"> Air.js
</span><ins>+Basic
</ins><span class="cx"> 
</span><span class="cx"> # Used for iOS testing
</span><span class="cx"> Animation/css-animation.html
</span></span></pre>
</div>
</div>

</body>
</html>