Update V8 to version 4.1.0.21

This is a cherry-pick of all commits up to and including the
4.1.0.21 cherry-pick in Chromium.

Original commit message:

Version 4.1.0.21 (cherry-pick)

Merged 206e9136bde0f2b5ae8cb77afbb1e7833e5bd412

Unlink pages from the space page list after evacuation.

BUG=430201
LOG=N
R=jkummerow@chromium.org

Review URL: https://codereview.chromium.org/953813002

Cr-Commit-Position: refs/branch-heads/4.1@{#22}
Cr-Branched-From: 2e08d2a7aa9d65d269d8c57aba82eb38a8cb0a18-refs/heads/candidates@{#25353}

---

FPIIM-449

Change-Id: I8c23c7bbb70772b4858fe8a47b64fa97ee0d1f8c
diff --git a/test/js-perf-test/Classes/default-constructor.js b/test/js-perf-test/Classes/default-constructor.js
new file mode 100644
index 0000000..49dcaa6
--- /dev/null
+++ b/test/js-perf-test/Classes/default-constructor.js
@@ -0,0 +1,33 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+'use strict';
+
+var DefaultConstructorBenchmark = new BenchmarkSuite('DefaultConstructor',
+    [100], [
+      new Benchmark('NoSuperClass', false, false, 0, NoSuperClass),
+      new Benchmark('WithSuperClass', false, false, 0, WithSuperClass),
+      new Benchmark('WithSuperClassArguments', false, false, 0,
+                    WithSuperClassArguments),
+    ]);
+
+
+class BaseClass {}
+
+
+class DerivedClass extends BaseClass {}
+
+
+function NoSuperClass() {
+  return new BaseClass();
+}
+
+
+function WithSuperClass() {
+  return new DerivedClass();
+}
+
+
+function WithSuperClassArguments() {
+  return new DerivedClass(0, 1, 2, 3, 4);
+}
diff --git a/test/js-perf-test/Classes/run.js b/test/js-perf-test/Classes/run.js
new file mode 100644
index 0000000..5d48b32
--- /dev/null
+++ b/test/js-perf-test/Classes/run.js
@@ -0,0 +1,28 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+load('../base.js');
+load('super.js');
+load('default-constructor.js');
+
+
+var success = true;
+
+function PrintResult(name, result) {
+  print(name + '-Classes(Score): ' + result);
+}
+
+
+function PrintError(name, error) {
+  PrintResult(name, error);
+  success = false;
+}
+
+
+BenchmarkSuite.config.doWarmup = undefined;
+BenchmarkSuite.config.doDeterministic = undefined;
+
+BenchmarkSuite.RunSuites({ NotifyResult: PrintResult,
+                           NotifyError: PrintError });
diff --git a/test/js-perf-test/Classes/super.js b/test/js-perf-test/Classes/super.js
new file mode 100644
index 0000000..a9ec766
--- /dev/null
+++ b/test/js-perf-test/Classes/super.js
@@ -0,0 +1,59 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+'use strict';
+
+var SuperBenchmark = new BenchmarkSuite('Super', [100], [
+     new Benchmark('SuperMethodCall', false, false, 0, SuperMethodCall),
+     new Benchmark('SuperGetterCall', false, false, 0, SuperGetterCall),
+     new Benchmark('SuperSetterCall', false, false, 0, SuperSetterCall),
+]);
+
+
+function Base() { }
+Base.prototype = {
+  constructor: Base,
+  get x() {
+    return this._x++;
+  },
+  set x(v) {
+    this._x += v;
+    return this._x;
+  }
+}
+
+Base.prototype.f = function() {
+  return this._x++;
+}.toMethod(Base.prototype);
+
+function Derived() {
+  this._x = 1;
+}
+Derived.prototype = Object.create(Base.prototype);
+Object.setPrototypeOf(Derived, Base);
+
+Derived.prototype.SuperCall = function() {
+  return super.f();
+}.toMethod(Derived.prototype);
+
+Derived.prototype.GetterCall = function() {
+  return super.x;
+}.toMethod(Derived.prototype);
+
+Derived.prototype.SetterCall = function() {
+  return super.x = 5;
+}.toMethod(Derived.prototype);
+
+var derived = new Derived();
+
+function SuperMethodCall() {
+  return derived.SuperCall();
+}
+
+function SuperGetterCall() {
+  return derived.GetterCall();
+}
+
+function SuperSetterCall() {
+  return derived.SetterCall();
+}
diff --git a/test/js-perf-test/Collections/common.js b/test/js-perf-test/Collections/common.js
new file mode 100644
index 0000000..3ea3933
--- /dev/null
+++ b/test/js-perf-test/Collections/common.js
@@ -0,0 +1,31 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+var N = 10;
+var keys;
+
+
+function SetupSmiKeys() {
+  keys = new Array(N * 2);
+  for (var i = 0; i < N * 2; i++) {
+    keys[i] = i;
+  }
+}
+
+
+function SetupStringKeys() {
+  keys = new Array(N * 2);
+  for (var i = 0; i < N * 2; i++) {
+    keys[i] = 's' + i;
+  }
+}
+
+
+function SetupObjectKeys() {
+  keys = new Array(N * 2);
+  for (var i = 0; i < N * 2; i++) {
+    keys[i] = {};
+  }
+}
diff --git a/test/js-perf-test/Collections/map.js b/test/js-perf-test/Collections/map.js
new file mode 100644
index 0000000..4f55798
--- /dev/null
+++ b/test/js-perf-test/Collections/map.js
@@ -0,0 +1,217 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+var MapSmiBenchmark = new BenchmarkSuite('Map-Smi', [1000], [
+  new Benchmark('Set', false, false, 0, MapSetSmi, MapSetupSmiBase, MapTearDown),
+  new Benchmark('Has', false, false, 0, MapHasSmi, MapSetupSmi, MapTearDown),
+  new Benchmark('Get', false, false, 0, MapGetSmi, MapSetupSmi, MapTearDown),
+  new Benchmark('Delete', false, false, 0, MapDeleteSmi, MapSetupSmi, MapTearDown),
+]);
+
+
+var MapStringBenchmark = new BenchmarkSuite('Map-String', [1000], [
+  new Benchmark('Set', false, false, 0, MapSetString, MapSetupStringBase, MapTearDown),
+  new Benchmark('Has', false, false, 0, MapHasString, MapSetupString, MapTearDown),
+  new Benchmark('Get', false, false, 0, MapGetString, MapSetupString, MapTearDown),
+  new Benchmark('Delete', false, false, 0, MapDeleteString, MapSetupString, MapTearDown),
+]);
+
+
+var MapObjectBenchmark = new BenchmarkSuite('Map-Object', [1000], [
+  new Benchmark('Set', false, false, 0, MapSetObject, MapSetupObjectBase, MapTearDown),
+  new Benchmark('Has', false, false, 0, MapHasObject, MapSetupObject, MapTearDown),
+  new Benchmark('Get', false, false, 0, MapGetObject, MapSetupObject, MapTearDown),
+  new Benchmark('Delete', false, false, 0, MapDeleteObject, MapSetupObject, MapTearDown),
+]);
+
+
+var MapIterationBenchmark = new BenchmarkSuite('Map-Iteration', [1000], [
+  new Benchmark('ForEach', false, false, 0, MapForEach, MapSetupSmi, MapTearDown),
+]);
+
+
+var map;
+
+
+function MapSetupSmiBase() {
+  SetupSmiKeys();
+  map = new Map;
+}
+
+
+function MapSetupSmi() {
+  MapSetupSmiBase();
+  MapSetSmi();
+}
+
+
+function MapSetupStringBase() {
+  SetupStringKeys();
+  map = new Map;
+}
+
+
+function MapSetupString() {
+  MapSetupStringBase();
+  MapSetString();
+}
+
+
+function MapSetupObjectBase() {
+  SetupObjectKeys();
+  map = new Map;
+}
+
+
+function MapSetupObject() {
+  MapSetupObjectBase();
+  MapSetObject();
+}
+
+
+function MapTearDown() {
+  map = null;
+}
+
+
+function MapSetSmi() {
+  for (var i = 0; i < N; i++) {
+    map.set(keys[i], i);
+  }
+}
+
+
+function MapHasSmi() {
+  for (var i = 0; i < N; i++) {
+    if (!map.has(keys[i])) {
+      throw new Error();
+    }
+  }
+  for (var i = N; i < 2 * N; i++) {
+    if (map.has(keys[i])) {
+      throw new Error();
+    }
+  }
+}
+
+
+function MapGetSmi() {
+  for (var i = 0; i < N; i++) {
+    if (map.get(keys[i]) !== i) {
+      throw new Error();
+    }
+  }
+  for (var i = N; i < 2 * N; i++) {
+    if (map.get(keys[i]) !== undefined) {
+      throw new Error();
+    }
+  }
+}
+
+
+function MapDeleteSmi() {
+  // This is run more than once per setup so we will end up deleting items
+  // more than once. Therefore, we do not the return value of delete.
+  for (var i = 0; i < N; i++) {
+    map.delete(keys[i]);
+  }
+}
+
+
+function MapSetString() {
+  for (var i = 0; i < N; i++) {
+    map.set(keys[i], i);
+  }
+}
+
+
+function MapHasString() {
+  for (var i = 0; i < N; i++) {
+    if (!map.has(keys[i])) {
+      throw new Error();
+    }
+  }
+  for (var i = N; i < 2 * N; i++) {
+    if (map.has(keys[i])) {
+      throw new Error();
+    }
+  }
+}
+
+
+function MapGetString() {
+  for (var i = 0; i < N; i++) {
+    if (map.get(keys[i]) !== i) {
+      throw new Error();
+    }
+  }
+  for (var i = N; i < 2 * N; i++) {
+    if (map.get(keys[i]) !== undefined) {
+      throw new Error();
+    }
+  }
+}
+
+
+function MapDeleteString() {
+  // This is run more than once per setup so we will end up deleting items
+  // more than once. Therefore, we do not the return value of delete.
+  for (var i = 0; i < N; i++) {
+    map.delete(keys[i]);
+  }
+}
+
+
+function MapSetObject() {
+  for (var i = 0; i < N; i++) {
+    map.set(keys[i], i);
+  }
+}
+
+
+function MapHasObject() {
+  for (var i = 0; i < N; i++) {
+    if (!map.has(keys[i])) {
+      throw new Error();
+    }
+  }
+  for (var i = N; i < 2 * N; i++) {
+    if (map.has(keys[i])) {
+      throw new Error();
+    }
+  }
+}
+
+
+function MapGetObject() {
+  for (var i = 0; i < N; i++) {
+    if (map.get(keys[i]) !== i) {
+      throw new Error();
+    }
+  }
+  for (var i = N; i < 2 * N; i++) {
+    if (map.get(keys[i]) !== undefined) {
+      throw new Error();
+    }
+  }
+}
+
+
+function MapDeleteObject() {
+  // This is run more than once per setup so we will end up deleting items
+  // more than once. Therefore, we do not the return value of delete.
+  for (var i = 0; i < N; i++) {
+    map.delete(keys[i]);
+  }
+}
+
+
+function MapForEach() {
+  map.forEach(function(v, k) {
+    if (v !== k) {
+      throw new Error();
+    }
+  });
+}
diff --git a/test/js-perf-test/Collections/run.js b/test/js-perf-test/Collections/run.js
new file mode 100644
index 0000000..50f1ee1
--- /dev/null
+++ b/test/js-perf-test/Collections/run.js
@@ -0,0 +1,31 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+load('../base.js');
+load('common.js');
+load('map.js');
+load('set.js');
+load('weakmap.js');
+load('weakset.js');
+
+
+var success = true;
+
+function PrintResult(name, result) {
+  print(name + '-Collections(Score): ' + result);
+}
+
+
+function PrintError(name, error) {
+  PrintResult(name, error);
+  success = false;
+}
+
+
+BenchmarkSuite.config.doWarmup = undefined;
+BenchmarkSuite.config.doDeterministic = undefined;
+
+BenchmarkSuite.RunSuites({ NotifyResult: PrintResult,
+                           NotifyError: PrintError });
diff --git a/test/js-perf-test/Collections/set.js b/test/js-perf-test/Collections/set.js
new file mode 100644
index 0000000..3be27f5
--- /dev/null
+++ b/test/js-perf-test/Collections/set.js
@@ -0,0 +1,172 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+var SetSmiBenchmark = new BenchmarkSuite('Set-Smi', [1000], [
+  new Benchmark('Set', false, false, 0, SetAddSmi, SetSetupSmiBase, SetTearDown),
+  new Benchmark('Has', false, false, 0, SetHasSmi, SetSetupSmi, SetTearDown),
+  new Benchmark('Delete', false, false, 0, SetDeleteSmi, SetSetupSmi, SetTearDown),
+]);
+
+
+var SetStringBenchmark = new BenchmarkSuite('Set-String', [1000], [
+  new Benchmark('Set', false, false, 0, SetAddString, SetSetupStringBase, SetTearDown),
+  new Benchmark('Has', false, false, 0, SetHasString, SetSetupString, SetTearDown),
+  new Benchmark('Delete', false, false, 0, SetDeleteString, SetSetupString, SetTearDown),
+]);
+
+
+var SetObjectBenchmark = new BenchmarkSuite('Set-Object', [1000], [
+  new Benchmark('Set', false, false, 0, SetAddObject, SetSetupObjectBase, SetTearDown),
+  new Benchmark('Has', false, false, 0, SetHasObject, SetSetupObject, SetTearDown),
+  new Benchmark('Delete', false, false, 0, SetDeleteObject, SetSetupObject, SetTearDown),
+]);
+
+
+var SetIterationBenchmark = new BenchmarkSuite('Set-Iteration', [1000], [
+  new Benchmark('ForEach', false, false, 0, SetForEach, SetSetupSmi, SetTearDown),
+]);
+
+
+var set;
+
+
+function SetSetupSmiBase() {
+  SetupSmiKeys();
+  set = new Set;
+}
+
+
+function SetSetupSmi() {
+  SetSetupSmiBase();
+  SetAddSmi();
+}
+
+
+function SetSetupStringBase() {
+  SetupStringKeys();
+  set = new Set;
+}
+
+
+function SetSetupString() {
+  SetSetupStringBase();
+  SetAddString();
+}
+
+
+function SetSetupObjectBase() {
+  SetupObjectKeys();
+  set = new Set;
+}
+
+
+function SetSetupObject() {
+  SetSetupObjectBase();
+  SetAddObject();
+}
+
+
+function SetTearDown() {
+  set = null;
+}
+
+
+function SetAddSmi() {
+  for (var i = 0; i < N; i++) {
+    set.add(keys[i], i);
+  }
+}
+
+
+function SetHasSmi() {
+  for (var i = 0; i < N; i++) {
+    if (!set.has(keys[i])) {
+      throw new Error();
+    }
+  }
+  for (var i = N; i < 2 * N; i++) {
+    if (set.has(keys[i])) {
+      throw new Error();
+    }
+  }
+}
+
+
+function SetDeleteSmi() {
+  // This is run more than once per setup so we will end up deleting items
+  // more than once. Therefore, we do not the return value of delete.
+  for (var i = 0; i < N; i++) {
+    set.delete(keys[i]);
+  }
+}
+
+
+function SetAddString() {
+  for (var i = 0; i < N; i++) {
+    set.add(keys[i], i);
+  }
+}
+
+
+function SetHasString() {
+  for (var i = 0; i < N; i++) {
+    if (!set.has(keys[i])) {
+      throw new Error();
+    }
+  }
+  for (var i = N; i < 2 * N; i++) {
+    if (set.has(keys[i])) {
+      throw new Error();
+    }
+  }
+}
+
+
+function SetDeleteString() {
+  // This is run more than once per setup so we will end up deleting items
+  // more than once. Therefore, we do not the return value of delete.
+  for (var i = 0; i < N; i++) {
+    set.delete(keys[i]);
+  }
+}
+
+
+function SetAddObject() {
+  for (var i = 0; i < N; i++) {
+    set.add(keys[i], i);
+  }
+}
+
+
+function SetHasObject() {
+  for (var i = 0; i < N; i++) {
+    if (!set.has(keys[i])) {
+      throw new Error();
+    }
+  }
+  for (var i = N; i < 2 * N; i++) {
+    if (set.has(keys[i])) {
+      throw new Error();
+    }
+  }
+}
+
+
+function SetDeleteObject() {
+  // This is run more than once per setup so we will end up deleting items
+  // more than once. Therefore, we do not the return value of delete.
+  for (var i = 0; i < N; i++) {
+    set.delete(keys[i]);
+  }
+}
+
+
+function SetForEach() {
+  set.forEach(function(v, k) {
+    if (v !== k) {
+      throw new Error();
+    }
+  });
+}
diff --git a/test/js-perf-test/Collections/weakmap.js b/test/js-perf-test/Collections/weakmap.js
new file mode 100644
index 0000000..9aa265f
--- /dev/null
+++ b/test/js-perf-test/Collections/weakmap.js
@@ -0,0 +1,79 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+var MapBenchmark = new BenchmarkSuite('WeakMap', [1000], [
+  new Benchmark('Set', false, false, 0, WeakMapSet, WeakMapSetupBase,
+      WeakMapTearDown),
+  new Benchmark('Has', false, false, 0, WeakMapHas, WeakMapSetup,
+      WeakMapTearDown),
+  new Benchmark('Get', false, false, 0, WeakMapGet, WeakMapSetup,
+      WeakMapTearDown),
+  new Benchmark('Delete', false, false, 0, WeakMapDelete, WeakMapSetup,
+      WeakMapTearDown),
+]);
+
+
+var wm;
+
+
+function WeakMapSetupBase() {
+  SetupObjectKeys();
+  wm = new WeakMap;
+}
+
+
+function WeakMapSetup() {
+  WeakMapSetupBase();
+  WeakMapSet();
+}
+
+
+function WeakMapTearDown() {
+  wm = null;
+}
+
+
+function WeakMapSet() {
+  for (var i = 0; i < N; i++) {
+    wm.set(keys[i], i);
+  }
+}
+
+
+function WeakMapHas() {
+  for (var i = 0; i < N; i++) {
+    if (!wm.has(keys[i])) {
+      throw new Error();
+    }
+  }
+  for (var i = N; i < 2 * N; i++) {
+    if (wm.has(keys[i])) {
+      throw new Error();
+    }
+  }
+}
+
+
+function WeakMapGet() {
+  for (var i = 0; i < N; i++) {
+    if (wm.get(keys[i]) !== i) {
+      throw new Error();
+    }
+  }
+  for (var i = N; i < 2 * N; i++) {
+    if (wm.get(keys[i]) !== undefined) {
+      throw new Error();
+    }
+  }
+}
+
+
+function WeakMapDelete() {
+  // This is run more than once per setup so we will end up deleting items
+  // more than once. Therefore, we do not the return value of delete.
+  for (var i = 0; i < N; i++) {
+    wm.delete(keys[i]);
+  }
+}
diff --git a/test/js-perf-test/Collections/weakset.js b/test/js-perf-test/Collections/weakset.js
new file mode 100644
index 0000000..2936477
--- /dev/null
+++ b/test/js-perf-test/Collections/weakset.js
@@ -0,0 +1,63 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+var SetBenchmark = new BenchmarkSuite('WeakSet', [1000], [
+  new Benchmark('Add', false, false, 0, WeakSetAdd, WeakSetSetupBase,
+      WeakSetTearDown),
+  new Benchmark('Has', false, false, 0, WeakSetHas, WeakSetSetup,
+      WeakSetTearDown),
+  new Benchmark('Delete', false, false, 0, WeakSetDelete, WeakSetSetup,
+      WeakSetTearDown),
+]);
+
+
+var ws;
+
+
+function WeakSetSetupBase() {
+  SetupObjectKeys();
+  ws = new WeakSet;
+}
+
+
+function WeakSetSetup() {
+  WeakSetSetupBase();
+  WeakSetAdd();
+}
+
+
+function WeakSetTearDown() {
+  ws = null;
+}
+
+
+function WeakSetAdd() {
+  for (var i = 0; i < N; i++) {
+    ws.add(keys[i]);
+  }
+}
+
+
+function WeakSetHas() {
+  for (var i = 0; i < N; i++) {
+    if (!ws.has(keys[i])) {
+      throw new Error();
+    }
+  }
+  for (var i = N; i < 2 * N; i++) {
+    if (ws.has(keys[i])) {
+      throw new Error();
+    }
+  }
+}
+
+
+function WeakSetDelete() {
+  // This is run more than once per setup so we will end up deleting items
+  // more than once. Therefore, we do not the return value of delete.
+  for (var i = 0; i < N; i++) {
+    ws.delete(keys[i]);
+  }
+}
diff --git a/test/js-perf-test/Iterators/forof.js b/test/js-perf-test/Iterators/forof.js
new file mode 100644
index 0000000..1877ebe
--- /dev/null
+++ b/test/js-perf-test/Iterators/forof.js
@@ -0,0 +1,94 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+new BenchmarkSuite('ForOf', [1000], [
+  new Benchmark('ArrayValues', false, false, 0,
+                ForOf, ForOfArraySetup, ForOfTearDown),
+  new Benchmark('ArrayKeys', false, false, 0,
+                ForOf, ForOfArrayKeysSetup, ForOfTearDown),
+  new Benchmark('ArrayEntries', false, false, 0,
+                ForOf, ForOfArrayEntriesSetup, ForOfTearDown),
+  new Benchmark('Uint8Array', false, false, 0,
+                ForOf, ForOfUint8ArraySetup, ForOfTearDown),
+  new Benchmark('Float64Array', false, false, 0,
+                ForOf, ForOfFloat64ArraySetup, ForOfTearDown),
+  new Benchmark('String', false, false, 0,
+                ForOf, ForOfStringSetup, ForOfTearDown),
+]);
+
+
+var iterable;
+var N = 100;
+var expected, result;
+
+
+function ForOfArraySetupHelper(constructor) {
+  iterable = new constructor(N);
+  for (var i = 0; i < N; i++) iterable[i] = i;
+  expected = N - 1;
+}
+
+
+function ForOfArraySetup() {
+  ForOfArraySetupHelper(Array);
+  // Default iterator is values().
+}
+
+
+function ForOfArrayKeysSetup() {
+  ForOfArraySetupHelper(Array);
+  iterable = iterable.keys();
+}
+
+
+function ForOfArrayEntriesSetup() {
+  ForOfArraySetupHelper(Array);
+  iterable = iterable.entries();
+  expected = [N-1, N-1];
+}
+
+
+function ForOfUint8ArraySetup() {
+  ForOfArraySetupHelper(Uint8Array);
+}
+
+
+function ForOfFloat64ArraySetup() {
+  ForOfArraySetupHelper(Float64Array);
+}
+
+
+function ForOfStringSetup() {
+  iterable = "abcdefhijklmnopqrstuvwxyzABCDEFHIJKLMNOPQRSTUVWXYZ0123456789";
+  expected = "9";
+}
+
+
+function Equals(expected, actual) {
+  if (expected === actual) return true;
+  if (typeof expected !== typeof actual) return false;
+  if (typeof expected !== 'object') return false;
+  for (var k of Object.keys(expected)) {
+    if (!(k in actual)) return false;
+    if (!Equals(expected[k], actual[k])) return false;
+  }
+  for (var k of Object.keys(actual)) {
+    if (!(k in expected)) return false;
+  }
+  return true;
+}
+
+function ForOfTearDown() {
+  iterable = null;
+  if (!Equals(expected, result)) {
+    throw new Error("Bad result: " + result);
+  }
+}
+
+
+function ForOf() {
+  for (var x of iterable) {
+    result = x;
+  }
+}
diff --git a/test/js-perf-test/Iterators/run.js b/test/js-perf-test/Iterators/run.js
new file mode 100644
index 0000000..ff4897f
--- /dev/null
+++ b/test/js-perf-test/Iterators/run.js
@@ -0,0 +1,27 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+load('../base.js');
+load('forof.js');
+
+
+var success = true;
+
+function PrintResult(name, result) {
+  print(name + '-Iterators(Score): ' + result);
+}
+
+
+function PrintError(name, error) {
+  PrintResult(name, error);
+  success = false;
+}
+
+
+BenchmarkSuite.config.doWarmup = undefined;
+BenchmarkSuite.config.doDeterministic = undefined;
+
+BenchmarkSuite.RunSuites({ NotifyResult: PrintResult,
+                           NotifyError: PrintError });
diff --git a/test/js-perf-test/JSTests.json b/test/js-perf-test/JSTests.json
new file mode 100644
index 0000000..0a99ad4
--- /dev/null
+++ b/test/js-perf-test/JSTests.json
@@ -0,0 +1,86 @@
+{
+  "name": "JSTests",
+  "run_count": 5,
+  "run_count_android_arm": 3,
+  "run_count_android_arm64": 3,
+  "units": "score",
+  "total": true,
+  "resources": ["base.js"],
+  "tests": [
+    {
+      "name": "Classes",
+      "path": ["Classes"],
+      "main": "run.js",
+      "resources": ["super.js", "default-constructor.js"],
+      "flags": ["--harmony-classes"],
+      "results_regexp": "^%s\\-Classes\\(Score\\): (.+)$",
+      "tests": [
+        {"name": "Super"},
+        {"name": "DefaultConstructor"}
+      ]
+    },
+    {
+      "name": "Collections",
+      "path": ["Collections"],
+      "main": "run.js",
+      "resources": [
+        "common.js",
+        "map.js",
+        "run.js",
+        "set.js",
+        "weakmap.js",
+        "weakset.js"
+      ],
+      "results_regexp": "^%s\\-Collections\\(Score\\): (.+)$",
+      "tests": [
+        {"name": "Map-Smi"},
+        {"name": "Map-String"},
+        {"name": "Map-Object"},
+        {"name": "Map-Iteration"},
+        {"name": "Set-Smi"},
+        {"name": "Set-String"},
+        {"name": "Set-Object"},
+        {"name": "Set-Iteration"},
+        {"name": "WeakMap"},
+        {"name": "WeakSet"}
+      ]
+    },
+    {
+      "name": "Iterators",
+      "path": ["Iterators"],
+      "main": "run.js",
+      "resources": ["forof.js"],
+      "results_regexp": "^%s\\-Iterators\\(Score\\): (.+)$",
+      "tests": [
+        {"name": "ForOf"}
+      ]
+    },
+    {
+      "name": "Strings",
+      "path": ["Strings"],
+      "main": "run.js",
+      "resources": ["harmony-string.js"],
+      "flags": ["--harmony-strings"],
+      "results_regexp": "^%s\\-Strings\\(Score\\): (.+)$",
+      "tests": [
+        {"name": "StringFunctions"}
+      ]
+    },
+    {
+      "name": "Templates",
+      "path": ["Templates"],
+      "main": "run.js",
+      "resources": ["templates.js"],
+      "flags": ["--harmony-templates"],
+      "run_count": 5,
+      "units": "score",
+      "results_regexp": "^%s\\-Templates\\(Score\\): (.+)$",
+      "total": true,
+      "tests": [
+        {"name": "Untagged"},
+        {"name": "LargeUntagged"},
+        {"name": "Tagged"}
+      ]
+    }
+  ]
+}
diff --git a/test/js-perf-test/Strings/harmony-string.js b/test/js-perf-test/Strings/harmony-string.js
new file mode 100644
index 0000000..c2eac4e
--- /dev/null
+++ b/test/js-perf-test/Strings/harmony-string.js
@@ -0,0 +1,111 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+new BenchmarkSuite('StringFunctions', [1000], [
+  new Benchmark('StringRepeat', false, false, 0,
+                Repeat, RepeatSetup, RepeatTearDown),
+  new Benchmark('StringStartsWith', false, false, 0,
+                StartsWith, WithSetup, WithTearDown),
+  new Benchmark('StringEndsWith', false, false, 0,
+                EndsWith, WithSetup, WithTearDown),
+  new Benchmark('StringIncludes', false, false, 0,
+                Includes, IncludesSetup, WithTearDown),
+  new Benchmark('StringFromCodePoint', false, false, 0,
+                FromCodePoint, FromCodePointSetup, FromCodePointTearDown),
+  new Benchmark('StringCodePointAt', false, false, 0,
+                CodePointAt, CodePointAtSetup, CodePointAtTearDown),
+]);
+
+
+var result;
+
+var stringRepeatSource = "abc";
+
+function RepeatSetup() {
+  result = undefined;
+}
+
+function Repeat() {
+  result = stringRepeatSource.repeat(500);
+}
+
+function RepeatTearDown() {
+  var expected = "";
+  for(var i = 0; i < 1000; i++) {
+    expected += stringRepeatSource;
+  }
+  return result === expected;
+}
+
+
+var str;
+var substr;
+
+function WithSetup() {
+  str = "abc".repeat(500);
+  substr = "abc".repeat(200);
+  result = undefined;
+}
+
+function WithTearDown() {
+  return !!result;
+}
+
+function StartsWith() {
+  result = str.startsWith(substr);
+}
+
+function EndsWith() {
+  result = str.endsWith(substr);
+}
+
+function IncludesSetup() {
+  str = "def".repeat(100) + "abc".repeat(100) + "qqq".repeat(100);
+  substr = "abc".repeat(100);
+}
+
+function Includes() {
+  result = str.includes(substr);
+}
+
+var MAX_CODE_POINT = 0xFFFFF;
+
+function FromCodePointSetup() {
+  result = new Array(MAX_CODE_POINT + 1);
+}
+
+function FromCodePoint() {
+  for (var i = 0; i <= MAX_CODE_POINT; i++) {
+    result[i] = String.fromCodePoint(i);
+  }
+}
+
+function FromCodePointTearDown() {
+  for (var i = 0; i <= MAX_CODE_POINT; i++) {
+    if (i !== result[i].codePointAt(0)) return false;
+  }
+  return true;
+}
+
+
+var allCodePoints;
+
+function CodePointAtSetup() {
+  allCodePoints = new Array(MAX_CODE_POINT + 1);
+  for (var i = 0; i <= MAX_CODE_POINT; i++) {
+    allCodePoints = String.fromCodePoint(i);
+  }
+  result = undefined;
+}
+
+function CodePointAt() {
+  result = 0;
+  for (var i = 0; i <= MAX_CODE_POINT; i++) {
+    result += allCodePoints.codePointAt(i);
+  }
+}
+
+function CodePointAtTearDown() {
+  return result === MAX_CODE_POINT * (MAX_CODE_POINT + 1) / 2;
+}
diff --git a/test/js-perf-test/Strings/run.js b/test/js-perf-test/Strings/run.js
new file mode 100644
index 0000000..79ca26e
--- /dev/null
+++ b/test/js-perf-test/Strings/run.js
@@ -0,0 +1,27 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+load('../base.js');
+load('harmony-string.js');
+
+
+var success = true;
+
+function PrintResult(name, result) {
+  print(name + '-Strings(Score): ' + result);
+}
+
+
+function PrintError(name, error) {
+  PrintResult(name, error);
+  success = false;
+}
+
+
+BenchmarkSuite.config.doWarmup = undefined;
+BenchmarkSuite.config.doDeterministic = undefined;
+
+BenchmarkSuite.RunSuites({ NotifyResult: PrintResult,
+                           NotifyError: PrintError });
diff --git a/test/js-perf-test/Templates/run.js b/test/js-perf-test/Templates/run.js
new file mode 100644
index 0000000..73f1edd
--- /dev/null
+++ b/test/js-perf-test/Templates/run.js
@@ -0,0 +1,27 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+load('../base.js');
+load('templates.js');
+
+
+var success = true;
+
+function PrintResult(name, result) {
+  print(name + '-Templates(Score): ' + result);
+}
+
+
+function PrintError(name, error) {
+  PrintResult(name, error);
+  success = false;
+}
+
+
+BenchmarkSuite.config.doWarmup = undefined;
+BenchmarkSuite.config.doDeterministic = undefined;
+
+BenchmarkSuite.RunSuites({ NotifyResult: PrintResult,
+                           NotifyError: PrintError });
diff --git a/test/js-perf-test/Templates/templates.js b/test/js-perf-test/Templates/templates.js
new file mode 100644
index 0000000..4034ce7
--- /dev/null
+++ b/test/js-perf-test/Templates/templates.js
@@ -0,0 +1,87 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+new BenchmarkSuite('Untagged', [1000], [
+  new Benchmark('Untagged-Simple', false, false, 0,
+                Untagged, UntaggedSetup, UntaggedTearDown),
+]);
+
+new BenchmarkSuite('LargeUntagged', [1000], [
+  new Benchmark('Untagged-Large', false, false, 0,
+                UntaggedLarge, UntaggedLargeSetup, UntaggedLargeTearDown),
+]);
+
+new BenchmarkSuite('Tagged', [1000], [
+  new Benchmark('TaggedRawSimple', false, false, 0,
+                TaggedRaw, TaggedRawSetup, TaggedRawTearDown),
+]);
+
+var result;
+var SUBJECT = 'Bob';
+var TARGET = 'Mary';
+var OBJECT = 'apple';
+
+function UntaggedSetup() {
+  result = undefined;
+}
+
+function Untagged() {
+  result = `${SUBJECT} gives ${TARGET} an ${OBJECT}.`;
+}
+
+function UntaggedTearDown() {
+  var expected = '' + SUBJECT + ' gives ' + TARGET + ' an ' + OBJECT + '.';
+  return result === expected;
+}
+
+
+// ----------------------------------------------------------------------------
+
+function UntaggedLargeSetup() {
+  result = undefined;
+}
+
+function UntaggedLarge() {
+  result = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus
+ aliquam, elit euismod vestibulum ${0}lacinia, arcu odio sagittis mauris, id
+ blandit dolor felis pretium nisl. Maecenas porttitor, nunc ut accumsan mollis,
+ arcu metus rutrum arcu, ${1}ut varius dolor lorem nec risus. Integer convallis
+ tristique ante, non pretium ante suscipit at. Sed egestas massa enim, convallis
+ fermentum neque vehicula ac. Donec imperdiet a tortor ac semper. Morbi accumsan
+ quam nec erat viverra iaculis. ${2}Donec a scelerisque cras amet.`;
+}
+
+function UntaggedLargeTearDown() {
+  var expected = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
+      "Vivamus\n aliquam, elit euismod vestibulum " + 0 + "lacinia, arcu odio" +
+      " sagittis mauris, id\n blandit dolor felis pretium nisl. Maecenas " +
+      "porttitor, nunc ut accumsan mollis,\n arcu metus rutrum arcu, " + 1 +
+      "ut varius dolor lorem nec risus. Integer convallis\n tristique ante, " +
+      "non pretium ante suscipit at. Sed egestas massa enim, convallis\n " +
+      "fermentum neque vehicula ac. Donec imperdiet a tortor ac semper. Morbi" +
+      " accumsan\n quam nec erat viverra iaculis. " + 2 + "Donec a " +
+      "scelerisque cras amet.";
+  return result === expected;
+}
+
+
+// ----------------------------------------------------------------------------
+
+
+function TaggedRawSetup() {
+  result = undefined;
+}
+
+function TaggedRaw() {
+  result = String.raw `${SUBJECT} gives ${TARGET} an ${OBJECT} \ud83c\udf4f.`;
+}
+
+function TaggedRawTearDown() {
+  var expected =
+      '' + SUBJECT + ' gives ' + TARGET + ' an ' + OBJECT + ' \\ud83c\\udf4f.';
+  return result === expected;
+}
+
+
+// ----------------------------------------------------------------------------
diff --git a/test/js-perf-test/base.js b/test/js-perf-test/base.js
new file mode 100644
index 0000000..b0ce40b
--- /dev/null
+++ b/test/js-perf-test/base.js
@@ -0,0 +1,367 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+// Performance.now is used in latency benchmarks, the fallback is Date.now.
+var performance = performance || {};
+performance.now = (function() {
+  return performance.now       ||
+         performance.mozNow    ||
+         performance.msNow     ||
+         performance.oNow      ||
+         performance.webkitNow ||
+         Date.now;
+})();
+
+// Simple framework for running the benchmark suites and
+// computing a score based on the timing measurements.
+
+
+// A benchmark has a name (string) and a function that will be run to
+// do the performance measurement. The optional setup and tearDown
+// arguments are functions that will be invoked before and after
+// running the benchmark, but the running time of these functions will
+// not be accounted for in the benchmark score.
+function Benchmark(name, doWarmup, doDeterministic, deterministicIterations,
+                   run, setup, tearDown, rmsResult, minIterations) {
+  this.name = name;
+  this.doWarmup = doWarmup;
+  this.doDeterministic = doDeterministic;
+  this.deterministicIterations = deterministicIterations;
+  this.run = run;
+  this.Setup = setup ? setup : function() { };
+  this.TearDown = tearDown ? tearDown : function() { };
+  this.rmsResult = rmsResult ? rmsResult : null;
+  this.minIterations = minIterations ? minIterations : 32;
+}
+
+
+// Benchmark results hold the benchmark and the measured time used to
+// run the benchmark. The benchmark score is computed later once a
+// full benchmark suite has run to completion. If latency is set to 0
+// then there is no latency score for this benchmark.
+function BenchmarkResult(benchmark, time, latency) {
+  this.benchmark = benchmark;
+  this.time = time;
+  this.latency = latency;
+}
+
+
+// Automatically convert results to numbers. Used by the geometric
+// mean computation.
+BenchmarkResult.prototype.valueOf = function() {
+  return this.time;
+}
+
+
+// Suites of benchmarks consist of a name and the set of benchmarks in
+// addition to the reference timing that the final score will be based
+// on. This way, all scores are relative to a reference run and higher
+// scores implies better performance.
+function BenchmarkSuite(name, reference, benchmarks) {
+  this.name = name;
+  this.reference = reference;
+  this.benchmarks = benchmarks;
+  BenchmarkSuite.suites.push(this);
+}
+
+
+// Keep track of all declared benchmark suites.
+BenchmarkSuite.suites = [];
+
+// Scores are not comparable across versions. Bump the version if
+// you're making changes that will affect that scores, e.g. if you add
+// a new benchmark or change an existing one.
+BenchmarkSuite.version = '1';
+
+
+// Defines global benchsuite running mode that overrides benchmark suite
+// behavior. Intended to be set by the benchmark driver. Undefined
+// values here allow a benchmark to define behaviour itself.
+BenchmarkSuite.config = {
+  doWarmup: undefined,
+  doDeterministic: undefined
+};
+
+
+// Override the alert function to throw an exception instead.
+alert = function(s) {
+  throw "Alert called with argument: " + s;
+};
+
+
+// To make the benchmark results predictable, we replace Math.random
+// with a 100% deterministic alternative.
+BenchmarkSuite.ResetRNG = function() {
+  Math.random = (function() {
+    var seed = 49734321;
+    return function() {
+      // Robert Jenkins' 32 bit integer hash function.
+      seed = ((seed + 0x7ed55d16) + (seed << 12))  & 0xffffffff;
+      seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff;
+      seed = ((seed + 0x165667b1) + (seed << 5))   & 0xffffffff;
+      seed = ((seed + 0xd3a2646c) ^ (seed << 9))   & 0xffffffff;
+      seed = ((seed + 0xfd7046c5) + (seed << 3))   & 0xffffffff;
+      seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff;
+      return (seed & 0xfffffff) / 0x10000000;
+    };
+  })();
+}
+
+
+// Runs all registered benchmark suites and optionally yields between
+// each individual benchmark to avoid running for too long in the
+// context of browsers. Once done, the final score is reported to the
+// runner.
+BenchmarkSuite.RunSuites = function(runner, skipBenchmarks) {
+  skipBenchmarks = typeof skipBenchmarks === 'undefined' ? [] : skipBenchmarks;
+  var continuation = null;
+  var suites = BenchmarkSuite.suites;
+  var length = suites.length;
+  BenchmarkSuite.scores = [];
+  var index = 0;
+  function RunStep() {
+    while (continuation || index < length) {
+      if (continuation) {
+        continuation = continuation();
+      } else {
+        var suite = suites[index++];
+        if (runner.NotifyStart) runner.NotifyStart(suite.name);
+        if (skipBenchmarks.indexOf(suite.name) > -1) {
+          suite.NotifySkipped(runner);
+        } else {
+          continuation = suite.RunStep(runner);
+        }
+      }
+      if (continuation && typeof window != 'undefined' && window.setTimeout) {
+        window.setTimeout(RunStep, 25);
+        return;
+      }
+    }
+
+    // show final result
+    if (runner.NotifyScore) {
+      var score = BenchmarkSuite.GeometricMean(BenchmarkSuite.scores);
+      var formatted = BenchmarkSuite.FormatScore(100 * score);
+      runner.NotifyScore(formatted);
+    }
+  }
+  RunStep();
+}
+
+
+// Counts the total number of registered benchmarks. Useful for
+// showing progress as a percentage.
+BenchmarkSuite.CountBenchmarks = function() {
+  var result = 0;
+  var suites = BenchmarkSuite.suites;
+  for (var i = 0; i < suites.length; i++) {
+    result += suites[i].benchmarks.length;
+  }
+  return result;
+}
+
+
+// Computes the geometric mean of a set of numbers.
+BenchmarkSuite.GeometricMean = function(numbers) {
+  var log = 0;
+  for (var i = 0; i < numbers.length; i++) {
+    log += Math.log(numbers[i]);
+  }
+  return Math.pow(Math.E, log / numbers.length);
+}
+
+
+// Computes the geometric mean of a set of throughput time measurements.
+BenchmarkSuite.GeometricMeanTime = function(measurements) {
+  var log = 0;
+  for (var i = 0; i < measurements.length; i++) {
+    log += Math.log(measurements[i].time);
+  }
+  return Math.pow(Math.E, log / measurements.length);
+}
+
+
+// Computes the geometric mean of a set of rms measurements.
+BenchmarkSuite.GeometricMeanLatency = function(measurements) {
+  var log = 0;
+  var hasLatencyResult = false;
+  for (var i = 0; i < measurements.length; i++) {
+    if (measurements[i].latency != 0) {
+      log += Math.log(measurements[i].latency);
+      hasLatencyResult = true;
+    }
+  }
+  if (hasLatencyResult) {
+    return Math.pow(Math.E, log / measurements.length);
+  } else {
+    return 0;
+  }
+}
+
+
+// Converts a score value to a string with at least three significant
+// digits.
+BenchmarkSuite.FormatScore = function(value) {
+  if (value > 100) {
+    return value.toFixed(0);
+  } else {
+    return value.toPrecision(3);
+  }
+}
+
+// Notifies the runner that we're done running a single benchmark in
+// the benchmark suite. This can be useful to report progress.
+BenchmarkSuite.prototype.NotifyStep = function(result) {
+  this.results.push(result);
+  if (this.runner.NotifyStep) this.runner.NotifyStep(result.benchmark.name);
+}
+
+
+// Notifies the runner that we're done with running a suite and that
+// we have a result which can be reported to the user if needed.
+BenchmarkSuite.prototype.NotifyResult = function() {
+  var mean = BenchmarkSuite.GeometricMeanTime(this.results);
+  var score = this.reference[0] / mean;
+  BenchmarkSuite.scores.push(score);
+  if (this.runner.NotifyResult) {
+    var formatted = BenchmarkSuite.FormatScore(100 * score);
+    this.runner.NotifyResult(this.name, formatted);
+  }
+  if (this.reference.length == 2) {
+    var meanLatency = BenchmarkSuite.GeometricMeanLatency(this.results);
+    if (meanLatency != 0) {
+      var scoreLatency = this.reference[1] / meanLatency;
+      BenchmarkSuite.scores.push(scoreLatency);
+      if (this.runner.NotifyResult) {
+        var formattedLatency = BenchmarkSuite.FormatScore(100 * scoreLatency)
+        this.runner.NotifyResult(this.name + "Latency", formattedLatency);
+      }
+    }
+  }
+}
+
+
+BenchmarkSuite.prototype.NotifySkipped = function(runner) {
+  BenchmarkSuite.scores.push(1);  // push default reference score.
+  if (runner.NotifyResult) {
+    runner.NotifyResult(this.name, "Skipped");
+  }
+}
+
+
+// Notifies the runner that running a benchmark resulted in an error.
+BenchmarkSuite.prototype.NotifyError = function(error) {
+  if (this.runner.NotifyError) {
+    this.runner.NotifyError(this.name, error);
+  }
+  if (this.runner.NotifyStep) {
+    this.runner.NotifyStep(this.name);
+  }
+}
+
+
+// Runs a single benchmark for at least a second and computes the
+// average time it takes to run a single iteration.
+BenchmarkSuite.prototype.RunSingleBenchmark = function(benchmark, data) {
+  var config = BenchmarkSuite.config;
+  var doWarmup = config.doWarmup !== undefined
+                 ? config.doWarmup
+                 : benchmark.doWarmup;
+  var doDeterministic = config.doDeterministic !== undefined
+                        ? config.doDeterministic
+                        : benchmark.doDeterministic;
+
+  function Measure(data) {
+    var elapsed = 0;
+    var start = new Date();
+
+  // Run either for 1 second or for the number of iterations specified
+  // by minIterations, depending on the config flag doDeterministic.
+    for (var i = 0; (doDeterministic ?
+      i<benchmark.deterministicIterations : elapsed < 1000); i++) {
+      benchmark.run();
+      elapsed = new Date() - start;
+    }
+    if (data != null) {
+      data.runs += i;
+      data.elapsed += elapsed;
+    }
+  }
+
+  // Sets up data in order to skip or not the warmup phase.
+  if (!doWarmup && data == null) {
+    data = { runs: 0, elapsed: 0 };
+  }
+
+  if (data == null) {
+    Measure(null);
+    return { runs: 0, elapsed: 0 };
+  } else {
+    Measure(data);
+    // If we've run too few iterations, we continue for another second.
+    if (data.runs < benchmark.minIterations) return data;
+    var usec = (data.elapsed * 1000) / data.runs;
+    var rms = (benchmark.rmsResult != null) ? benchmark.rmsResult() : 0;
+    this.NotifyStep(new BenchmarkResult(benchmark, usec, rms));
+    return null;
+  }
+}
+
+
+// This function starts running a suite, but stops between each
+// individual benchmark in the suite and returns a continuation
+// function which can be invoked to run the next benchmark. Once the
+// last benchmark has been executed, null is returned.
+BenchmarkSuite.prototype.RunStep = function(runner) {
+  BenchmarkSuite.ResetRNG();
+  this.results = [];
+  this.runner = runner;
+  var length = this.benchmarks.length;
+  var index = 0;
+  var suite = this;
+  var data;
+
+  // Run the setup, the actual benchmark, and the tear down in three
+  // separate steps to allow the framework to yield between any of the
+  // steps.
+
+  function RunNextSetup() {
+    if (index < length) {
+      try {
+        suite.benchmarks[index].Setup();
+      } catch (e) {
+        suite.NotifyError(e);
+        return null;
+      }
+      return RunNextBenchmark;
+    }
+    suite.NotifyResult();
+    return null;
+  }
+
+  function RunNextBenchmark() {
+    try {
+      data = suite.RunSingleBenchmark(suite.benchmarks[index], data);
+    } catch (e) {
+      suite.NotifyError(e);
+      return null;
+    }
+    // If data is null, we're done with this benchmark.
+    return (data == null) ? RunNextTearDown : RunNextBenchmark();
+  }
+
+  function RunNextTearDown() {
+    try {
+      suite.benchmarks[index++].TearDown();
+    } catch (e) {
+      suite.NotifyError(e);
+      return null;
+    }
+    return RunNextSetup;
+  }
+
+  // Start out running the setup.
+  return RunNextSetup();
+}