Upgrade V8 to version 4.9.385.28

https://chromium.googlesource.com/v8/v8/+/4.9.385.28

FPIIM-449

Change-Id: I4b2e74289d4bf3667f2f3dc8aa2e541f63e26eb4
diff --git a/src/js/OWNERS b/src/js/OWNERS
new file mode 100644
index 0000000..f7002c7
--- /dev/null
+++ b/src/js/OWNERS
@@ -0,0 +1,11 @@
+set noparent
+
+adamk@chromium.org
+bmeurer@chromium.org
+cbruni@chromium.org
+ishell@chromium.org
+jkummerow@chromium.org
+littledan@chromium.org
+rossberg@chromium.org
+verwaest@chromium.org
+yangguo@chromium.org
diff --git a/src/js/array-iterator.js b/src/js/array-iterator.js
new file mode 100644
index 0000000..2609ebd
--- /dev/null
+++ b/src/js/array-iterator.js
@@ -0,0 +1,154 @@
+// Copyright 2013 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.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -----------------------------------------------------------------------
+// Imports
+
+var arrayIterationKindSymbol =
+    utils.ImportNow("array_iteration_kind_symbol");
+var arrayIteratorNextIndexSymbol =
+    utils.ImportNow("array_iterator_next_symbol");
+var arrayIteratorObjectSymbol =
+    utils.ImportNow("array_iterator_object_symbol");
+var GlobalArray = global.Array;
+var IteratorPrototype = utils.ImportNow("IteratorPrototype");
+var iteratorSymbol = utils.ImportNow("iterator_symbol");
+var MakeTypeError;
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+var GlobalTypedArray = global.Uint8Array.__proto__;
+
+utils.Import(function(from) {
+  MakeTypeError = from.MakeTypeError;
+})
+
+// -----------------------------------------------------------------------
+
+function ArrayIterator() {}
+
+
+// TODO(wingo): Update section numbers when ES6 has stabilized.  The
+// section numbers below are already out of date as of the May 2014
+// draft.
+
+
+// 15.4.5.1 CreateArrayIterator Abstract Operation
+function CreateArrayIterator(array, kind) {
+  var object = TO_OBJECT(array);
+  var iterator = new ArrayIterator;
+  SET_PRIVATE(iterator, arrayIteratorObjectSymbol, object);
+  SET_PRIVATE(iterator, arrayIteratorNextIndexSymbol, 0);
+  SET_PRIVATE(iterator, arrayIterationKindSymbol, kind);
+  return iterator;
+}
+
+
+// 22.1.5.2.2 %ArrayIteratorPrototype%[@@iterator]
+function ArrayIteratorIterator() {
+    return this;
+}
+
+
+// ES6 section 22.1.5.2.1 %ArrayIteratorPrototype%.next( )
+function ArrayIteratorNext() {
+  var iterator = this;
+  var value = UNDEFINED;
+  var done = true;
+
+  if (!IS_RECEIVER(iterator) ||
+      !HAS_DEFINED_PRIVATE(iterator, arrayIteratorNextIndexSymbol)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'Array Iterator.prototype.next', this);
+  }
+
+  var array = GET_PRIVATE(iterator, arrayIteratorObjectSymbol);
+  if (!IS_UNDEFINED(array)) {
+    var index = GET_PRIVATE(iterator, arrayIteratorNextIndexSymbol);
+    var itemKind = GET_PRIVATE(iterator, arrayIterationKindSymbol);
+    var length = TO_UINT32(array.length);
+
+    // "sparse" is never used.
+
+    if (index >= length) {
+      SET_PRIVATE(iterator, arrayIteratorObjectSymbol, UNDEFINED);
+    } else {
+      SET_PRIVATE(iterator, arrayIteratorNextIndexSymbol, index + 1);
+
+      if (itemKind == ITERATOR_KIND_VALUES) {
+        value = array[index];
+      } else if (itemKind == ITERATOR_KIND_ENTRIES) {
+        value = [index, array[index]];
+      } else {
+        value = index;
+      }
+      done = false;
+    }
+  }
+
+  return %_CreateIterResultObject(value, done);
+}
+
+
+function ArrayEntries() {
+  return CreateArrayIterator(this, ITERATOR_KIND_ENTRIES);
+}
+
+
+function ArrayValues() {
+  return CreateArrayIterator(this, ITERATOR_KIND_VALUES);
+}
+
+
+function ArrayKeys() {
+  return CreateArrayIterator(this, ITERATOR_KIND_KEYS);
+}
+
+
+%FunctionSetPrototype(ArrayIterator, {__proto__: IteratorPrototype});
+%FunctionSetInstanceClassName(ArrayIterator, 'Array Iterator');
+
+utils.InstallFunctions(ArrayIterator.prototype, DONT_ENUM, [
+  'next', ArrayIteratorNext
+]);
+utils.SetFunctionName(ArrayIteratorIterator, iteratorSymbol);
+%AddNamedProperty(ArrayIterator.prototype, iteratorSymbol,
+                  ArrayIteratorIterator, DONT_ENUM);
+%AddNamedProperty(ArrayIterator.prototype, toStringTagSymbol,
+                  "Array Iterator", READ_ONLY | DONT_ENUM);
+
+utils.InstallFunctions(GlobalArray.prototype, DONT_ENUM, [
+  // No 'values' since it breaks webcompat: http://crbug.com/409858
+  'entries', ArrayEntries,
+  'keys', ArrayKeys
+]);
+
+// TODO(adam): Remove this call once 'values' is in the above
+// InstallFunctions block, as it'll be redundant.
+utils.SetFunctionName(ArrayValues, 'values');
+
+%AddNamedProperty(GlobalArray.prototype, iteratorSymbol, ArrayValues,
+                  DONT_ENUM);
+
+%AddNamedProperty(GlobalTypedArray.prototype,
+                  'entries', ArrayEntries, DONT_ENUM);
+%AddNamedProperty(GlobalTypedArray.prototype, 'values', ArrayValues, DONT_ENUM);
+%AddNamedProperty(GlobalTypedArray.prototype, 'keys', ArrayKeys, DONT_ENUM);
+%AddNamedProperty(GlobalTypedArray.prototype,
+                  iteratorSymbol, ArrayValues, DONT_ENUM);
+
+// -------------------------------------------------------------------
+// Exports
+
+utils.Export(function(to) {
+  to.ArrayValues = ArrayValues;
+});
+
+%InstallToContext(["array_values_iterator", ArrayValues]);
+
+})
diff --git a/src/js/array.js b/src/js/array.js
new file mode 100644
index 0000000..f9cf161
--- /dev/null
+++ b/src/js/array.js
@@ -0,0 +1,1983 @@
+// Copyright 2012 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.
+
+(function(global, utils, extrasUtils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var AddIndexedProperty;
+var FLAG_harmony_tolength;
+var FLAG_harmony_species;
+var GetIterator;
+var GetMethod;
+var GlobalArray = global.Array;
+var InternalArray = utils.InternalArray;
+var InternalPackedArray = utils.InternalPackedArray;
+var MakeTypeError;
+var MaxSimple;
+var MinSimple;
+var ObjectDefineProperty;
+var ObjectHasOwnProperty;
+var ObjectToString = utils.ImportNow("object_to_string");
+var ObserveBeginPerformSplice;
+var ObserveEndPerformSplice;
+var ObserveEnqueueSpliceRecord;
+var SameValueZero;
+var iteratorSymbol = utils.ImportNow("iterator_symbol");
+var unscopablesSymbol = utils.ImportNow("unscopables_symbol");
+
+utils.Import(function(from) {
+  AddIndexedProperty = from.AddIndexedProperty;
+  GetIterator = from.GetIterator;
+  GetMethod = from.GetMethod;
+  MakeTypeError = from.MakeTypeError;
+  MaxSimple = from.MaxSimple;
+  MinSimple = from.MinSimple;
+  ObjectDefineProperty = from.ObjectDefineProperty;
+  ObjectHasOwnProperty = from.ObjectHasOwnProperty;
+  ObserveBeginPerformSplice = from.ObserveBeginPerformSplice;
+  ObserveEndPerformSplice = from.ObserveEndPerformSplice;
+  ObserveEnqueueSpliceRecord = from.ObserveEnqueueSpliceRecord;
+  SameValueZero = from.SameValueZero;
+});
+
+utils.ImportFromExperimental(function(from) {
+  FLAG_harmony_tolength = from.FLAG_harmony_tolength;
+  FLAG_harmony_species = from.FLAG_harmony_species;
+});
+
+// -------------------------------------------------------------------
+
+
+function ArraySpeciesCreate(array, length) {
+  var constructor;
+  if (FLAG_harmony_species) {
+    constructor = %ArraySpeciesConstructor(array);
+  } else {
+    constructor = GlobalArray;
+  }
+  return new constructor(length);
+}
+
+
+function DefineIndexedProperty(array, i, value) {
+  if (FLAG_harmony_species) {
+    var result = ObjectDefineProperty(array, i, {
+      value: value, writable: true, configurable: true, enumerable: true
+    });
+    if (!result) throw MakeTypeError(kStrictCannotAssign, i);
+  } else {
+    AddIndexedProperty(array, i, value);
+  }
+}
+
+
+// Global list of arrays visited during toString, toLocaleString and
+// join invocations.
+var visited_arrays = new InternalArray();
+
+
+// Gets a sorted array of array keys.  Useful for operations on sparse
+// arrays.  Dupes have not been removed.
+function GetSortedArrayKeys(array, indices) {
+  var keys = new InternalArray();
+  if (IS_NUMBER(indices)) {
+    // It's an interval
+    var limit = indices;
+    for (var i = 0; i < limit; ++i) {
+      var e = array[i];
+      if (!IS_UNDEFINED(e) || i in array) {
+        keys.push(i);
+      }
+    }
+  } else {
+    var length = indices.length;
+    for (var k = 0; k < length; ++k) {
+      var key = indices[k];
+      if (!IS_UNDEFINED(key)) {
+        var e = array[key];
+        if (!IS_UNDEFINED(e) || key in array) {
+          keys.push(key);
+        }
+      }
+    }
+    keys.sort(function(a, b) { return a - b; });
+  }
+  return keys;
+}
+
+
+function SparseJoinWithSeparatorJS(array, len, convert, separator) {
+  var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
+  var totalLength = 0;
+  var elements = new InternalArray(keys.length * 2);
+  var previousKey = -1;
+  for (var i = 0; i < keys.length; i++) {
+    var key = keys[i];
+    if (key != previousKey) {  // keys may contain duplicates.
+      var e = array[key];
+      if (!IS_STRING(e)) e = convert(e);
+      elements[i * 2] = key;
+      elements[i * 2 + 1] = e;
+      previousKey = key;
+    }
+  }
+  return %SparseJoinWithSeparator(elements, len, separator);
+}
+
+
+// Optimized for sparse arrays if separator is ''.
+function SparseJoin(array, len, convert) {
+  var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
+  var last_key = -1;
+  var keys_length = keys.length;
+
+  var elements = new InternalArray(keys_length);
+  var elements_length = 0;
+
+  for (var i = 0; i < keys_length; i++) {
+    var key = keys[i];
+    if (key != last_key) {
+      var e = array[key];
+      if (!IS_STRING(e)) e = convert(e);
+      elements[elements_length++] = e;
+      last_key = key;
+    }
+  }
+  return %StringBuilderConcat(elements, elements_length, '');
+}
+
+
+function UseSparseVariant(array, length, is_array, touched) {
+  // Only use the sparse variant on arrays that are likely to be sparse and the
+  // number of elements touched in the operation is relatively small compared to
+  // the overall size of the array.
+  if (!is_array || length < 1000 || %IsObserved(array) ||
+      %HasComplexElements(array)) {
+    return false;
+  }
+  if (!%_IsSmi(length)) {
+    return true;
+  }
+  var elements_threshold = length >> 2;  // No more than 75% holes
+  var estimated_elements = %EstimateNumberOfElements(array);
+  return (estimated_elements < elements_threshold) &&
+    (touched > estimated_elements * 4);
+}
+
+
+function Join(array, length, separator, convert) {
+  if (length == 0) return '';
+
+  var is_array = IS_ARRAY(array);
+
+  if (is_array) {
+    // If the array is cyclic, return the empty string for already
+    // visited arrays.
+    if (!%PushIfAbsent(visited_arrays, array)) return '';
+  }
+
+  // Attempt to convert the elements.
+  try {
+    if (UseSparseVariant(array, length, is_array, length)) {
+      %NormalizeElements(array);
+      if (separator.length == 0) {
+        return SparseJoin(array, length, convert);
+      } else {
+        return SparseJoinWithSeparatorJS(array, length, convert, separator);
+      }
+    }
+
+    // Fast case for one-element arrays.
+    if (length == 1) {
+      var e = array[0];
+      if (IS_STRING(e)) return e;
+      return convert(e);
+    }
+
+    // Construct an array for the elements.
+    var elements = new InternalArray(length);
+
+    // We pull the empty separator check outside the loop for speed!
+    if (separator.length == 0) {
+      var elements_length = 0;
+      for (var i = 0; i < length; i++) {
+        var e = array[i];
+        if (!IS_STRING(e)) e = convert(e);
+        elements[elements_length++] = e;
+      }
+      elements.length = elements_length;
+      var result = %_FastOneByteArrayJoin(elements, '');
+      if (!IS_UNDEFINED(result)) return result;
+      return %StringBuilderConcat(elements, elements_length, '');
+    }
+    // Non-empty separator case.
+    // If the first element is a number then use the heuristic that the
+    // remaining elements are also likely to be numbers.
+    if (!IS_NUMBER(array[0])) {
+      for (var i = 0; i < length; i++) {
+        var e = array[i];
+        if (!IS_STRING(e)) e = convert(e);
+        elements[i] = e;
+      }
+    } else {
+      for (var i = 0; i < length; i++) {
+        var e = array[i];
+        if (IS_NUMBER(e)) {
+          e = %_NumberToString(e);
+        } else if (!IS_STRING(e)) {
+          e = convert(e);
+        }
+        elements[i] = e;
+      }
+    }
+    var result = %_FastOneByteArrayJoin(elements, separator);
+    if (!IS_UNDEFINED(result)) return result;
+
+    return %StringBuilderJoin(elements, length, separator);
+  } finally {
+    // Make sure to remove the last element of the visited array no
+    // matter what happens.
+    if (is_array) visited_arrays.length = visited_arrays.length - 1;
+  }
+}
+
+
+function ConvertToString(x) {
+  if (IS_NULL_OR_UNDEFINED(x)) {
+    return '';
+  } else {
+    return TO_STRING(x);
+  }
+}
+
+
+function ConvertToLocaleString(e) {
+  if (IS_NULL_OR_UNDEFINED(e)) {
+    return '';
+  } else {
+    return TO_STRING(e.toLocaleString());
+  }
+}
+
+
+// This function implements the optimized splice implementation that can use
+// special array operations to handle sparse arrays in a sensible fashion.
+function SparseSlice(array, start_i, del_count, len, deleted_elements) {
+  // Move deleted elements to a new array (the return value from splice).
+  var indices = %GetArrayKeys(array, start_i + del_count);
+  if (IS_NUMBER(indices)) {
+    var limit = indices;
+    for (var i = start_i; i < limit; ++i) {
+      var current = array[i];
+      if (!IS_UNDEFINED(current) || i in array) {
+        DefineIndexedProperty(deleted_elements, i - start_i, current);
+      }
+    }
+  } else {
+    var length = indices.length;
+    for (var k = 0; k < length; ++k) {
+      var key = indices[k];
+      if (!IS_UNDEFINED(key)) {
+        if (key >= start_i) {
+          var current = array[key];
+          if (!IS_UNDEFINED(current) || key in array) {
+            DefineIndexedProperty(deleted_elements, key - start_i, current);
+          }
+        }
+      }
+    }
+  }
+}
+
+
+// This function implements the optimized splice implementation that can use
+// special array operations to handle sparse arrays in a sensible fashion.
+function SparseMove(array, start_i, del_count, len, num_additional_args) {
+  // Bail out if no moving is necessary.
+  if (num_additional_args === del_count) return;
+  // Move data to new array.
+  var new_array = new InternalArray(
+      // Clamp array length to 2^32-1 to avoid early RangeError.
+      MinSimple(len - del_count + num_additional_args, 0xffffffff));
+  var big_indices;
+  var indices = %GetArrayKeys(array, len);
+  if (IS_NUMBER(indices)) {
+    var limit = indices;
+    for (var i = 0; i < start_i && i < limit; ++i) {
+      var current = array[i];
+      if (!IS_UNDEFINED(current) || i in array) {
+        new_array[i] = current;
+      }
+    }
+    for (var i = start_i + del_count; i < limit; ++i) {
+      var current = array[i];
+      if (!IS_UNDEFINED(current) || i in array) {
+        new_array[i - del_count + num_additional_args] = current;
+      }
+    }
+  } else {
+    var length = indices.length;
+    for (var k = 0; k < length; ++k) {
+      var key = indices[k];
+      if (!IS_UNDEFINED(key)) {
+        if (key < start_i) {
+          var current = array[key];
+          if (!IS_UNDEFINED(current) || key in array) {
+            new_array[key] = current;
+          }
+        } else if (key >= start_i + del_count) {
+          var current = array[key];
+          if (!IS_UNDEFINED(current) || key in array) {
+            var new_key = key - del_count + num_additional_args;
+            new_array[new_key] = current;
+            if (new_key > 0xfffffffe) {
+              big_indices = big_indices || new InternalArray();
+              big_indices.push(new_key);
+            }
+          }
+        }
+      }
+    }
+  }
+  // Move contents of new_array into this array
+  %MoveArrayContents(new_array, array);
+  // Add any moved values that aren't elements anymore.
+  if (!IS_UNDEFINED(big_indices)) {
+    var length = big_indices.length;
+    for (var i = 0; i < length; ++i) {
+      var key = big_indices[i];
+      array[key] = new_array[key];
+    }
+  }
+}
+
+
+// This is part of the old simple-minded splice.  We are using it either
+// because the receiver is not an array (so we have no choice) or because we
+// know we are not deleting or moving a lot of elements.
+function SimpleSlice(array, start_i, del_count, len, deleted_elements) {
+  var is_array = IS_ARRAY(array);
+  for (var i = 0; i < del_count; i++) {
+    var index = start_i + i;
+    if (HAS_INDEX(array, index, is_array)) {
+      var current = array[index];
+      DefineIndexedProperty(deleted_elements, i, current);
+    }
+  }
+}
+
+
+function SimpleMove(array, start_i, del_count, len, num_additional_args) {
+  var is_array = IS_ARRAY(array);
+  if (num_additional_args !== del_count) {
+    // Move the existing elements after the elements to be deleted
+    // to the right position in the resulting array.
+    if (num_additional_args > del_count) {
+      for (var i = len - del_count; i > start_i; i--) {
+        var from_index = i + del_count - 1;
+        var to_index = i + num_additional_args - 1;
+        if (HAS_INDEX(array, from_index, is_array)) {
+          array[to_index] = array[from_index];
+        } else {
+          delete array[to_index];
+        }
+      }
+    } else {
+      for (var i = start_i; i < len - del_count; i++) {
+        var from_index = i + del_count;
+        var to_index = i + num_additional_args;
+        if (HAS_INDEX(array, from_index, is_array)) {
+          array[to_index] = array[from_index];
+        } else {
+          delete array[to_index];
+        }
+      }
+      for (var i = len; i > len - del_count + num_additional_args; i--) {
+        delete array[i - 1];
+      }
+    }
+  }
+}
+
+
+// -------------------------------------------------------------------
+
+
+function ArrayToString() {
+  var array;
+  var func;
+  if (IS_ARRAY(this)) {
+    func = this.join;
+    if (func === ArrayJoin) {
+      return Join(this, this.length, ',', ConvertToString);
+    }
+    array = this;
+  } else {
+    array = TO_OBJECT(this);
+    func = array.join;
+  }
+  if (!IS_CALLABLE(func)) {
+    return %_Call(ObjectToString, array);
+  }
+  return %_Call(func, array);
+}
+
+
+function InnerArrayToLocaleString(array, length) {
+  var len = TO_LENGTH_OR_UINT32(length);
+  if (len === 0) return "";
+  return Join(array, len, ',', ConvertToLocaleString);
+}
+
+
+function ArrayToLocaleString() {
+  var array = TO_OBJECT(this);
+  var arrayLen = array.length;
+  return InnerArrayToLocaleString(array, arrayLen);
+}
+
+
+function InnerArrayJoin(separator, array, length) {
+  if (IS_UNDEFINED(separator)) {
+    separator = ',';
+  } else {
+    separator = TO_STRING(separator);
+  }
+
+  var result = %_FastOneByteArrayJoin(array, separator);
+  if (!IS_UNDEFINED(result)) return result;
+
+  // Fast case for one-element arrays.
+  if (length === 1) {
+    var e = array[0];
+    if (IS_NULL_OR_UNDEFINED(e)) return '';
+    return TO_STRING(e);
+  }
+
+  return Join(array, length, separator, ConvertToString);
+}
+
+
+function ArrayJoin(separator) {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.join");
+
+  var array = TO_OBJECT(this);
+  var length = TO_LENGTH_OR_UINT32(array.length);
+
+  return InnerArrayJoin(separator, array, length);
+}
+
+
+function ObservedArrayPop(n) {
+  n--;
+  var value = this[n];
+
+  try {
+    ObserveBeginPerformSplice(this);
+    delete this[n];
+    this.length = n;
+  } finally {
+    ObserveEndPerformSplice(this);
+    ObserveEnqueueSpliceRecord(this, n, [value], 0);
+  }
+
+  return value;
+}
+
+
+// Removes the last element from the array and returns it. See
+// ECMA-262, section 15.4.4.6.
+function ArrayPop() {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.pop");
+
+  var array = TO_OBJECT(this);
+  var n = TO_LENGTH_OR_UINT32(array.length);
+  if (n == 0) {
+    array.length = n;
+    return;
+  }
+
+  if (%IsObserved(array))
+    return ObservedArrayPop.call(array, n);
+
+  n--;
+  var value = array[n];
+  %DeleteProperty_Strict(array, n);
+  array.length = n;
+  return value;
+}
+
+
+function ObservedArrayPush() {
+  var n = TO_LENGTH_OR_UINT32(this.length);
+  var m = %_ArgumentsLength();
+
+  try {
+    ObserveBeginPerformSplice(this);
+    for (var i = 0; i < m; i++) {
+      this[i+n] = %_Arguments(i);
+    }
+    var new_length = n + m;
+    this.length = new_length;
+  } finally {
+    ObserveEndPerformSplice(this);
+    ObserveEnqueueSpliceRecord(this, n, [], m);
+  }
+
+  return new_length;
+}
+
+
+// Appends the arguments to the end of the array and returns the new
+// length of the array. See ECMA-262, section 15.4.4.7.
+function ArrayPush() {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.push");
+
+  if (%IsObserved(this))
+    return ObservedArrayPush.apply(this, arguments);
+
+  var array = TO_OBJECT(this);
+  var n = TO_LENGTH_OR_UINT32(array.length);
+  var m = %_ArgumentsLength();
+
+  // It appears that there is no enforced, absolute limit on the number of
+  // arguments, but it would surely blow the stack to use 2**30 or more.
+  // To avoid integer overflow, do the comparison to the max safe integer
+  // after subtracting 2**30 from both sides. (2**31 would seem like a
+  // natural value, but it is negative in JS, and 2**32 is 1.)
+  if (m > (1 << 30) || (n - (1 << 30)) + m > kMaxSafeInteger - (1 << 30)) {
+    throw MakeTypeError(kPushPastSafeLength, m, n);
+  }
+
+  for (var i = 0; i < m; i++) {
+    array[i+n] = %_Arguments(i);
+  }
+
+  var new_length = n + m;
+  array.length = new_length;
+  return new_length;
+}
+
+
+// For implementing reverse() on large, sparse arrays.
+function SparseReverse(array, len) {
+  var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
+  var high_counter = keys.length - 1;
+  var low_counter = 0;
+  while (low_counter <= high_counter) {
+    var i = keys[low_counter];
+    var j = keys[high_counter];
+
+    var j_complement = len - j - 1;
+    var low, high;
+
+    if (j_complement <= i) {
+      high = j;
+      while (keys[--high_counter] == j) { }
+      low = j_complement;
+    }
+    if (j_complement >= i) {
+      low = i;
+      while (keys[++low_counter] == i) { }
+      high = len - i - 1;
+    }
+
+    var current_i = array[low];
+    if (!IS_UNDEFINED(current_i) || low in array) {
+      var current_j = array[high];
+      if (!IS_UNDEFINED(current_j) || high in array) {
+        array[low] = current_j;
+        array[high] = current_i;
+      } else {
+        array[high] = current_i;
+        delete array[low];
+      }
+    } else {
+      var current_j = array[high];
+      if (!IS_UNDEFINED(current_j) || high in array) {
+        array[low] = current_j;
+        delete array[high];
+      }
+    }
+  }
+}
+
+function PackedArrayReverse(array, len) {
+  var j = len - 1;
+  for (var i = 0; i < j; i++, j--) {
+    var current_i = array[i];
+    var current_j = array[j];
+    array[i] = current_j;
+    array[j] = current_i;
+  }
+  return array;
+}
+
+
+function GenericArrayReverse(array, len) {
+  var j = len - 1;
+  for (var i = 0; i < j; i++, j--) {
+    if (i in array) {
+      var current_i = array[i];
+      if (j in array) {
+        var current_j = array[j];
+        array[i] = current_j;
+        array[j] = current_i;
+      } else {
+        array[j] = current_i;
+        delete array[i];
+      }
+    } else {
+      if (j in array) {
+        var current_j = array[j];
+        array[i] = current_j;
+        delete array[j];
+      }
+    }
+  }
+  return array;
+}
+
+
+function ArrayReverse() {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reverse");
+
+  var array = TO_OBJECT(this);
+  var len = TO_LENGTH_OR_UINT32(array.length);
+  var isArray = IS_ARRAY(array);
+
+  if (UseSparseVariant(array, len, isArray, len)) {
+    %NormalizeElements(array);
+    SparseReverse(array, len);
+    return array;
+  } else if (isArray && %_HasFastPackedElements(array)) {
+    return PackedArrayReverse(array, len);
+  } else {
+    return GenericArrayReverse(array, len);
+  }
+}
+
+
+function ObservedArrayShift(len) {
+  var first = this[0];
+
+  try {
+    ObserveBeginPerformSplice(this);
+    SimpleMove(this, 0, 1, len, 0);
+    this.length = len - 1;
+  } finally {
+    ObserveEndPerformSplice(this);
+    ObserveEnqueueSpliceRecord(this, 0, [first], 0);
+  }
+
+  return first;
+}
+
+
+function ArrayShift() {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.shift");
+
+  var array = TO_OBJECT(this);
+  var len = TO_LENGTH_OR_UINT32(array.length);
+
+  if (len === 0) {
+    array.length = 0;
+    return;
+  }
+
+  if (%object_is_sealed(array)) throw MakeTypeError(kArrayFunctionsOnSealed);
+
+  if (%IsObserved(array))
+    return ObservedArrayShift.call(array, len);
+
+  var first = array[0];
+
+  if (UseSparseVariant(array, len, IS_ARRAY(array), len)) {
+    SparseMove(array, 0, 1, len, 0);
+  } else {
+    SimpleMove(array, 0, 1, len, 0);
+  }
+
+  array.length = len - 1;
+
+  return first;
+}
+
+
+function ObservedArrayUnshift() {
+  var len = TO_LENGTH_OR_UINT32(this.length);
+  var num_arguments = %_ArgumentsLength();
+
+  try {
+    ObserveBeginPerformSplice(this);
+    SimpleMove(this, 0, 0, len, num_arguments);
+    for (var i = 0; i < num_arguments; i++) {
+      this[i] = %_Arguments(i);
+    }
+    var new_length = len + num_arguments;
+    this.length = new_length;
+  } finally {
+    ObserveEndPerformSplice(this);
+    ObserveEnqueueSpliceRecord(this, 0, [], num_arguments);
+  }
+
+  return new_length;
+}
+
+
+function ArrayUnshift(arg1) {  // length == 1
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.unshift");
+
+  if (%IsObserved(this))
+    return ObservedArrayUnshift.apply(this, arguments);
+
+  var array = TO_OBJECT(this);
+  var len = TO_LENGTH_OR_UINT32(array.length);
+  var num_arguments = %_ArgumentsLength();
+
+  if (len > 0 && UseSparseVariant(array, len, IS_ARRAY(array), len) &&
+      !%object_is_sealed(array)) {
+    SparseMove(array, 0, 0, len, num_arguments);
+  } else {
+    SimpleMove(array, 0, 0, len, num_arguments);
+  }
+
+  for (var i = 0; i < num_arguments; i++) {
+    array[i] = %_Arguments(i);
+  }
+
+  var new_length = len + num_arguments;
+  array.length = new_length;
+  return new_length;
+}
+
+
+function ArraySlice(start, end) {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.slice");
+
+  var array = TO_OBJECT(this);
+  var len = TO_LENGTH_OR_UINT32(array.length);
+  var start_i = TO_INTEGER(start);
+  var end_i = len;
+
+  if (!IS_UNDEFINED(end)) end_i = TO_INTEGER(end);
+
+  if (start_i < 0) {
+    start_i += len;
+    if (start_i < 0) start_i = 0;
+  } else {
+    if (start_i > len) start_i = len;
+  }
+
+  if (end_i < 0) {
+    end_i += len;
+    if (end_i < 0) end_i = 0;
+  } else {
+    if (end_i > len) end_i = len;
+  }
+
+  var result = ArraySpeciesCreate(array, MaxSimple(end_i - start_i, 0));
+
+  if (end_i < start_i) return result;
+
+  if (UseSparseVariant(array, len, IS_ARRAY(array), end_i - start_i)) {
+    %NormalizeElements(array);
+    %NormalizeElements(result);
+    SparseSlice(array, start_i, end_i - start_i, len, result);
+  } else {
+    SimpleSlice(array, start_i, end_i - start_i, len, result);
+  }
+
+  result.length = end_i - start_i;
+
+  return result;
+}
+
+
+function ComputeSpliceStartIndex(start_i, len) {
+  if (start_i < 0) {
+    start_i += len;
+    return start_i < 0 ? 0 : start_i;
+  }
+
+  return start_i > len ? len : start_i;
+}
+
+
+function ComputeSpliceDeleteCount(delete_count, num_arguments, len, start_i) {
+  // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
+  // given as a request to delete all the elements from the start.
+  // And it differs from the case of undefined delete count.
+  // This does not follow ECMA-262, but we do the same for
+  // compatibility.
+  var del_count = 0;
+  if (num_arguments == 1)
+    return len - start_i;
+
+  del_count = TO_INTEGER(delete_count);
+  if (del_count < 0)
+    return 0;
+
+  if (del_count > len - start_i)
+    return len - start_i;
+
+  return del_count;
+}
+
+
+function ObservedArraySplice(start, delete_count) {
+  var num_arguments = %_ArgumentsLength();
+  var len = TO_LENGTH_OR_UINT32(this.length);
+  var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len);
+  var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len,
+                                           start_i);
+  var deleted_elements = [];
+  deleted_elements.length = del_count;
+  var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0;
+
+  try {
+    ObserveBeginPerformSplice(this);
+
+    SimpleSlice(this, start_i, del_count, len, deleted_elements);
+    SimpleMove(this, start_i, del_count, len, num_elements_to_add);
+
+    // Insert the arguments into the resulting array in
+    // place of the deleted elements.
+    var i = start_i;
+    var arguments_index = 2;
+    var arguments_length = %_ArgumentsLength();
+    while (arguments_index < arguments_length) {
+      this[i++] = %_Arguments(arguments_index++);
+    }
+    this.length = len - del_count + num_elements_to_add;
+
+  } finally {
+    ObserveEndPerformSplice(this);
+    if (deleted_elements.length || num_elements_to_add) {
+      ObserveEnqueueSpliceRecord(this,
+                                 start_i,
+                                 deleted_elements.slice(),
+                                 num_elements_to_add);
+    }
+  }
+
+  // Return the deleted elements.
+  return deleted_elements;
+}
+
+
+function ArraySplice(start, delete_count) {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.splice");
+
+  if (%IsObserved(this))
+    return ObservedArraySplice.apply(this, arguments);
+
+  var num_arguments = %_ArgumentsLength();
+  var array = TO_OBJECT(this);
+  var len = TO_LENGTH_OR_UINT32(array.length);
+  var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len);
+  var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len,
+                                           start_i);
+  var deleted_elements = ArraySpeciesCreate(array, del_count);
+  deleted_elements.length = del_count;
+  var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0;
+
+  if (del_count != num_elements_to_add && %object_is_sealed(array)) {
+    throw MakeTypeError(kArrayFunctionsOnSealed);
+  } else if (del_count > 0 && %object_is_frozen(array)) {
+    throw MakeTypeError(kArrayFunctionsOnFrozen);
+  }
+
+  var changed_elements = del_count;
+  if (num_elements_to_add != del_count) {
+    // If the slice needs to do a actually move elements after the insertion
+    // point, then include those in the estimate of changed elements.
+    changed_elements += len - start_i - del_count;
+  }
+  if (UseSparseVariant(array, len, IS_ARRAY(array), changed_elements)) {
+    %NormalizeElements(array);
+    %NormalizeElements(deleted_elements);
+    SparseSlice(array, start_i, del_count, len, deleted_elements);
+    SparseMove(array, start_i, del_count, len, num_elements_to_add);
+  } else {
+    SimpleSlice(array, start_i, del_count, len, deleted_elements);
+    SimpleMove(array, start_i, del_count, len, num_elements_to_add);
+  }
+
+  // Insert the arguments into the resulting array in
+  // place of the deleted elements.
+  var i = start_i;
+  var arguments_index = 2;
+  var arguments_length = %_ArgumentsLength();
+  while (arguments_index < arguments_length) {
+    array[i++] = %_Arguments(arguments_index++);
+  }
+  array.length = len - del_count + num_elements_to_add;
+
+  // Return the deleted elements.
+  return deleted_elements;
+}
+
+
+function InnerArraySort(array, length, comparefn) {
+  // In-place QuickSort algorithm.
+  // For short (length <= 22) arrays, insertion sort is used for efficiency.
+
+  if (!IS_CALLABLE(comparefn)) {
+    comparefn = function (x, y) {
+      if (x === y) return 0;
+      if (%_IsSmi(x) && %_IsSmi(y)) {
+        return %SmiLexicographicCompare(x, y);
+      }
+      x = TO_STRING(x);
+      y = TO_STRING(y);
+      if (x == y) return 0;
+      else return x < y ? -1 : 1;
+    };
+  }
+  var InsertionSort = function InsertionSort(a, from, to) {
+    for (var i = from + 1; i < to; i++) {
+      var element = a[i];
+      for (var j = i - 1; j >= from; j--) {
+        var tmp = a[j];
+        var order = comparefn(tmp, element);
+        if (order > 0) {
+          a[j + 1] = tmp;
+        } else {
+          break;
+        }
+      }
+      a[j + 1] = element;
+    }
+  };
+
+  var GetThirdIndex = function(a, from, to) {
+    var t_array = new InternalArray();
+    // Use both 'from' and 'to' to determine the pivot candidates.
+    var increment = 200 + ((to - from) & 15);
+    var j = 0;
+    from += 1;
+    to -= 1;
+    for (var i = from; i < to; i += increment) {
+      t_array[j] = [i, a[i]];
+      j++;
+    }
+    t_array.sort(function(a, b) {
+      return comparefn(a[1], b[1]);
+    });
+    var third_index = t_array[t_array.length >> 1][0];
+    return third_index;
+  }
+
+  var QuickSort = function QuickSort(a, from, to) {
+    var third_index = 0;
+    while (true) {
+      // Insertion sort is faster for short arrays.
+      if (to - from <= 10) {
+        InsertionSort(a, from, to);
+        return;
+      }
+      if (to - from > 1000) {
+        third_index = GetThirdIndex(a, from, to);
+      } else {
+        third_index = from + ((to - from) >> 1);
+      }
+      // Find a pivot as the median of first, last and middle element.
+      var v0 = a[from];
+      var v1 = a[to - 1];
+      var v2 = a[third_index];
+      var c01 = comparefn(v0, v1);
+      if (c01 > 0) {
+        // v1 < v0, so swap them.
+        var tmp = v0;
+        v0 = v1;
+        v1 = tmp;
+      } // v0 <= v1.
+      var c02 = comparefn(v0, v2);
+      if (c02 >= 0) {
+        // v2 <= v0 <= v1.
+        var tmp = v0;
+        v0 = v2;
+        v2 = v1;
+        v1 = tmp;
+      } else {
+        // v0 <= v1 && v0 < v2
+        var c12 = comparefn(v1, v2);
+        if (c12 > 0) {
+          // v0 <= v2 < v1
+          var tmp = v1;
+          v1 = v2;
+          v2 = tmp;
+        }
+      }
+      // v0 <= v1 <= v2
+      a[from] = v0;
+      a[to - 1] = v2;
+      var pivot = v1;
+      var low_end = from + 1;   // Upper bound of elements lower than pivot.
+      var high_start = to - 1;  // Lower bound of elements greater than pivot.
+      a[third_index] = a[low_end];
+      a[low_end] = pivot;
+
+      // From low_end to i are elements equal to pivot.
+      // From i to high_start are elements that haven't been compared yet.
+      partition: for (var i = low_end + 1; i < high_start; i++) {
+        var element = a[i];
+        var order = comparefn(element, pivot);
+        if (order < 0) {
+          a[i] = a[low_end];
+          a[low_end] = element;
+          low_end++;
+        } else if (order > 0) {
+          do {
+            high_start--;
+            if (high_start == i) break partition;
+            var top_elem = a[high_start];
+            order = comparefn(top_elem, pivot);
+          } while (order > 0);
+          a[i] = a[high_start];
+          a[high_start] = element;
+          if (order < 0) {
+            element = a[i];
+            a[i] = a[low_end];
+            a[low_end] = element;
+            low_end++;
+          }
+        }
+      }
+      if (to - high_start < low_end - from) {
+        QuickSort(a, high_start, to);
+        to = low_end;
+      } else {
+        QuickSort(a, from, low_end);
+        from = high_start;
+      }
+    }
+  };
+
+  // Copy elements in the range 0..length from obj's prototype chain
+  // to obj itself, if obj has holes. Return one more than the maximal index
+  // of a prototype property.
+  var CopyFromPrototype = function CopyFromPrototype(obj, length) {
+    var max = 0;
+    for (var proto = %_GetPrototype(obj); proto; proto = %_GetPrototype(proto)) {
+      var indices = %GetArrayKeys(proto, length);
+      if (IS_NUMBER(indices)) {
+        // It's an interval.
+        var proto_length = indices;
+        for (var i = 0; i < proto_length; i++) {
+          if (!HAS_OWN_PROPERTY(obj, i) && HAS_OWN_PROPERTY(proto, i)) {
+            obj[i] = proto[i];
+            if (i >= max) { max = i + 1; }
+          }
+        }
+      } else {
+        for (var i = 0; i < indices.length; i++) {
+          var index = indices[i];
+          if (!IS_UNDEFINED(index) && !HAS_OWN_PROPERTY(obj, index)
+              && HAS_OWN_PROPERTY(proto, index)) {
+            obj[index] = proto[index];
+            if (index >= max) { max = index + 1; }
+          }
+        }
+      }
+    }
+    return max;
+  };
+
+  // Set a value of "undefined" on all indices in the range from..to
+  // where a prototype of obj has an element. I.e., shadow all prototype
+  // elements in that range.
+  var ShadowPrototypeElements = function(obj, from, to) {
+    for (var proto = %_GetPrototype(obj); proto; proto = %_GetPrototype(proto)) {
+      var indices = %GetArrayKeys(proto, to);
+      if (IS_NUMBER(indices)) {
+        // It's an interval.
+        var proto_length = indices;
+        for (var i = from; i < proto_length; i++) {
+          if (HAS_OWN_PROPERTY(proto, i)) {
+            obj[i] = UNDEFINED;
+          }
+        }
+      } else {
+        for (var i = 0; i < indices.length; i++) {
+          var index = indices[i];
+          if (!IS_UNDEFINED(index) && from <= index &&
+              HAS_OWN_PROPERTY(proto, index)) {
+            obj[index] = UNDEFINED;
+          }
+        }
+      }
+    }
+  };
+
+  var SafeRemoveArrayHoles = function SafeRemoveArrayHoles(obj) {
+    // Copy defined elements from the end to fill in all holes and undefineds
+    // in the beginning of the array.  Write undefineds and holes at the end
+    // after loop is finished.
+    var first_undefined = 0;
+    var last_defined = length - 1;
+    var num_holes = 0;
+    while (first_undefined < last_defined) {
+      // Find first undefined element.
+      while (first_undefined < last_defined &&
+             !IS_UNDEFINED(obj[first_undefined])) {
+        first_undefined++;
+      }
+      // Maintain the invariant num_holes = the number of holes in the original
+      // array with indices <= first_undefined or > last_defined.
+      if (!HAS_OWN_PROPERTY(obj, first_undefined)) {
+        num_holes++;
+      }
+
+      // Find last defined element.
+      while (first_undefined < last_defined &&
+             IS_UNDEFINED(obj[last_defined])) {
+        if (!HAS_OWN_PROPERTY(obj, last_defined)) {
+          num_holes++;
+        }
+        last_defined--;
+      }
+      if (first_undefined < last_defined) {
+        // Fill in hole or undefined.
+        obj[first_undefined] = obj[last_defined];
+        obj[last_defined] = UNDEFINED;
+      }
+    }
+    // If there were any undefineds in the entire array, first_undefined
+    // points to one past the last defined element.  Make this true if
+    // there were no undefineds, as well, so that first_undefined == number
+    // of defined elements.
+    if (!IS_UNDEFINED(obj[first_undefined])) first_undefined++;
+    // Fill in the undefineds and the holes.  There may be a hole where
+    // an undefined should be and vice versa.
+    var i;
+    for (i = first_undefined; i < length - num_holes; i++) {
+      obj[i] = UNDEFINED;
+    }
+    for (i = length - num_holes; i < length; i++) {
+      // For compatability with Webkit, do not expose elements in the prototype.
+      if (i in %_GetPrototype(obj)) {
+        obj[i] = UNDEFINED;
+      } else {
+        delete obj[i];
+      }
+    }
+
+    // Return the number of defined elements.
+    return first_undefined;
+  };
+
+  if (length < 2) return array;
+
+  var is_array = IS_ARRAY(array);
+  var max_prototype_element;
+  if (!is_array) {
+    // For compatibility with JSC, we also sort elements inherited from
+    // the prototype chain on non-Array objects.
+    // We do this by copying them to this object and sorting only
+    // own elements. This is not very efficient, but sorting with
+    // inherited elements happens very, very rarely, if at all.
+    // The specification allows "implementation dependent" behavior
+    // if an element on the prototype chain has an element that
+    // might interact with sorting.
+    max_prototype_element = CopyFromPrototype(array, length);
+  }
+
+  // %RemoveArrayHoles returns -1 if fast removal is not supported.
+  var num_non_undefined = %RemoveArrayHoles(array, length);
+
+  if (num_non_undefined == -1) {
+    // The array is observed, or there were indexed accessors in the array.
+    // Move array holes and undefineds to the end using a Javascript function
+    // that is safe in the presence of accessors and is observable.
+    num_non_undefined = SafeRemoveArrayHoles(array);
+  }
+
+  QuickSort(array, 0, num_non_undefined);
+
+  if (!is_array && (num_non_undefined + 1 < max_prototype_element)) {
+    // For compatibility with JSC, we shadow any elements in the prototype
+    // chain that has become exposed by sort moving a hole to its position.
+    ShadowPrototypeElements(array, num_non_undefined, max_prototype_element);
+  }
+
+  return array;
+}
+
+
+function ArraySort(comparefn) {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.sort");
+
+  var array = TO_OBJECT(this);
+  var length = TO_LENGTH_OR_UINT32(array.length);
+  return InnerArraySort(array, length, comparefn);
+}
+
+
+// The following functions cannot be made efficient on sparse arrays while
+// preserving the semantics, since the calls to the receiver function can add
+// or delete elements from the array.
+function InnerArrayFilter(f, receiver, array, length, result) {
+  var result_length = 0;
+  var is_array = IS_ARRAY(array);
+  for (var i = 0; i < length; i++) {
+    if (HAS_INDEX(array, i, is_array)) {
+      var element = array[i];
+      if (%_Call(f, receiver, element, i, array)) {
+        DefineIndexedProperty(result, result_length, element);
+        result_length++;
+      }
+    }
+  }
+  return result;
+}
+
+
+
+function ArrayFilter(f, receiver) {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.filter");
+
+  // Pull out the length so that modifications to the length in the
+  // loop will not affect the looping and side effects are visible.
+  var array = TO_OBJECT(this);
+  var length = TO_LENGTH_OR_UINT32(array.length);
+  if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
+  var result = ArraySpeciesCreate(array, 0);
+  return InnerArrayFilter(f, receiver, array, length, result);
+}
+
+
+function InnerArrayForEach(f, receiver, array, length) {
+  if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
+
+  var is_array = IS_ARRAY(array);
+  for (var i = 0; i < length; i++) {
+    if (HAS_INDEX(array, i, is_array)) {
+      var element = array[i];
+      %_Call(f, receiver, element, i, array);
+    }
+  }
+}
+
+
+function ArrayForEach(f, receiver) {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.forEach");
+
+  // Pull out the length so that modifications to the length in the
+  // loop will not affect the looping and side effects are visible.
+  var array = TO_OBJECT(this);
+  var length = TO_LENGTH_OR_UINT32(array.length);
+  InnerArrayForEach(f, receiver, array, length);
+}
+
+
+function InnerArraySome(f, receiver, array, length) {
+  if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
+
+  var is_array = IS_ARRAY(array);
+  for (var i = 0; i < length; i++) {
+    if (HAS_INDEX(array, i, is_array)) {
+      var element = array[i];
+      if (%_Call(f, receiver, element, i, array)) return true;
+    }
+  }
+  return false;
+}
+
+
+// Executes the function once for each element present in the
+// array until it finds one where callback returns true.
+function ArraySome(f, receiver) {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.some");
+
+  // Pull out the length so that modifications to the length in the
+  // loop will not affect the looping and side effects are visible.
+  var array = TO_OBJECT(this);
+  var length = TO_LENGTH_OR_UINT32(array.length);
+  return InnerArraySome(f, receiver, array, length);
+}
+
+
+function InnerArrayEvery(f, receiver, array, length) {
+  if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
+
+  var is_array = IS_ARRAY(array);
+  for (var i = 0; i < length; i++) {
+    if (HAS_INDEX(array, i, is_array)) {
+      var element = array[i];
+      if (!%_Call(f, receiver, element, i, array)) return false;
+    }
+  }
+  return true;
+}
+
+function ArrayEvery(f, receiver) {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.every");
+
+  // Pull out the length so that modifications to the length in the
+  // loop will not affect the looping and side effects are visible.
+  var array = TO_OBJECT(this);
+  var length = TO_LENGTH_OR_UINT32(array.length);
+  return InnerArrayEvery(f, receiver, array, length);
+}
+
+
+function ArrayMap(f, receiver) {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.map");
+
+  // Pull out the length so that modifications to the length in the
+  // loop will not affect the looping and side effects are visible.
+  var array = TO_OBJECT(this);
+  var length = TO_LENGTH_OR_UINT32(array.length);
+  if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
+  var result = ArraySpeciesCreate(array, length);
+  var is_array = IS_ARRAY(array);
+  for (var i = 0; i < length; i++) {
+    if (HAS_INDEX(array, i, is_array)) {
+      var element = array[i];
+      DefineIndexedProperty(result, i, %_Call(f, receiver, element, i, array));
+    }
+  }
+  return result;
+}
+
+
+// For .indexOf, we don't need to pass in the number of arguments
+// at the callsite since ToInteger(undefined) == 0; however, for
+// .lastIndexOf, we need to pass it, since the behavior for passing
+// undefined is 0 but for not including the argument is length-1.
+function InnerArrayIndexOf(array, element, index, length) {
+  if (length == 0) return -1;
+  if (IS_UNDEFINED(index)) {
+    index = 0;
+  } else {
+    index = TO_INTEGER(index);
+    // If index is negative, index from the end of the array.
+    if (index < 0) {
+      index = length + index;
+      // If index is still negative, search the entire array.
+      if (index < 0) index = 0;
+    }
+  }
+  var min = index;
+  var max = length;
+  if (UseSparseVariant(array, length, IS_ARRAY(array), max - min)) {
+    %NormalizeElements(array);
+    var indices = %GetArrayKeys(array, length);
+    if (IS_NUMBER(indices)) {
+      // It's an interval.
+      max = indices;  // Capped by length already.
+      // Fall through to loop below.
+    } else {
+      if (indices.length == 0) return -1;
+      // Get all the keys in sorted order.
+      var sortedKeys = GetSortedArrayKeys(array, indices);
+      var n = sortedKeys.length;
+      var i = 0;
+      while (i < n && sortedKeys[i] < index) i++;
+      while (i < n) {
+        var key = sortedKeys[i];
+        if (!IS_UNDEFINED(key) && array[key] === element) return key;
+        i++;
+      }
+      return -1;
+    }
+  }
+  // Lookup through the array.
+  if (!IS_UNDEFINED(element)) {
+    for (var i = min; i < max; i++) {
+      if (array[i] === element) return i;
+    }
+    return -1;
+  }
+  // Lookup through the array.
+  for (var i = min; i < max; i++) {
+    if (IS_UNDEFINED(array[i]) && i in array) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+
+function ArrayIndexOf(element, index) {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.indexOf");
+
+  var length = TO_LENGTH_OR_UINT32(this.length);
+  return InnerArrayIndexOf(this, element, index, length);
+}
+
+
+function InnerArrayLastIndexOf(array, element, index, length, argumentsLength) {
+  if (length == 0) return -1;
+  if (argumentsLength < 2) {
+    index = length - 1;
+  } else {
+    index = TO_INTEGER(index);
+    // If index is negative, index from end of the array.
+    if (index < 0) index += length;
+    // If index is still negative, do not search the array.
+    if (index < 0) return -1;
+    else if (index >= length) index = length - 1;
+  }
+  var min = 0;
+  var max = index;
+  if (UseSparseVariant(array, length, IS_ARRAY(array), index)) {
+    %NormalizeElements(array);
+    var indices = %GetArrayKeys(array, index + 1);
+    if (IS_NUMBER(indices)) {
+      // It's an interval.
+      max = indices;  // Capped by index already.
+      // Fall through to loop below.
+    } else {
+      if (indices.length == 0) return -1;
+      // Get all the keys in sorted order.
+      var sortedKeys = GetSortedArrayKeys(array, indices);
+      var i = sortedKeys.length - 1;
+      while (i >= 0) {
+        var key = sortedKeys[i];
+        if (!IS_UNDEFINED(key) && array[key] === element) return key;
+        i--;
+      }
+      return -1;
+    }
+  }
+  // Lookup through the array.
+  if (!IS_UNDEFINED(element)) {
+    for (var i = max; i >= min; i--) {
+      if (array[i] === element) return i;
+    }
+    return -1;
+  }
+  for (var i = max; i >= min; i--) {
+    if (IS_UNDEFINED(array[i]) && i in array) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+
+function ArrayLastIndexOf(element, index) {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.lastIndexOf");
+
+  var length = TO_LENGTH_OR_UINT32(this.length);
+  return InnerArrayLastIndexOf(this, element, index, length,
+                        %_ArgumentsLength());
+}
+
+
+function InnerArrayReduce(callback, current, array, length, argumentsLength) {
+  if (!IS_CALLABLE(callback)) {
+    throw MakeTypeError(kCalledNonCallable, callback);
+  }
+
+  var is_array = IS_ARRAY(array);
+  var i = 0;
+  find_initial: if (argumentsLength < 2) {
+    for (; i < length; i++) {
+      if (HAS_INDEX(array, i, is_array)) {
+        current = array[i++];
+        break find_initial;
+      }
+    }
+    throw MakeTypeError(kReduceNoInitial);
+  }
+
+  for (; i < length; i++) {
+    if (HAS_INDEX(array, i, is_array)) {
+      var element = array[i];
+      current = callback(current, element, i, array);
+    }
+  }
+  return current;
+}
+
+
+function ArrayReduce(callback, current) {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduce");
+
+  // Pull out the length so that modifications to the length in the
+  // loop will not affect the looping and side effects are visible.
+  var array = TO_OBJECT(this);
+  var length = TO_LENGTH_OR_UINT32(array.length);
+  return InnerArrayReduce(callback, current, array, length,
+                          %_ArgumentsLength());
+}
+
+
+function InnerArrayReduceRight(callback, current, array, length,
+                               argumentsLength) {
+  if (!IS_CALLABLE(callback)) {
+    throw MakeTypeError(kCalledNonCallable, callback);
+  }
+
+  var is_array = IS_ARRAY(array);
+  var i = length - 1;
+  find_initial: if (argumentsLength < 2) {
+    for (; i >= 0; i--) {
+      if (HAS_INDEX(array, i, is_array)) {
+        current = array[i--];
+        break find_initial;
+      }
+    }
+    throw MakeTypeError(kReduceNoInitial);
+  }
+
+  for (; i >= 0; i--) {
+    if (HAS_INDEX(array, i, is_array)) {
+      var element = array[i];
+      current = callback(current, element, i, array);
+    }
+  }
+  return current;
+}
+
+
+function ArrayReduceRight(callback, current) {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduceRight");
+
+  // Pull out the length so that side effects are visible before the
+  // callback function is checked.
+  var array = TO_OBJECT(this);
+  var length = TO_LENGTH_OR_UINT32(array.length);
+  return InnerArrayReduceRight(callback, current, array, length,
+                               %_ArgumentsLength());
+}
+
+
+function InnerArrayCopyWithin(target, start, end, array, length) {
+  target = TO_INTEGER(target);
+  var to;
+  if (target < 0) {
+    to = MaxSimple(length + target, 0);
+  } else {
+    to = MinSimple(target, length);
+  }
+
+  start = TO_INTEGER(start);
+  var from;
+  if (start < 0) {
+    from = MaxSimple(length + start, 0);
+  } else {
+    from = MinSimple(start, length);
+  }
+
+  end = IS_UNDEFINED(end) ? length : TO_INTEGER(end);
+  var final;
+  if (end < 0) {
+    final = MaxSimple(length + end, 0);
+  } else {
+    final = MinSimple(end, length);
+  }
+
+  var count = MinSimple(final - from, length - to);
+  var direction = 1;
+  if (from < to && to < (from + count)) {
+    direction = -1;
+    from = from + count - 1;
+    to = to + count - 1;
+  }
+
+  while (count > 0) {
+    if (from in array) {
+      array[to] = array[from];
+    } else {
+      delete array[to];
+    }
+    from = from + direction;
+    to = to + direction;
+    count--;
+  }
+
+  return array;
+}
+
+
+// ES6 draft 03-17-15, section 22.1.3.3
+function ArrayCopyWithin(target, start, end) {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.copyWithin");
+
+  var array = TO_OBJECT(this);
+  var length = TO_LENGTH(array.length);
+
+  return InnerArrayCopyWithin(target, start, end, array, length);
+}
+
+
+function InnerArrayFind(predicate, thisArg, array, length) {
+  if (!IS_CALLABLE(predicate)) {
+    throw MakeTypeError(kCalledNonCallable, predicate);
+  }
+
+  for (var i = 0; i < length; i++) {
+    var element = array[i];
+    if (%_Call(predicate, thisArg, element, i, array)) {
+      return element;
+    }
+  }
+
+  return;
+}
+
+
+// ES6 draft 07-15-13, section 15.4.3.23
+function ArrayFind(predicate, thisArg) {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.find");
+
+  var array = TO_OBJECT(this);
+  var length = TO_INTEGER(array.length);
+
+  return InnerArrayFind(predicate, thisArg, array, length);
+}
+
+
+function InnerArrayFindIndex(predicate, thisArg, array, length) {
+  if (!IS_CALLABLE(predicate)) {
+    throw MakeTypeError(kCalledNonCallable, predicate);
+  }
+
+  for (var i = 0; i < length; i++) {
+    var element = array[i];
+    if (%_Call(predicate, thisArg, element, i, array)) {
+      return i;
+    }
+  }
+
+  return -1;
+}
+
+
+// ES6 draft 07-15-13, section 15.4.3.24
+function ArrayFindIndex(predicate, thisArg) {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.findIndex");
+
+  var array = TO_OBJECT(this);
+  var length = TO_INTEGER(array.length);
+
+  return InnerArrayFindIndex(predicate, thisArg, array, length);
+}
+
+
+// ES6, draft 04-05-14, section 22.1.3.6
+function InnerArrayFill(value, start, end, array, length) {
+  var i = IS_UNDEFINED(start) ? 0 : TO_INTEGER(start);
+  var end = IS_UNDEFINED(end) ? length : TO_INTEGER(end);
+
+  if (i < 0) {
+    i += length;
+    if (i < 0) i = 0;
+  } else {
+    if (i > length) i = length;
+  }
+
+  if (end < 0) {
+    end += length;
+    if (end < 0) end = 0;
+  } else {
+    if (end > length) end = length;
+  }
+
+  if ((end - i) > 0 && %object_is_frozen(array)) {
+    throw MakeTypeError(kArrayFunctionsOnFrozen);
+  }
+
+  for (; i < end; i++)
+    array[i] = value;
+  return array;
+}
+
+
+// ES6, draft 04-05-14, section 22.1.3.6
+function ArrayFill(value, start, end) {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.fill");
+
+  var array = TO_OBJECT(this);
+  var length = TO_LENGTH_OR_UINT32(array.length);
+
+  return InnerArrayFill(value, start, end, array, length);
+}
+
+
+function InnerArrayIncludes(searchElement, fromIndex, array, length) {
+  if (length === 0) {
+    return false;
+  }
+
+  var n = TO_INTEGER(fromIndex);
+
+  var k;
+  if (n >= 0) {
+    k = n;
+  } else {
+    k = length + n;
+    if (k < 0) {
+      k = 0;
+    }
+  }
+
+  while (k < length) {
+    var elementK = array[k];
+    if (SameValueZero(searchElement, elementK)) {
+      return true;
+    }
+
+    ++k;
+  }
+
+  return false;
+}
+
+
+// ES2016 draft, section 22.1.3.11
+function ArrayIncludes(searchElement, fromIndex) {
+  CHECK_OBJECT_COERCIBLE(this, "Array.prototype.includes");
+
+  var array = TO_OBJECT(this);
+  var length = TO_LENGTH(array.length);
+
+  return InnerArrayIncludes(searchElement, fromIndex, array, length);
+}
+
+
+function AddArrayElement(constructor, array, i, value) {
+  if (constructor === GlobalArray) {
+    AddIndexedProperty(array, i, value);
+  } else {
+    ObjectDefineProperty(array, i, {
+      value: value, writable: true, configurable: true, enumerable: true
+    });
+  }
+}
+
+
+// ES6, draft 10-14-14, section 22.1.2.1
+function ArrayFrom(arrayLike, mapfn, receiver) {
+  var items = TO_OBJECT(arrayLike);
+  var mapping = !IS_UNDEFINED(mapfn);
+
+  if (mapping) {
+    if (!IS_CALLABLE(mapfn)) {
+      throw MakeTypeError(kCalledNonCallable, mapfn);
+    }
+  }
+
+  var iterable = GetMethod(items, iteratorSymbol);
+  var k;
+  var result;
+  var mappedValue;
+  var nextValue;
+
+  if (!IS_UNDEFINED(iterable)) {
+    result = %IsConstructor(this) ? new this() : [];
+
+    var iterator = GetIterator(items, iterable);
+
+    k = 0;
+    while (true) {
+      var next = iterator.next();
+
+      if (!IS_RECEIVER(next)) {
+        throw MakeTypeError(kIteratorResultNotAnObject, next);
+      }
+
+      if (next.done) {
+        result.length = k;
+        return result;
+      }
+
+      nextValue = next.value;
+      if (mapping) {
+        mappedValue = %_Call(mapfn, receiver, nextValue, k);
+      } else {
+        mappedValue = nextValue;
+      }
+      AddArrayElement(this, result, k, mappedValue);
+      k++;
+    }
+  } else {
+    var len = TO_LENGTH(items.length);
+    result = %IsConstructor(this) ? new this(len) : new GlobalArray(len);
+
+    for (k = 0; k < len; ++k) {
+      nextValue = items[k];
+      if (mapping) {
+        mappedValue = %_Call(mapfn, receiver, nextValue, k);
+      } else {
+        mappedValue = nextValue;
+      }
+      AddArrayElement(this, result, k, mappedValue);
+    }
+
+    result.length = k;
+    return result;
+  }
+}
+
+
+// ES6, draft 05-22-14, section 22.1.2.3
+function ArrayOf() {
+  var length = %_ArgumentsLength();
+  var constructor = this;
+  // TODO: Implement IsConstructor (ES6 section 7.2.5)
+  var array = %IsConstructor(constructor) ? new constructor(length) : [];
+  for (var i = 0; i < length; i++) {
+    AddArrayElement(constructor, array, i, %_Arguments(i));
+  }
+  array.length = length;
+  return array;
+}
+
+// -------------------------------------------------------------------
+
+// Set up non-enumerable constructor property on the Array.prototype
+// object.
+%AddNamedProperty(GlobalArray.prototype, "constructor", GlobalArray,
+                  DONT_ENUM);
+
+// Set up unscopable properties on the Array.prototype object.
+var unscopables = {
+  __proto__: null,
+  copyWithin: true,
+  entries: true,
+  fill: true,
+  find: true,
+  findIndex: true,
+  keys: true,
+};
+
+%AddNamedProperty(GlobalArray.prototype, unscopablesSymbol, unscopables,
+                  DONT_ENUM | READ_ONLY);
+
+%FunctionSetLength(ArrayFrom, 1);
+
+// Set up non-enumerable functions on the Array object.
+utils.InstallFunctions(GlobalArray, DONT_ENUM, [
+  "from", ArrayFrom,
+  "of", ArrayOf
+]);
+
+var specialFunctions = %SpecialArrayFunctions();
+
+var getFunction = function(name, jsBuiltin, len) {
+  var f = jsBuiltin;
+  if (specialFunctions.hasOwnProperty(name)) {
+    f = specialFunctions[name];
+  }
+  if (!IS_UNDEFINED(len)) {
+    %FunctionSetLength(f, len);
+  }
+  return f;
+};
+
+// Set up non-enumerable functions of the Array.prototype object and
+// set their names.
+// Manipulate the length of some of the functions to meet
+// expectations set by ECMA-262 or Mozilla.
+utils.InstallFunctions(GlobalArray.prototype, DONT_ENUM, [
+  "toString", getFunction("toString", ArrayToString),
+  "toLocaleString", getFunction("toLocaleString", ArrayToLocaleString),
+  "join", getFunction("join", ArrayJoin),
+  "pop", getFunction("pop", ArrayPop),
+  "push", getFunction("push", ArrayPush, 1),
+  "reverse", getFunction("reverse", ArrayReverse),
+  "shift", getFunction("shift", ArrayShift),
+  "unshift", getFunction("unshift", ArrayUnshift, 1),
+  "slice", getFunction("slice", ArraySlice, 2),
+  "splice", getFunction("splice", ArraySplice, 2),
+  "sort", getFunction("sort", ArraySort),
+  "filter", getFunction("filter", ArrayFilter, 1),
+  "forEach", getFunction("forEach", ArrayForEach, 1),
+  "some", getFunction("some", ArraySome, 1),
+  "every", getFunction("every", ArrayEvery, 1),
+  "map", getFunction("map", ArrayMap, 1),
+  "indexOf", getFunction("indexOf", ArrayIndexOf, 1),
+  "lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1),
+  "reduce", getFunction("reduce", ArrayReduce, 1),
+  "reduceRight", getFunction("reduceRight", ArrayReduceRight, 1),
+  "copyWithin", getFunction("copyWithin", ArrayCopyWithin, 2),
+  "find", getFunction("find", ArrayFind, 1),
+  "findIndex", getFunction("findIndex", ArrayFindIndex, 1),
+  "fill", getFunction("fill", ArrayFill, 1),
+  "includes", getFunction("includes", ArrayIncludes, 1),
+]);
+
+%FinishArrayPrototypeSetup(GlobalArray.prototype);
+
+// The internal Array prototype doesn't need to be fancy, since it's never
+// exposed to user code.
+// Adding only the functions that are actually used.
+utils.SetUpLockedPrototype(InternalArray, GlobalArray(), [
+  "indexOf", getFunction("indexOf", ArrayIndexOf),
+  "join", getFunction("join", ArrayJoin),
+  "pop", getFunction("pop", ArrayPop),
+  "push", getFunction("push", ArrayPush),
+  "shift", getFunction("shift", ArrayShift),
+  "sort", getFunction("sort", ArraySort),
+  "splice", getFunction("splice", ArraySplice)
+]);
+
+utils.SetUpLockedPrototype(InternalPackedArray, GlobalArray(), [
+  "join", getFunction("join", ArrayJoin),
+  "pop", getFunction("pop", ArrayPop),
+  "push", getFunction("push", ArrayPush),
+  "shift", getFunction("shift", ArrayShift)
+]);
+
+// V8 extras get a separate copy of InternalPackedArray. We give them the basic
+// manipulation methods.
+utils.SetUpLockedPrototype(extrasUtils.InternalPackedArray, GlobalArray(), [
+  "push", getFunction("push", ArrayPush),
+  "pop", getFunction("pop", ArrayPop),
+  "shift", getFunction("shift", ArrayShift),
+  "unshift", getFunction("unshift", ArrayUnshift),
+  "splice", getFunction("splice", ArraySplice),
+  "slice", getFunction("slice", ArraySlice)
+]);
+
+// -------------------------------------------------------------------
+// Exports
+
+utils.Export(function(to) {
+  to.ArrayFrom = ArrayFrom;
+  to.ArrayIndexOf = ArrayIndexOf;
+  to.ArrayJoin = ArrayJoin;
+  to.ArrayPush = ArrayPush;
+  to.ArrayToString = ArrayToString;
+  to.InnerArrayCopyWithin = InnerArrayCopyWithin;
+  to.InnerArrayEvery = InnerArrayEvery;
+  to.InnerArrayFill = InnerArrayFill;
+  to.InnerArrayFilter = InnerArrayFilter;
+  to.InnerArrayFind = InnerArrayFind;
+  to.InnerArrayFindIndex = InnerArrayFindIndex;
+  to.InnerArrayForEach = InnerArrayForEach;
+  to.InnerArrayIncludes = InnerArrayIncludes;
+  to.InnerArrayIndexOf = InnerArrayIndexOf;
+  to.InnerArrayJoin = InnerArrayJoin;
+  to.InnerArrayLastIndexOf = InnerArrayLastIndexOf;
+  to.InnerArrayReduce = InnerArrayReduce;
+  to.InnerArrayReduceRight = InnerArrayReduceRight;
+  to.InnerArraySome = InnerArraySome;
+  to.InnerArraySort = InnerArraySort;
+  to.InnerArrayToLocaleString = InnerArrayToLocaleString;
+  to.PackedArrayReverse = PackedArrayReverse;
+});
+
+%InstallToContext([
+  "array_pop", ArrayPop,
+  "array_push", ArrayPush,
+  "array_shift", ArrayShift,
+  "array_splice", ArraySplice,
+  "array_slice", ArraySlice,
+  "array_unshift", ArrayUnshift,
+]);
+
+});
diff --git a/src/js/arraybuffer.js b/src/js/arraybuffer.js
new file mode 100644
index 0000000..f0273c7
--- /dev/null
+++ b/src/js/arraybuffer.js
@@ -0,0 +1,92 @@
+// Copyright 2013 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.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalArrayBuffer = global.ArrayBuffer;
+var MakeTypeError;
+var MaxSimple;
+var MinSimple;
+var SpeciesConstructor;
+
+utils.Import(function(from) {
+  MakeTypeError = from.MakeTypeError;
+  MaxSimple = from.MaxSimple;
+  MinSimple = from.MinSimple;
+  SpeciesConstructor = from.SpeciesConstructor;
+});
+
+// -------------------------------------------------------------------
+
+function ArrayBufferGetByteLen() {
+  if (!IS_ARRAYBUFFER(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'ArrayBuffer.prototype.byteLength', this);
+  }
+  return %_ArrayBufferGetByteLength(this);
+}
+
+// ES6 Draft 15.13.5.5.3
+function ArrayBufferSlice(start, end) {
+  if (!IS_ARRAYBUFFER(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'ArrayBuffer.prototype.slice', this);
+  }
+
+  var relativeStart = TO_INTEGER(start);
+  if (!IS_UNDEFINED(end)) {
+    end = TO_INTEGER(end);
+  }
+  var first;
+  var byte_length = %_ArrayBufferGetByteLength(this);
+  if (relativeStart < 0) {
+    first = MaxSimple(byte_length + relativeStart, 0);
+  } else {
+    first = MinSimple(relativeStart, byte_length);
+  }
+  var relativeEnd = IS_UNDEFINED(end) ? byte_length : end;
+  var fin;
+  if (relativeEnd < 0) {
+    fin = MaxSimple(byte_length + relativeEnd, 0);
+  } else {
+    fin = MinSimple(relativeEnd, byte_length);
+  }
+
+  if (fin < first) {
+    fin = first;
+  }
+  var newLen = fin - first;
+  var constructor = SpeciesConstructor(this, GlobalArrayBuffer, true);
+  var result = new constructor(newLen);
+  if (!IS_ARRAYBUFFER(result)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'ArrayBuffer.prototype.slice', result);
+  }
+  // TODO(littledan): Check for a detached ArrayBuffer
+  if (result === this) {
+    throw MakeTypeError(kArrayBufferSpeciesThis);
+  }
+  if (%_ArrayBufferGetByteLength(result) < newLen) {
+    throw MakeTypeError(kArrayBufferTooShort);
+  }
+
+  %ArrayBufferSliceImpl(this, result, first, newLen);
+  return result;
+}
+
+utils.InstallGetter(GlobalArrayBuffer.prototype, "byteLength",
+                    ArrayBufferGetByteLen);
+
+utils.InstallFunctions(GlobalArrayBuffer.prototype, DONT_ENUM, [
+  "slice", ArrayBufferSlice
+]);
+
+})
diff --git a/src/js/collection-iterator.js b/src/js/collection-iterator.js
new file mode 100644
index 0000000..621d726
--- /dev/null
+++ b/src/js/collection-iterator.js
@@ -0,0 +1,183 @@
+// 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.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalMap = global.Map;
+var GlobalSet = global.Set;
+var iteratorSymbol = utils.ImportNow("iterator_symbol");
+var MakeTypeError;
+var MapIterator = utils.ImportNow("MapIterator");
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+var SetIterator = utils.ImportNow("SetIterator");
+
+utils.Import(function(from) {
+  MakeTypeError = from.MakeTypeError;
+});
+
+// -------------------------------------------------------------------
+
+function SetIteratorConstructor(set, kind) {
+  %SetIteratorInitialize(this, set, kind);
+}
+
+
+function SetIteratorNextJS() {
+  if (!IS_SET_ITERATOR(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'Set Iterator.prototype.next', this);
+  }
+
+  var value_array = [UNDEFINED, UNDEFINED];
+  var result = %_CreateIterResultObject(value_array, false);
+  switch (%SetIteratorNext(this, value_array)) {
+    case 0:
+      result.value = UNDEFINED;
+      result.done = true;
+      break;
+    case ITERATOR_KIND_VALUES:
+      result.value = value_array[0];
+      break;
+    case ITERATOR_KIND_ENTRIES:
+      value_array[1] = value_array[0];
+      break;
+  }
+
+  return result;
+}
+
+
+function SetEntries() {
+  if (!IS_SET(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'Set.prototype.entries', this);
+  }
+  return new SetIterator(this, ITERATOR_KIND_ENTRIES);
+}
+
+
+function SetValues() {
+  if (!IS_SET(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'Set.prototype.values', this);
+  }
+  return new SetIterator(this, ITERATOR_KIND_VALUES);
+}
+
+// -------------------------------------------------------------------
+
+%SetCode(SetIterator, SetIteratorConstructor);
+%FunctionSetInstanceClassName(SetIterator, 'Set Iterator');
+utils.InstallFunctions(SetIterator.prototype, DONT_ENUM, [
+  'next', SetIteratorNextJS
+]);
+
+%AddNamedProperty(SetIterator.prototype, toStringTagSymbol,
+    "Set Iterator", READ_ONLY | DONT_ENUM);
+
+utils.InstallFunctions(GlobalSet.prototype, DONT_ENUM, [
+  'entries', SetEntries,
+  'keys', SetValues,
+  'values', SetValues
+]);
+
+%AddNamedProperty(GlobalSet.prototype, iteratorSymbol, SetValues, DONT_ENUM);
+
+// -------------------------------------------------------------------
+
+function MapIteratorConstructor(map, kind) {
+  %MapIteratorInitialize(this, map, kind);
+}
+
+
+function MapIteratorNextJS() {
+  if (!IS_MAP_ITERATOR(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'Map Iterator.prototype.next', this);
+  }
+
+  var value_array = [UNDEFINED, UNDEFINED];
+  var result = %_CreateIterResultObject(value_array, false);
+  switch (%MapIteratorNext(this, value_array)) {
+    case 0:
+      result.value = UNDEFINED;
+      result.done = true;
+      break;
+    case ITERATOR_KIND_KEYS:
+      result.value = value_array[0];
+      break;
+    case ITERATOR_KIND_VALUES:
+      result.value = value_array[1];
+      break;
+    // ITERATOR_KIND_ENTRIES does not need any processing.
+  }
+
+  return result;
+}
+
+
+function MapEntries() {
+  if (!IS_MAP(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'Map.prototype.entries', this);
+  }
+  return new MapIterator(this, ITERATOR_KIND_ENTRIES);
+}
+
+
+function MapKeys() {
+  if (!IS_MAP(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'Map.prototype.keys', this);
+  }
+  return new MapIterator(this, ITERATOR_KIND_KEYS);
+}
+
+
+function MapValues() {
+  if (!IS_MAP(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'Map.prototype.values', this);
+  }
+  return new MapIterator(this, ITERATOR_KIND_VALUES);
+}
+
+// -------------------------------------------------------------------
+
+%SetCode(MapIterator, MapIteratorConstructor);
+%FunctionSetInstanceClassName(MapIterator, 'Map Iterator');
+utils.InstallFunctions(MapIterator.prototype, DONT_ENUM, [
+  'next', MapIteratorNextJS
+]);
+
+%AddNamedProperty(MapIterator.prototype, toStringTagSymbol,
+    "Map Iterator", READ_ONLY | DONT_ENUM);
+
+
+utils.InstallFunctions(GlobalMap.prototype, DONT_ENUM, [
+  'entries', MapEntries,
+  'keys', MapKeys,
+  'values', MapValues
+]);
+
+%AddNamedProperty(GlobalMap.prototype, iteratorSymbol, MapEntries, DONT_ENUM);
+
+// -------------------------------------------------------------------
+// Exports
+
+utils.Export(function(to) {
+  to.MapEntries = MapEntries;
+  to.MapIteratorNext = MapIteratorNextJS;
+  to.SetIteratorNext = SetIteratorNextJS;
+  to.SetValues = SetValues;
+});
+
+})
diff --git a/src/js/collection.js b/src/js/collection.js
new file mode 100644
index 0000000..0d7195d
--- /dev/null
+++ b/src/js/collection.js
@@ -0,0 +1,478 @@
+// Copyright 2012 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.
+
+(function(global, utils) {
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalMap = global.Map;
+var GlobalObject = global.Object;
+var GlobalSet = global.Set;
+var hashCodeSymbol = utils.ImportNow("hash_code_symbol");
+var IntRandom;
+var MakeTypeError;
+var MapIterator;
+var NumberIsNaN;
+var SetIterator;
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+
+utils.Import(function(from) {
+  IntRandom = from.IntRandom;
+  MakeTypeError = from.MakeTypeError;
+  MapIterator = from.MapIterator;
+  NumberIsNaN = from.NumberIsNaN;
+  SetIterator = from.SetIterator;
+});
+
+// -------------------------------------------------------------------
+
+function HashToEntry(table, hash, numBuckets) {
+  var bucket = ORDERED_HASH_TABLE_HASH_TO_BUCKET(hash, numBuckets);
+  return ORDERED_HASH_TABLE_BUCKET_AT(table, bucket);
+}
+%SetForceInlineFlag(HashToEntry);
+
+
+function SetFindEntry(table, numBuckets, key, hash) {
+  var entry = HashToEntry(table, hash, numBuckets);
+  if (entry === NOT_FOUND) return entry;
+  var candidate = ORDERED_HASH_SET_KEY_AT(table, entry, numBuckets);
+  if (key === candidate) return entry;
+  var keyIsNaN = NumberIsNaN(key);
+  while (true) {
+    if (keyIsNaN && NumberIsNaN(candidate)) {
+      return entry;
+    }
+    entry = ORDERED_HASH_SET_CHAIN_AT(table, entry, numBuckets);
+    if (entry === NOT_FOUND) return entry;
+    candidate = ORDERED_HASH_SET_KEY_AT(table, entry, numBuckets);
+    if (key === candidate) return entry;
+  }
+  return NOT_FOUND;
+}
+%SetForceInlineFlag(SetFindEntry);
+
+
+function MapFindEntry(table, numBuckets, key, hash) {
+  var entry = HashToEntry(table, hash, numBuckets);
+  if (entry === NOT_FOUND) return entry;
+  var candidate = ORDERED_HASH_MAP_KEY_AT(table, entry, numBuckets);
+  if (key === candidate) return entry;
+  var keyIsNaN = NumberIsNaN(key);
+  while (true) {
+    if (keyIsNaN && NumberIsNaN(candidate)) {
+      return entry;
+    }
+    entry = ORDERED_HASH_MAP_CHAIN_AT(table, entry, numBuckets);
+    if (entry === NOT_FOUND) return entry;
+    candidate = ORDERED_HASH_MAP_KEY_AT(table, entry, numBuckets);
+    if (key === candidate) return entry;
+  }
+  return NOT_FOUND;
+}
+%SetForceInlineFlag(MapFindEntry);
+
+
+function ComputeIntegerHash(key, seed) {
+  var hash = key;
+  hash = hash ^ seed;
+  hash = ~hash + (hash << 15);  // hash = (hash << 15) - hash - 1;
+  hash = hash ^ (hash >>> 12);
+  hash = hash + (hash << 2);
+  hash = hash ^ (hash >>> 4);
+  hash = (hash * 2057) | 0;  // hash = (hash + (hash << 3)) + (hash << 11);
+  hash = hash ^ (hash >>> 16);
+  return hash & 0x3fffffff;
+}
+%SetForceInlineFlag(ComputeIntegerHash);
+
+function GetExistingHash(key) {
+  if (%_IsSmi(key)) {
+    return ComputeIntegerHash(key, 0);
+  }
+  if (IS_STRING(key)) {
+    var field = %_StringGetRawHashField(key);
+    if ((field & 1 /* Name::kHashNotComputedMask */) === 0) {
+      return field >>> 2 /* Name::kHashShift */;
+    }
+  } else if (IS_RECEIVER(key) && !IS_PROXY(key) && !IS_GLOBAL(key)) {
+    var hash = GET_PRIVATE(key, hashCodeSymbol);
+    return hash;
+  }
+  return %GenericHash(key);
+}
+%SetForceInlineFlag(GetExistingHash);
+
+
+function GetHash(key) {
+  var hash = GetExistingHash(key);
+  if (IS_UNDEFINED(hash)) {
+    hash = IntRandom() | 0;
+    if (hash === 0) hash = 1;
+    SET_PRIVATE(key, hashCodeSymbol, hash);
+  }
+  return hash;
+}
+%SetForceInlineFlag(GetHash);
+
+
+// -------------------------------------------------------------------
+// Harmony Set
+
+function SetConstructor(iterable) {
+  if (IS_UNDEFINED(new.target)) {
+    throw MakeTypeError(kConstructorNotFunction, "Set");
+  }
+
+  %_SetInitialize(this);
+
+  if (!IS_NULL_OR_UNDEFINED(iterable)) {
+    var adder = this.add;
+    if (!IS_CALLABLE(adder)) {
+      throw MakeTypeError(kPropertyNotFunction, adder, 'add', this);
+    }
+
+    for (var value of iterable) {
+      %_Call(adder, this, value);
+    }
+  }
+}
+
+
+function SetAdd(key) {
+  if (!IS_SET(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver, 'Set.prototype.add', this);
+  }
+  // Normalize -0 to +0 as required by the spec.
+  // Even though we use SameValueZero as the comparison for the keys we don't
+  // want to ever store -0 as the key since the key is directly exposed when
+  // doing iteration.
+  if (key === 0) {
+    key = 0;
+  }
+  var table = %_JSCollectionGetTable(this);
+  var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
+  var hash = GetHash(key);
+  if (SetFindEntry(table, numBuckets, key, hash) !== NOT_FOUND) return this;
+
+  var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
+  var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table);
+  var capacity = numBuckets << 1;
+  if ((nof + nod) >= capacity) {
+    // Need to grow, bail out to runtime.
+    %SetGrow(this);
+    // Re-load state from the grown backing store.
+    table = %_JSCollectionGetTable(this);
+    numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
+    nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
+    nod = ORDERED_HASH_TABLE_DELETED_COUNT(table);
+  }
+  var entry = nof + nod;
+  var index = ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets);
+  var bucket = ORDERED_HASH_TABLE_HASH_TO_BUCKET(hash, numBuckets);
+  var chainEntry = ORDERED_HASH_TABLE_BUCKET_AT(table, bucket);
+  ORDERED_HASH_TABLE_SET_BUCKET_AT(table, bucket, entry);
+  ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof + 1);
+  FIXED_ARRAY_SET(table, index, key);
+  FIXED_ARRAY_SET_SMI(table, index + 1, chainEntry);
+  return this;
+}
+
+
+function SetHas(key) {
+  if (!IS_SET(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver, 'Set.prototype.has', this);
+  }
+  var table = %_JSCollectionGetTable(this);
+  var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
+  var hash = GetExistingHash(key);
+  if (IS_UNDEFINED(hash)) return false;
+  return SetFindEntry(table, numBuckets, key, hash) !== NOT_FOUND;
+}
+
+
+function SetDelete(key) {
+  if (!IS_SET(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'Set.prototype.delete', this);
+  }
+  var table = %_JSCollectionGetTable(this);
+  var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
+  var hash = GetExistingHash(key);
+  if (IS_UNDEFINED(hash)) return false;
+  var entry = SetFindEntry(table, numBuckets, key, hash);
+  if (entry === NOT_FOUND) return false;
+
+  var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table) - 1;
+  var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table) + 1;
+  var index = ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets);
+  FIXED_ARRAY_SET(table, index, %_TheHole());
+  ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof);
+  ORDERED_HASH_TABLE_SET_DELETED_COUNT(table, nod);
+  if (nof < (numBuckets >>> 1)) %SetShrink(this);
+  return true;
+}
+
+
+function SetGetSize() {
+  if (!IS_SET(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'Set.prototype.size', this);
+  }
+  var table = %_JSCollectionGetTable(this);
+  return ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
+}
+
+
+function SetClearJS() {
+  if (!IS_SET(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'Set.prototype.clear', this);
+  }
+  %_SetClear(this);
+}
+
+
+function SetForEach(f, receiver) {
+  if (!IS_SET(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'Set.prototype.forEach', this);
+  }
+
+  if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
+
+  var iterator = new SetIterator(this, ITERATOR_KIND_VALUES);
+  var key;
+  var value_array = [UNDEFINED];
+  while (%SetIteratorNext(iterator, value_array)) {
+    key = value_array[0];
+    %_Call(f, receiver, key, key, this);
+  }
+}
+
+// -------------------------------------------------------------------
+
+%SetCode(GlobalSet, SetConstructor);
+%FunctionSetLength(GlobalSet, 0);
+%FunctionSetPrototype(GlobalSet, new GlobalObject());
+%AddNamedProperty(GlobalSet.prototype, "constructor", GlobalSet, DONT_ENUM);
+%AddNamedProperty(GlobalSet.prototype, toStringTagSymbol, "Set",
+                  DONT_ENUM | READ_ONLY);
+
+%FunctionSetLength(SetForEach, 1);
+
+// Set up the non-enumerable functions on the Set prototype object.
+utils.InstallGetter(GlobalSet.prototype, "size", SetGetSize);
+utils.InstallFunctions(GlobalSet.prototype, DONT_ENUM, [
+  "add", SetAdd,
+  "has", SetHas,
+  "delete", SetDelete,
+  "clear", SetClearJS,
+  "forEach", SetForEach
+]);
+
+
+// -------------------------------------------------------------------
+// Harmony Map
+
+function MapConstructor(iterable) {
+  if (IS_UNDEFINED(new.target)) {
+    throw MakeTypeError(kConstructorNotFunction, "Map");
+  }
+
+  %_MapInitialize(this);
+
+  if (!IS_NULL_OR_UNDEFINED(iterable)) {
+    var adder = this.set;
+    if (!IS_CALLABLE(adder)) {
+      throw MakeTypeError(kPropertyNotFunction, adder, 'set', this);
+    }
+
+    for (var nextItem of iterable) {
+      if (!IS_RECEIVER(nextItem)) {
+        throw MakeTypeError(kIteratorValueNotAnObject, nextItem);
+      }
+      %_Call(adder, this, nextItem[0], nextItem[1]);
+    }
+  }
+}
+
+
+function MapGet(key) {
+  if (!IS_MAP(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'Map.prototype.get', this);
+  }
+  var table = %_JSCollectionGetTable(this);
+  var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
+  var hash = GetExistingHash(key);
+  if (IS_UNDEFINED(hash)) return UNDEFINED;
+  var entry = MapFindEntry(table, numBuckets, key, hash);
+  if (entry === NOT_FOUND) return UNDEFINED;
+  return ORDERED_HASH_MAP_VALUE_AT(table, entry, numBuckets);
+}
+
+
+function MapSet(key, value) {
+  if (!IS_MAP(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'Map.prototype.set', this);
+  }
+  // Normalize -0 to +0 as required by the spec.
+  // Even though we use SameValueZero as the comparison for the keys we don't
+  // want to ever store -0 as the key since the key is directly exposed when
+  // doing iteration.
+  if (key === 0) {
+    key = 0;
+  }
+
+  var table = %_JSCollectionGetTable(this);
+  var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
+  var hash = GetHash(key);
+  var entry = MapFindEntry(table, numBuckets, key, hash);
+  if (entry !== NOT_FOUND) {
+    var existingIndex = ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets);
+    FIXED_ARRAY_SET(table, existingIndex + 1, value);
+    return this;
+  }
+
+  var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
+  var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table);
+  var capacity = numBuckets << 1;
+  if ((nof + nod) >= capacity) {
+    // Need to grow, bail out to runtime.
+    %MapGrow(this);
+    // Re-load state from the grown backing store.
+    table = %_JSCollectionGetTable(this);
+    numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
+    nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
+    nod = ORDERED_HASH_TABLE_DELETED_COUNT(table);
+  }
+  entry = nof + nod;
+  var index = ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets);
+  var bucket = ORDERED_HASH_TABLE_HASH_TO_BUCKET(hash, numBuckets);
+  var chainEntry = ORDERED_HASH_TABLE_BUCKET_AT(table, bucket);
+  ORDERED_HASH_TABLE_SET_BUCKET_AT(table, bucket, entry);
+  ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof + 1);
+  FIXED_ARRAY_SET(table, index, key);
+  FIXED_ARRAY_SET(table, index + 1, value);
+  FIXED_ARRAY_SET(table, index + 2, chainEntry);
+  return this;
+}
+
+
+function MapHas(key) {
+  if (!IS_MAP(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'Map.prototype.has', this);
+  }
+  var table = %_JSCollectionGetTable(this);
+  var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
+  var hash = GetHash(key);
+  return MapFindEntry(table, numBuckets, key, hash) !== NOT_FOUND;
+}
+
+
+function MapDelete(key) {
+  if (!IS_MAP(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'Map.prototype.delete', this);
+  }
+  var table = %_JSCollectionGetTable(this);
+  var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
+  var hash = GetHash(key);
+  var entry = MapFindEntry(table, numBuckets, key, hash);
+  if (entry === NOT_FOUND) return false;
+
+  var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table) - 1;
+  var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table) + 1;
+  var index = ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets);
+  FIXED_ARRAY_SET(table, index, %_TheHole());
+  FIXED_ARRAY_SET(table, index + 1, %_TheHole());
+  ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof);
+  ORDERED_HASH_TABLE_SET_DELETED_COUNT(table, nod);
+  if (nof < (numBuckets >>> 1)) %MapShrink(this);
+  return true;
+}
+
+
+function MapGetSize() {
+  if (!IS_MAP(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'Map.prototype.size', this);
+  }
+  var table = %_JSCollectionGetTable(this);
+  return ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
+}
+
+
+function MapClearJS() {
+  if (!IS_MAP(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'Map.prototype.clear', this);
+  }
+  %_MapClear(this);
+}
+
+
+function MapForEach(f, receiver) {
+  if (!IS_MAP(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'Map.prototype.forEach', this);
+  }
+
+  if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
+
+  var iterator = new MapIterator(this, ITERATOR_KIND_ENTRIES);
+  var value_array = [UNDEFINED, UNDEFINED];
+  while (%MapIteratorNext(iterator, value_array)) {
+    %_Call(f, receiver, value_array[1], value_array[0], this);
+  }
+}
+
+// -------------------------------------------------------------------
+
+%SetCode(GlobalMap, MapConstructor);
+%FunctionSetLength(GlobalMap, 0);
+%FunctionSetPrototype(GlobalMap, new GlobalObject());
+%AddNamedProperty(GlobalMap.prototype, "constructor", GlobalMap, DONT_ENUM);
+%AddNamedProperty(
+    GlobalMap.prototype, toStringTagSymbol, "Map", DONT_ENUM | READ_ONLY);
+
+%FunctionSetLength(MapForEach, 1);
+
+// Set up the non-enumerable functions on the Map prototype object.
+utils.InstallGetter(GlobalMap.prototype, "size", MapGetSize);
+utils.InstallFunctions(GlobalMap.prototype, DONT_ENUM, [
+  "get", MapGet,
+  "set", MapSet,
+  "has", MapHas,
+  "delete", MapDelete,
+  "clear", MapClearJS,
+  "forEach", MapForEach
+]);
+
+// -----------------------------------------------------------------------
+// Exports
+
+%InstallToContext([
+  "map_get", MapGet,
+  "map_set", MapSet,
+  "map_has", MapHas,
+  "map_delete", MapDelete,
+  "set_add", SetAdd,
+  "set_has", SetHas,
+  "set_delete", SetDelete,
+]);
+
+utils.Export(function(to) {
+  to.GetExistingHash = GetExistingHash;
+  to.GetHash = GetHash;
+});
+
+})
diff --git a/src/js/generator.js b/src/js/generator.js
new file mode 100644
index 0000000..7f43656
--- /dev/null
+++ b/src/js/generator.js
@@ -0,0 +1,104 @@
+// Copyright 2013 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.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GeneratorFunctionPrototype = utils.ImportNow("GeneratorFunctionPrototype");
+var GeneratorFunction = utils.ImportNow("GeneratorFunction");
+var GlobalFunction = global.Function;
+var MakeTypeError;
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+
+utils.Import(function(from) {
+  MakeTypeError = from.MakeTypeError;
+});
+
+// ----------------------------------------------------------------------------
+
+// Generator functions and objects are specified by ES6, sections 15.19.3 and
+// 15.19.4.
+
+function GeneratorObjectNext(value) {
+  if (!IS_GENERATOR(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        '[Generator].prototype.next', this);
+  }
+
+  var continuation = %GeneratorGetContinuation(this);
+  if (continuation > 0) {
+    // Generator is suspended.
+    DEBUG_PREPARE_STEP_IN_IF_STEPPING(this);
+    try {
+      return %_GeneratorNext(this, value);
+    } catch (e) {
+      %GeneratorClose(this);
+      throw e;
+    }
+  } else if (continuation == 0) {
+    // Generator is already closed.
+    return { value: void 0, done: true };
+  } else {
+    // Generator is running.
+    throw MakeTypeError(kGeneratorRunning);
+  }
+}
+
+
+function GeneratorObjectThrow(exn) {
+  if (!IS_GENERATOR(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        '[Generator].prototype.throw', this);
+  }
+
+  var continuation = %GeneratorGetContinuation(this);
+  if (continuation > 0) {
+    // Generator is suspended.
+    try {
+      return %_GeneratorThrow(this, exn);
+    } catch (e) {
+      %GeneratorClose(this);
+      throw e;
+    }
+  } else if (continuation == 0) {
+    // Generator is already closed.
+    throw exn;
+  } else {
+    // Generator is running.
+    throw MakeTypeError(kGeneratorRunning);
+  }
+}
+
+// ----------------------------------------------------------------------------
+
+// Both Runtime_GeneratorNext and Runtime_GeneratorThrow are supported by
+// neither Crankshaft nor TurboFan, disable optimization of wrappers here.
+%NeverOptimizeFunction(GeneratorObjectNext);
+%NeverOptimizeFunction(GeneratorObjectThrow);
+
+// Set up non-enumerable functions on the generator prototype object.
+var GeneratorObjectPrototype = GeneratorFunctionPrototype.prototype;
+utils.InstallFunctions(GeneratorObjectPrototype,
+                       DONT_ENUM,
+                      ["next", GeneratorObjectNext,
+                       "throw", GeneratorObjectThrow]);
+
+%AddNamedProperty(GeneratorObjectPrototype, "constructor",
+    GeneratorFunctionPrototype, DONT_ENUM | READ_ONLY);
+%AddNamedProperty(GeneratorObjectPrototype,
+    toStringTagSymbol, "Generator", DONT_ENUM | READ_ONLY);
+%InternalSetPrototype(GeneratorFunctionPrototype, GlobalFunction.prototype);
+%AddNamedProperty(GeneratorFunctionPrototype,
+    toStringTagSymbol, "GeneratorFunction", DONT_ENUM | READ_ONLY);
+%AddNamedProperty(GeneratorFunctionPrototype, "constructor",
+    GeneratorFunction, DONT_ENUM | READ_ONLY);
+%InternalSetPrototype(GeneratorFunction, GlobalFunction);
+
+})
diff --git a/src/js/harmony-atomics.js b/src/js/harmony-atomics.js
new file mode 100644
index 0000000..b861a2a
--- /dev/null
+++ b/src/js/harmony-atomics.js
@@ -0,0 +1,215 @@
+// Copyright 2015 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.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalObject = global.Object;
+var MakeTypeError;
+var MaxSimple;
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+
+utils.Import(function(from) {
+  MakeTypeError = from.MakeTypeError;
+  MaxSimple = from.MaxSimple;
+});
+
+// -------------------------------------------------------------------
+
+
+function CheckSharedIntegerTypedArray(ia) {
+  if (!%IsSharedIntegerTypedArray(ia)) {
+    throw MakeTypeError(kNotIntegerSharedTypedArray, ia);
+  }
+}
+
+function CheckSharedInteger32TypedArray(ia) {
+  CheckSharedIntegerTypedArray(ia);
+  if (!%IsSharedInteger32TypedArray(ia)) {
+    throw MakeTypeError(kNotInt32SharedTypedArray, ia);
+  }
+}
+
+//-------------------------------------------------------------------
+
+function AtomicsCompareExchangeJS(sta, index, oldValue, newValue) {
+  CheckSharedIntegerTypedArray(sta);
+  index = TO_INTEGER(index);
+  if (index < 0 || index >= %_TypedArrayGetLength(sta)) {
+    return UNDEFINED;
+  }
+  oldValue = TO_NUMBER(oldValue);
+  newValue = TO_NUMBER(newValue);
+  return %_AtomicsCompareExchange(sta, index, oldValue, newValue);
+}
+
+function AtomicsLoadJS(sta, index) {
+  CheckSharedIntegerTypedArray(sta);
+  index = TO_INTEGER(index);
+  if (index < 0 || index >= %_TypedArrayGetLength(sta)) {
+    return UNDEFINED;
+  }
+  return %_AtomicsLoad(sta, index);
+}
+
+function AtomicsStoreJS(sta, index, value) {
+  CheckSharedIntegerTypedArray(sta);
+  index = TO_INTEGER(index);
+  if (index < 0 || index >= %_TypedArrayGetLength(sta)) {
+    return UNDEFINED;
+  }
+  value = TO_NUMBER(value);
+  return %_AtomicsStore(sta, index, value);
+}
+
+function AtomicsAddJS(ia, index, value) {
+  CheckSharedIntegerTypedArray(ia);
+  index = TO_INTEGER(index);
+  if (index < 0 || index >= %_TypedArrayGetLength(ia)) {
+    return UNDEFINED;
+  }
+  value = TO_NUMBER(value);
+  return %_AtomicsAdd(ia, index, value);
+}
+
+function AtomicsSubJS(ia, index, value) {
+  CheckSharedIntegerTypedArray(ia);
+  index = TO_INTEGER(index);
+  if (index < 0 || index >= %_TypedArrayGetLength(ia)) {
+    return UNDEFINED;
+  }
+  value = TO_NUMBER(value);
+  return %_AtomicsSub(ia, index, value);
+}
+
+function AtomicsAndJS(ia, index, value) {
+  CheckSharedIntegerTypedArray(ia);
+  index = TO_INTEGER(index);
+  if (index < 0 || index >= %_TypedArrayGetLength(ia)) {
+    return UNDEFINED;
+  }
+  value = TO_NUMBER(value);
+  return %_AtomicsAnd(ia, index, value);
+}
+
+function AtomicsOrJS(ia, index, value) {
+  CheckSharedIntegerTypedArray(ia);
+  index = TO_INTEGER(index);
+  if (index < 0 || index >= %_TypedArrayGetLength(ia)) {
+    return UNDEFINED;
+  }
+  value = TO_NUMBER(value);
+  return %_AtomicsOr(ia, index, value);
+}
+
+function AtomicsXorJS(ia, index, value) {
+  CheckSharedIntegerTypedArray(ia);
+  index = TO_INTEGER(index);
+  if (index < 0 || index >= %_TypedArrayGetLength(ia)) {
+    return UNDEFINED;
+  }
+  value = TO_NUMBER(value);
+  return %_AtomicsXor(ia, index, value);
+}
+
+function AtomicsExchangeJS(ia, index, value) {
+  CheckSharedIntegerTypedArray(ia);
+  index = TO_INTEGER(index);
+  if (index < 0 || index >= %_TypedArrayGetLength(ia)) {
+    return UNDEFINED;
+  }
+  value = TO_NUMBER(value);
+  return %_AtomicsExchange(ia, index, value);
+}
+
+function AtomicsIsLockFreeJS(size) {
+  return %_AtomicsIsLockFree(size);
+}
+
+// Futexes
+
+function AtomicsFutexWaitJS(ia, index, value, timeout) {
+  CheckSharedInteger32TypedArray(ia);
+  index = TO_INTEGER(index);
+  if (index < 0 || index >= %_TypedArrayGetLength(ia)) {
+    return UNDEFINED;
+  }
+  if (IS_UNDEFINED(timeout)) {
+    timeout = INFINITY;
+  } else {
+    timeout = TO_NUMBER(timeout);
+    if (NUMBER_IS_NAN(timeout)) {
+      timeout = INFINITY;
+    } else {
+      timeout = MaxSimple(0, timeout);
+    }
+  }
+  return %AtomicsFutexWait(ia, index, value, timeout);
+}
+
+function AtomicsFutexWakeJS(ia, index, count) {
+  CheckSharedInteger32TypedArray(ia);
+  index = TO_INTEGER(index);
+  if (index < 0 || index >= %_TypedArrayGetLength(ia)) {
+    return UNDEFINED;
+  }
+  count = MaxSimple(0, TO_INTEGER(count));
+  return %AtomicsFutexWake(ia, index, count);
+}
+
+function AtomicsFutexWakeOrRequeueJS(ia, index1, count, value, index2) {
+  CheckSharedInteger32TypedArray(ia);
+  index1 = TO_INTEGER(index1);
+  count = MaxSimple(0, TO_INTEGER(count));
+  value = TO_INT32(value);
+  index2 = TO_INTEGER(index2);
+  if (index1 < 0 || index1 >= %_TypedArrayGetLength(ia) ||
+      index2 < 0 || index2 >= %_TypedArrayGetLength(ia)) {
+    return UNDEFINED;
+  }
+  return %AtomicsFutexWakeOrRequeue(ia, index1, count, value, index2);
+}
+
+// -------------------------------------------------------------------
+
+function AtomicsConstructor() {}
+
+var Atomics = new AtomicsConstructor();
+
+%InternalSetPrototype(Atomics, GlobalObject.prototype);
+%AddNamedProperty(global, "Atomics", Atomics, DONT_ENUM);
+%FunctionSetInstanceClassName(AtomicsConstructor, 'Atomics');
+
+%AddNamedProperty(Atomics, toStringTagSymbol, "Atomics", READ_ONLY | DONT_ENUM);
+
+// These must match the values in src/futex-emulation.h
+utils.InstallConstants(Atomics, [
+  "OK", 0,
+  "NOTEQUAL", -1,
+  "TIMEDOUT", -2,
+]);
+
+utils.InstallFunctions(Atomics, DONT_ENUM, [
+  "compareExchange", AtomicsCompareExchangeJS,
+  "load", AtomicsLoadJS,
+  "store", AtomicsStoreJS,
+  "add", AtomicsAddJS,
+  "sub", AtomicsSubJS,
+  "and", AtomicsAndJS,
+  "or", AtomicsOrJS,
+  "xor", AtomicsXorJS,
+  "exchange", AtomicsExchangeJS,
+  "isLockFree", AtomicsIsLockFreeJS,
+  "futexWait", AtomicsFutexWaitJS,
+  "futexWake", AtomicsFutexWakeJS,
+  "futexWakeOrRequeue", AtomicsFutexWakeOrRequeueJS,
+]);
+
+})
diff --git a/src/js/harmony-object-observe.js b/src/js/harmony-object-observe.js
new file mode 100644
index 0000000..95dd298
--- /dev/null
+++ b/src/js/harmony-object-observe.js
@@ -0,0 +1,17 @@
+// Copyright 2015 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.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+var ObserveArrayMethods = utils.ImportNow("ObserveArrayMethods");
+var ObserveObjectMethods = utils.ImportNow("ObserveObjectMethods");;
+
+utils.InstallFunctions(global.Object, DONT_ENUM, ObserveObjectMethods);
+utils.InstallFunctions(global.Array, DONT_ENUM, ObserveArrayMethods);
+
+})
diff --git a/src/js/harmony-reflect.js b/src/js/harmony-reflect.js
new file mode 100644
index 0000000..dcadad5
--- /dev/null
+++ b/src/js/harmony-reflect.js
@@ -0,0 +1,37 @@
+// Copyright 2013-2015 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.
+
+(function(global, utils) {
+
+'use strict';
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalReflect = global.Reflect;
+var MakeTypeError;
+var ReflectApply = utils.ImportNow("reflect_apply");
+var ReflectConstruct = utils.ImportNow("reflect_construct");
+
+utils.Import(function(from) {
+  MakeTypeError = from.MakeTypeError;
+});
+
+// -------------------------------------------------------------------
+
+function ReflectEnumerate(obj) {
+  if (!IS_RECEIVER(obj))
+    throw MakeTypeError(kCalledOnNonObject, "Reflect.enumerate")
+  return (function* () { for (var x in obj) yield x })();
+}
+
+utils.InstallFunctions(GlobalReflect, DONT_ENUM, [
+  "apply", ReflectApply,
+  "construct", ReflectConstruct,
+  "enumerate", ReflectEnumerate
+]);
+
+})
diff --git a/src/js/harmony-regexp.js b/src/js/harmony-regexp.js
new file mode 100644
index 0000000..f76ef86
--- /dev/null
+++ b/src/js/harmony-regexp.js
@@ -0,0 +1,60 @@
+// 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.
+
+(function(global, utils) {
+
+'use strict';
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalRegExp = global.RegExp;
+var GlobalRegExpPrototype = GlobalRegExp.prototype;
+var MakeTypeError;
+var regExpFlagsSymbol = utils.ImportNow("regexp_flags_symbol");
+
+utils.Import(function(from) {
+  MakeTypeError = from.MakeTypeError;
+});
+
+// -------------------------------------------------------------------
+
+// ES6 draft 12-06-13, section 21.2.5.3
+// + https://bugs.ecmascript.org/show_bug.cgi?id=3423
+function RegExpGetFlags() {
+  if (!IS_RECEIVER(this)) {
+    throw MakeTypeError(
+        kRegExpNonObject, "RegExp.prototype.flags", TO_STRING(this));
+  }
+  var result = '';
+  if (this.global) result += 'g';
+  if (this.ignoreCase) result += 'i';
+  if (this.multiline) result += 'm';
+  if (this.unicode) result += 'u';
+  if (this.sticky) result += 'y';
+  return result;
+}
+
+// ES6 21.2.5.12.
+function RegExpGetSticky() {
+  if (!IS_REGEXP(this)) {
+    // Compat fix: RegExp.prototype.sticky == undefined; UseCounter tracks it
+    // TODO(littledan): Remove this workaround or standardize it
+    if (this === GlobalRegExpPrototype) {
+      %IncrementUseCounter(kRegExpPrototypeStickyGetter);
+      return UNDEFINED;
+    }
+    throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.sticky");
+  }
+  return !!REGEXP_STICKY(this);
+}
+%FunctionSetName(RegExpGetSticky, "RegExp.prototype.sticky");
+%SetNativeFlag(RegExpGetSticky);
+
+utils.InstallGetter(GlobalRegExp.prototype, 'flags', RegExpGetFlags);
+utils.InstallGetter(GlobalRegExp.prototype, 'sticky', RegExpGetSticky);
+
+})
diff --git a/src/js/harmony-sharedarraybuffer.js b/src/js/harmony-sharedarraybuffer.js
new file mode 100644
index 0000000..10ceb70
--- /dev/null
+++ b/src/js/harmony-sharedarraybuffer.js
@@ -0,0 +1,31 @@
+// Copyright 2015 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.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+var GlobalSharedArrayBuffer = global.SharedArrayBuffer;
+var MakeTypeError;
+
+utils.Import(function(from) {
+  MakeTypeError = from.MakeTypeError;
+})
+
+// -------------------------------------------------------------------
+
+function SharedArrayBufferGetByteLen() {
+  if (!IS_SHAREDARRAYBUFFER(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'SharedArrayBuffer.prototype.byteLength', this);
+  }
+  return %_ArrayBufferGetByteLength(this);
+}
+
+utils.InstallGetter(GlobalSharedArrayBuffer.prototype, "byteLength",
+                    SharedArrayBufferGetByteLen);
+
+})
diff --git a/src/js/harmony-simd.js b/src/js/harmony-simd.js
new file mode 100644
index 0000000..4df2f43
--- /dev/null
+++ b/src/js/harmony-simd.js
@@ -0,0 +1,941 @@
+// Copyright 2015 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.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalSIMD = global.SIMD;
+var MakeTypeError;
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+
+utils.Import(function(from) {
+  MakeTypeError = from.MakeTypeError;
+});
+
+// -------------------------------------------------------------------
+
+macro SIMD_FLOAT_TYPES(FUNCTION)
+FUNCTION(Float32x4, float32x4, 4)
+endmacro
+
+macro SIMD_INT_TYPES(FUNCTION)
+FUNCTION(Int32x4, int32x4, 4)
+FUNCTION(Int16x8, int16x8, 8)
+FUNCTION(Int8x16, int8x16, 16)
+endmacro
+
+macro SIMD_UINT_TYPES(FUNCTION)
+FUNCTION(Uint32x4, uint32x4, 4)
+FUNCTION(Uint16x8, uint16x8, 8)
+FUNCTION(Uint8x16, uint8x16, 16)
+endmacro
+
+macro SIMD_BOOL_TYPES(FUNCTION)
+FUNCTION(Bool32x4, bool32x4, 4)
+FUNCTION(Bool16x8, bool16x8, 8)
+FUNCTION(Bool8x16, bool8x16, 16)
+endmacro
+
+macro SIMD_ALL_TYPES(FUNCTION)
+SIMD_FLOAT_TYPES(FUNCTION)
+SIMD_INT_TYPES(FUNCTION)
+SIMD_UINT_TYPES(FUNCTION)
+SIMD_BOOL_TYPES(FUNCTION)
+endmacro
+
+macro DECLARE_GLOBALS(NAME, TYPE, LANES)
+var GlobalNAME = GlobalSIMD.NAME;
+endmacro
+
+SIMD_ALL_TYPES(DECLARE_GLOBALS)
+
+macro DECLARE_COMMON_FUNCTIONS(NAME, TYPE, LANES)
+function NAMECheckJS(a) {
+  return %NAMECheck(a);
+}
+
+function NAMEToString() {
+  var value = %_ValueOf(this);
+  if (typeof(value) !== 'TYPE') {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        "NAME.prototype.toString", this);
+  }
+  var str = "SIMD.NAME(";
+  str += %NAMEExtractLane(value, 0);
+  for (var i = 1; i < LANES; i++) {
+    str += ", " + %NAMEExtractLane(value, i);
+  }
+  return str + ")";
+}
+
+function NAMEToLocaleString() {
+  var value = %_ValueOf(this);
+  if (typeof(value) !== 'TYPE') {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        "NAME.prototype.toLocaleString", this);
+  }
+  var str = "SIMD.NAME(";
+  str += %NAMEExtractLane(value, 0).toLocaleString();
+  for (var i = 1; i < LANES; i++) {
+    str += ", " + %NAMEExtractLane(value, i).toLocaleString();
+  }
+  return str + ")";
+}
+
+function NAMEValueOf() {
+  var value = %_ValueOf(this);
+  if (typeof(value) !== 'TYPE') {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        "NAME.prototype.valueOf", this);
+  }
+  return value;
+}
+
+function NAMEExtractLaneJS(instance, lane) {
+  return %NAMEExtractLane(instance, lane);
+}
+endmacro
+
+SIMD_ALL_TYPES(DECLARE_COMMON_FUNCTIONS)
+
+macro DECLARE_SHIFT_FUNCTIONS(NAME, TYPE, LANES)
+function NAMEShiftLeftByScalarJS(instance, shift) {
+  return %NAMEShiftLeftByScalar(instance, shift);
+}
+
+function NAMEShiftRightByScalarJS(instance, shift) {
+  return %NAMEShiftRightByScalar(instance, shift);
+}
+endmacro
+
+SIMD_INT_TYPES(DECLARE_SHIFT_FUNCTIONS)
+SIMD_UINT_TYPES(DECLARE_SHIFT_FUNCTIONS)
+
+macro SIMD_SMALL_INT_TYPES(FUNCTION)
+FUNCTION(Int16x8)
+FUNCTION(Int8x16)
+FUNCTION(Uint8x16)
+FUNCTION(Uint16x8)
+endmacro
+
+macro DECLARE_SMALL_INT_FUNCTIONS(NAME)
+function NAMEAddSaturateJS(a, b) {
+  return %NAMEAddSaturate(a, b);
+}
+
+function NAMESubSaturateJS(a, b) {
+  return %NAMESubSaturate(a, b);
+}
+endmacro
+
+SIMD_SMALL_INT_TYPES(DECLARE_SMALL_INT_FUNCTIONS)
+
+macro DECLARE_SIGNED_FUNCTIONS(NAME, TYPE, LANES)
+function NAMENegJS(a) {
+  return %NAMENeg(a);
+}
+endmacro
+
+SIMD_FLOAT_TYPES(DECLARE_SIGNED_FUNCTIONS)
+SIMD_INT_TYPES(DECLARE_SIGNED_FUNCTIONS)
+
+macro DECLARE_BOOL_FUNCTIONS(NAME, TYPE, LANES)
+function NAMEReplaceLaneJS(instance, lane, value) {
+  return %NAMEReplaceLane(instance, lane, value);
+}
+
+function NAMEAnyTrueJS(s) {
+  return %NAMEAnyTrue(s);
+}
+
+function NAMEAllTrueJS(s) {
+  return %NAMEAllTrue(s);
+}
+endmacro
+
+SIMD_BOOL_TYPES(DECLARE_BOOL_FUNCTIONS)
+
+macro SIMD_NUMERIC_TYPES(FUNCTION)
+SIMD_FLOAT_TYPES(FUNCTION)
+SIMD_INT_TYPES(FUNCTION)
+SIMD_UINT_TYPES(FUNCTION)
+endmacro
+
+macro DECLARE_NUMERIC_FUNCTIONS(NAME, TYPE, LANES)
+function NAMEReplaceLaneJS(instance, lane, value) {
+  return %NAMEReplaceLane(instance, lane, TO_NUMBER(value));
+}
+
+function NAMESelectJS(selector, a, b) {
+  return %NAMESelect(selector, a, b);
+}
+
+function NAMEAddJS(a, b) {
+  return %NAMEAdd(a, b);
+}
+
+function NAMESubJS(a, b) {
+  return %NAMESub(a, b);
+}
+
+function NAMEMulJS(a, b) {
+  return %NAMEMul(a, b);
+}
+
+function NAMEMinJS(a, b) {
+  return %NAMEMin(a, b);
+}
+
+function NAMEMaxJS(a, b) {
+  return %NAMEMax(a, b);
+}
+
+function NAMEEqualJS(a, b) {
+  return %NAMEEqual(a, b);
+}
+
+function NAMENotEqualJS(a, b) {
+  return %NAMENotEqual(a, b);
+}
+
+function NAMELessThanJS(a, b) {
+  return %NAMELessThan(a, b);
+}
+
+function NAMELessThanOrEqualJS(a, b) {
+  return %NAMELessThanOrEqual(a, b);
+}
+
+function NAMEGreaterThanJS(a, b) {
+  return %NAMEGreaterThan(a, b);
+}
+
+function NAMEGreaterThanOrEqualJS(a, b) {
+  return %NAMEGreaterThanOrEqual(a, b);
+}
+
+function NAMELoadJS(tarray, index) {
+  return %NAMELoad(tarray, index);
+}
+
+function NAMEStoreJS(tarray, index, a) {
+  return %NAMEStore(tarray, index, a);
+}
+endmacro
+
+SIMD_NUMERIC_TYPES(DECLARE_NUMERIC_FUNCTIONS)
+
+macro SIMD_LOGICAL_TYPES(FUNCTION)
+SIMD_INT_TYPES(FUNCTION)
+SIMD_UINT_TYPES(FUNCTION)
+SIMD_BOOL_TYPES(FUNCTION)
+endmacro
+
+macro DECLARE_LOGICAL_FUNCTIONS(NAME, TYPE, LANES)
+function NAMEAndJS(a, b) {
+  return %NAMEAnd(a, b);
+}
+
+function NAMEOrJS(a, b) {
+  return %NAMEOr(a, b);
+}
+
+function NAMEXorJS(a, b) {
+  return %NAMEXor(a, b);
+}
+
+function NAMENotJS(a) {
+  return %NAMENot(a);
+}
+endmacro
+
+SIMD_LOGICAL_TYPES(DECLARE_LOGICAL_FUNCTIONS)
+
+macro SIMD_FROM_TYPES(FUNCTION)
+FUNCTION(Float32x4, Int32x4)
+FUNCTION(Float32x4, Uint32x4)
+FUNCTION(Int32x4, Float32x4)
+FUNCTION(Int32x4, Uint32x4)
+FUNCTION(Uint32x4, Float32x4)
+FUNCTION(Uint32x4, Int32x4)
+FUNCTION(Int16x8, Uint16x8)
+FUNCTION(Uint16x8, Int16x8)
+FUNCTION(Int8x16, Uint8x16)
+FUNCTION(Uint8x16, Int8x16)
+endmacro
+
+macro DECLARE_FROM_FUNCTIONS(TO, FROM)
+function TOFromFROMJS(a) {
+  return %TOFromFROM(a);
+}
+endmacro
+
+SIMD_FROM_TYPES(DECLARE_FROM_FUNCTIONS)
+
+macro SIMD_FROM_BITS_TYPES(FUNCTION)
+FUNCTION(Float32x4, Int32x4)
+FUNCTION(Float32x4, Uint32x4)
+FUNCTION(Float32x4, Int16x8)
+FUNCTION(Float32x4, Uint16x8)
+FUNCTION(Float32x4, Int8x16)
+FUNCTION(Float32x4, Uint8x16)
+FUNCTION(Int32x4, Float32x4)
+FUNCTION(Int32x4, Uint32x4)
+FUNCTION(Int32x4, Int16x8)
+FUNCTION(Int32x4, Uint16x8)
+FUNCTION(Int32x4, Int8x16)
+FUNCTION(Int32x4, Uint8x16)
+FUNCTION(Uint32x4, Float32x4)
+FUNCTION(Uint32x4, Int32x4)
+FUNCTION(Uint32x4, Int16x8)
+FUNCTION(Uint32x4, Uint16x8)
+FUNCTION(Uint32x4, Int8x16)
+FUNCTION(Uint32x4, Uint8x16)
+FUNCTION(Int16x8, Float32x4)
+FUNCTION(Int16x8, Int32x4)
+FUNCTION(Int16x8, Uint32x4)
+FUNCTION(Int16x8, Uint16x8)
+FUNCTION(Int16x8, Int8x16)
+FUNCTION(Int16x8, Uint8x16)
+FUNCTION(Uint16x8, Float32x4)
+FUNCTION(Uint16x8, Int32x4)
+FUNCTION(Uint16x8, Uint32x4)
+FUNCTION(Uint16x8, Int16x8)
+FUNCTION(Uint16x8, Int8x16)
+FUNCTION(Uint16x8, Uint8x16)
+FUNCTION(Int8x16, Float32x4)
+FUNCTION(Int8x16, Int32x4)
+FUNCTION(Int8x16, Uint32x4)
+FUNCTION(Int8x16, Int16x8)
+FUNCTION(Int8x16, Uint16x8)
+FUNCTION(Int8x16, Uint8x16)
+FUNCTION(Uint8x16, Float32x4)
+FUNCTION(Uint8x16, Int32x4)
+FUNCTION(Uint8x16, Uint32x4)
+FUNCTION(Uint8x16, Int16x8)
+FUNCTION(Uint8x16, Uint16x8)
+FUNCTION(Uint8x16, Int8x16)
+endmacro
+
+macro DECLARE_FROM_BITS_FUNCTIONS(TO, FROM)
+function TOFromFROMBitsJS(a) {
+  return %TOFromFROMBits(a);
+}
+endmacro
+
+SIMD_FROM_BITS_TYPES(DECLARE_FROM_BITS_FUNCTIONS)
+
+
+macro SIMD_LOADN_STOREN_TYPES(FUNCTION)
+FUNCTION(Float32x4, 1)
+FUNCTION(Float32x4, 2)
+FUNCTION(Float32x4, 3)
+FUNCTION(Int32x4, 1)
+FUNCTION(Int32x4, 2)
+FUNCTION(Int32x4, 3)
+FUNCTION(Uint32x4, 1)
+FUNCTION(Uint32x4, 2)
+FUNCTION(Uint32x4, 3)
+endmacro
+
+macro DECLARE_LOADN_STOREN_FUNCTIONS(NAME, COUNT)
+function NAMELoadCOUNTJS(tarray, index) {
+  return %NAMELoadCOUNT(tarray, index);
+}
+
+function NAMEStoreCOUNTJS(tarray, index, a) {
+  return %NAMEStoreCOUNT(tarray, index, a);
+}
+endmacro
+
+SIMD_LOADN_STOREN_TYPES(DECLARE_LOADN_STOREN_FUNCTIONS)
+
+//-------------------------------------------------------------------
+
+macro SIMD_X4_TYPES(FUNCTION)
+FUNCTION(Float32x4)
+FUNCTION(Int32x4)
+FUNCTION(Uint32x4)
+FUNCTION(Bool32x4)
+endmacro
+
+macro DECLARE_X4_FUNCTIONS(NAME)
+function NAMESplat(s) {
+  return %CreateNAME(s, s, s, s);
+}
+
+function NAMESwizzleJS(a, c0, c1, c2, c3) {
+  return %NAMESwizzle(a, c0, c1, c2, c3);
+}
+
+function NAMEShuffleJS(a, b, c0, c1, c2, c3) {
+  return %NAMEShuffle(a, b, c0, c1, c2, c3);
+}
+endmacro
+
+SIMD_X4_TYPES(DECLARE_X4_FUNCTIONS)
+
+macro SIMD_X8_TYPES(FUNCTION)
+FUNCTION(Int16x8)
+FUNCTION(Uint16x8)
+FUNCTION(Bool16x8)
+endmacro
+
+macro DECLARE_X8_FUNCTIONS(NAME)
+function NAMESplat(s) {
+  return %CreateNAME(s, s, s, s, s, s, s, s);
+}
+
+function NAMESwizzleJS(a, c0, c1, c2, c3, c4, c5, c6, c7) {
+  return %NAMESwizzle(a, c0, c1, c2, c3, c4, c5, c6, c7);
+}
+
+function NAMEShuffleJS(a, b, c0, c1, c2, c3, c4, c5, c6, c7) {
+  return %NAMEShuffle(a, b, c0, c1, c2, c3, c4, c5, c6, c7);
+}
+endmacro
+
+SIMD_X8_TYPES(DECLARE_X8_FUNCTIONS)
+
+macro SIMD_X16_TYPES(FUNCTION)
+FUNCTION(Int8x16)
+FUNCTION(Uint8x16)
+FUNCTION(Bool8x16)
+endmacro
+
+macro DECLARE_X16_FUNCTIONS(NAME)
+function NAMESplat(s) {
+  return %CreateNAME(s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s);
+}
+
+function NAMESwizzleJS(a, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11,
+                          c12, c13, c14, c15) {
+  return %NAMESwizzle(a, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11,
+                         c12, c13, c14, c15);
+}
+
+function NAMEShuffleJS(a, b, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10,
+                             c11, c12, c13, c14, c15) {
+  return %NAMEShuffle(a, b, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10,
+                            c11, c12, c13, c14, c15);
+}
+endmacro
+
+SIMD_X16_TYPES(DECLARE_X16_FUNCTIONS)
+
+//-------------------------------------------------------------------
+
+function Float32x4Constructor(c0, c1, c2, c3) {
+  if (!IS_UNDEFINED(new.target)) {
+    throw MakeTypeError(kNotConstructor, "Float32x4");
+  }
+  return %CreateFloat32x4(TO_NUMBER(c0), TO_NUMBER(c1),
+                          TO_NUMBER(c2), TO_NUMBER(c3));
+}
+
+
+function Int32x4Constructor(c0, c1, c2, c3) {
+  if (!IS_UNDEFINED(new.target)) {
+    throw MakeTypeError(kNotConstructor, "Int32x4");
+  }
+  return %CreateInt32x4(TO_NUMBER(c0), TO_NUMBER(c1),
+                        TO_NUMBER(c2), TO_NUMBER(c3));
+}
+
+
+function Uint32x4Constructor(c0, c1, c2, c3) {
+  if (!IS_UNDEFINED(new.target)) {
+    throw MakeTypeError(kNotConstructor, "Uint32x4");
+  }
+  return %CreateUint32x4(TO_NUMBER(c0), TO_NUMBER(c1),
+                         TO_NUMBER(c2), TO_NUMBER(c3));
+}
+
+
+function Bool32x4Constructor(c0, c1, c2, c3) {
+  if (!IS_UNDEFINED(new.target)) {
+    throw MakeTypeError(kNotConstructor, "Bool32x4");
+  }
+  return %CreateBool32x4(c0, c1, c2, c3);
+}
+
+
+function Int16x8Constructor(c0, c1, c2, c3, c4, c5, c6, c7) {
+  if (!IS_UNDEFINED(new.target)) {
+    throw MakeTypeError(kNotConstructor, "Int16x8");
+  }
+  return %CreateInt16x8(TO_NUMBER(c0), TO_NUMBER(c1),
+                        TO_NUMBER(c2), TO_NUMBER(c3),
+                        TO_NUMBER(c4), TO_NUMBER(c5),
+                        TO_NUMBER(c6), TO_NUMBER(c7));
+}
+
+
+function Uint16x8Constructor(c0, c1, c2, c3, c4, c5, c6, c7) {
+  if (!IS_UNDEFINED(new.target)) {
+    throw MakeTypeError(kNotConstructor, "Uint16x8");
+  }
+  return %CreateUint16x8(TO_NUMBER(c0), TO_NUMBER(c1),
+                         TO_NUMBER(c2), TO_NUMBER(c3),
+                         TO_NUMBER(c4), TO_NUMBER(c5),
+                         TO_NUMBER(c6), TO_NUMBER(c7));
+}
+
+
+function Bool16x8Constructor(c0, c1, c2, c3, c4, c5, c6, c7) {
+  if (!IS_UNDEFINED(new.target)) {
+    throw MakeTypeError(kNotConstructor, "Bool16x8");
+  }
+  return %CreateBool16x8(c0, c1, c2, c3, c4, c5, c6, c7);
+}
+
+
+function Int8x16Constructor(c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11,
+                            c12, c13, c14, c15) {
+  if (!IS_UNDEFINED(new.target)) {
+    throw MakeTypeError(kNotConstructor, "Int8x16");
+  }
+  return %CreateInt8x16(TO_NUMBER(c0), TO_NUMBER(c1),
+                        TO_NUMBER(c2), TO_NUMBER(c3),
+                        TO_NUMBER(c4), TO_NUMBER(c5),
+                        TO_NUMBER(c6), TO_NUMBER(c7),
+                        TO_NUMBER(c8), TO_NUMBER(c9),
+                        TO_NUMBER(c10), TO_NUMBER(c11),
+                        TO_NUMBER(c12), TO_NUMBER(c13),
+                        TO_NUMBER(c14), TO_NUMBER(c15));
+}
+
+
+function Uint8x16Constructor(c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11,
+                             c12, c13, c14, c15) {
+  if (!IS_UNDEFINED(new.target)) {
+    throw MakeTypeError(kNotConstructor, "Uint8x16");
+  }
+  return %CreateUint8x16(TO_NUMBER(c0), TO_NUMBER(c1),
+                         TO_NUMBER(c2), TO_NUMBER(c3),
+                         TO_NUMBER(c4), TO_NUMBER(c5),
+                         TO_NUMBER(c6), TO_NUMBER(c7),
+                         TO_NUMBER(c8), TO_NUMBER(c9),
+                         TO_NUMBER(c10), TO_NUMBER(c11),
+                         TO_NUMBER(c12), TO_NUMBER(c13),
+                         TO_NUMBER(c14), TO_NUMBER(c15));
+}
+
+
+function Bool8x16Constructor(c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11,
+                             c12, c13, c14, c15) {
+  if (!IS_UNDEFINED(new.target)) {
+    throw MakeTypeError(kNotConstructor, "Bool8x16");
+  }
+  return %CreateBool8x16(c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12,
+                         c13, c14, c15);
+}
+
+
+function Float32x4AbsJS(a) {
+  return %Float32x4Abs(a);
+}
+
+
+function Float32x4SqrtJS(a) {
+  return %Float32x4Sqrt(a);
+}
+
+
+function Float32x4RecipApproxJS(a) {
+  return %Float32x4RecipApprox(a);
+}
+
+
+function Float32x4RecipSqrtApproxJS(a) {
+  return %Float32x4RecipSqrtApprox(a);
+}
+
+
+function Float32x4DivJS(a, b) {
+  return %Float32x4Div(a, b);
+}
+
+
+function Float32x4MinNumJS(a, b) {
+  return %Float32x4MinNum(a, b);
+}
+
+
+function Float32x4MaxNumJS(a, b) {
+  return %Float32x4MaxNum(a, b);
+}
+
+
+%AddNamedProperty(GlobalSIMD, toStringTagSymbol, 'SIMD', READ_ONLY | DONT_ENUM);
+
+macro SETUP_SIMD_TYPE(NAME, TYPE, LANES)
+%SetCode(GlobalNAME, NAMEConstructor);
+%FunctionSetPrototype(GlobalNAME, {});
+%AddNamedProperty(GlobalNAME.prototype, 'constructor', GlobalNAME,
+    DONT_ENUM);
+%AddNamedProperty(GlobalNAME.prototype, toStringTagSymbol, 'NAME',
+    DONT_ENUM | READ_ONLY);
+utils.InstallFunctions(GlobalNAME.prototype, DONT_ENUM, [
+  'toLocaleString', NAMEToLocaleString,
+  'toString', NAMEToString,
+  'valueOf', NAMEValueOf,
+]);
+endmacro
+
+SIMD_ALL_TYPES(SETUP_SIMD_TYPE)
+
+//-------------------------------------------------------------------
+
+utils.InstallFunctions(GlobalFloat32x4, DONT_ENUM, [
+  'splat', Float32x4Splat,
+  'check', Float32x4CheckJS,
+  'extractLane', Float32x4ExtractLaneJS,
+  'replaceLane', Float32x4ReplaceLaneJS,
+  'neg', Float32x4NegJS,
+  'abs', Float32x4AbsJS,
+  'sqrt', Float32x4SqrtJS,
+  'reciprocalApproximation', Float32x4RecipApproxJS,
+  'reciprocalSqrtApproximation', Float32x4RecipSqrtApproxJS,
+  'add', Float32x4AddJS,
+  'sub', Float32x4SubJS,
+  'mul', Float32x4MulJS,
+  'div', Float32x4DivJS,
+  'min', Float32x4MinJS,
+  'max', Float32x4MaxJS,
+  'minNum', Float32x4MinNumJS,
+  'maxNum', Float32x4MaxNumJS,
+  'lessThan', Float32x4LessThanJS,
+  'lessThanOrEqual', Float32x4LessThanOrEqualJS,
+  'greaterThan', Float32x4GreaterThanJS,
+  'greaterThanOrEqual', Float32x4GreaterThanOrEqualJS,
+  'equal', Float32x4EqualJS,
+  'notEqual', Float32x4NotEqualJS,
+  'select', Float32x4SelectJS,
+  'swizzle', Float32x4SwizzleJS,
+  'shuffle', Float32x4ShuffleJS,
+  'fromInt32x4', Float32x4FromInt32x4JS,
+  'fromUint32x4', Float32x4FromUint32x4JS,
+  'fromInt32x4Bits', Float32x4FromInt32x4BitsJS,
+  'fromUint32x4Bits', Float32x4FromUint32x4BitsJS,
+  'fromInt16x8Bits', Float32x4FromInt16x8BitsJS,
+  'fromUint16x8Bits', Float32x4FromUint16x8BitsJS,
+  'fromInt8x16Bits', Float32x4FromInt8x16BitsJS,
+  'fromUint8x16Bits', Float32x4FromUint8x16BitsJS,
+  'load', Float32x4LoadJS,
+  'load1', Float32x4Load1JS,
+  'load2', Float32x4Load2JS,
+  'load3', Float32x4Load3JS,
+  'store', Float32x4StoreJS,
+  'store1', Float32x4Store1JS,
+  'store2', Float32x4Store2JS,
+  'store3', Float32x4Store3JS,
+]);
+
+utils.InstallFunctions(GlobalInt32x4, DONT_ENUM, [
+  'splat', Int32x4Splat,
+  'check', Int32x4CheckJS,
+  'extractLane', Int32x4ExtractLaneJS,
+  'replaceLane', Int32x4ReplaceLaneJS,
+  'neg', Int32x4NegJS,
+  'add', Int32x4AddJS,
+  'sub', Int32x4SubJS,
+  'mul', Int32x4MulJS,
+  'min', Int32x4MinJS,
+  'max', Int32x4MaxJS,
+  'and', Int32x4AndJS,
+  'or', Int32x4OrJS,
+  'xor', Int32x4XorJS,
+  'not', Int32x4NotJS,
+  'shiftLeftByScalar', Int32x4ShiftLeftByScalarJS,
+  'shiftRightByScalar', Int32x4ShiftRightByScalarJS,
+  'lessThan', Int32x4LessThanJS,
+  'lessThanOrEqual', Int32x4LessThanOrEqualJS,
+  'greaterThan', Int32x4GreaterThanJS,
+  'greaterThanOrEqual', Int32x4GreaterThanOrEqualJS,
+  'equal', Int32x4EqualJS,
+  'notEqual', Int32x4NotEqualJS,
+  'select', Int32x4SelectJS,
+  'swizzle', Int32x4SwizzleJS,
+  'shuffle', Int32x4ShuffleJS,
+  'fromFloat32x4', Int32x4FromFloat32x4JS,
+  'fromUint32x4', Int32x4FromUint32x4JS,
+  'fromFloat32x4Bits', Int32x4FromFloat32x4BitsJS,
+  'fromUint32x4Bits', Int32x4FromUint32x4BitsJS,
+  'fromInt16x8Bits', Int32x4FromInt16x8BitsJS,
+  'fromUint16x8Bits', Int32x4FromUint16x8BitsJS,
+  'fromInt8x16Bits', Int32x4FromInt8x16BitsJS,
+  'fromUint8x16Bits', Int32x4FromUint8x16BitsJS,
+  'load', Int32x4LoadJS,
+  'load1', Int32x4Load1JS,
+  'load2', Int32x4Load2JS,
+  'load3', Int32x4Load3JS,
+  'store', Int32x4StoreJS,
+  'store1', Int32x4Store1JS,
+  'store2', Int32x4Store2JS,
+  'store3', Int32x4Store3JS,
+]);
+
+utils.InstallFunctions(GlobalUint32x4, DONT_ENUM, [
+  'splat', Uint32x4Splat,
+  'check', Uint32x4CheckJS,
+  'extractLane', Uint32x4ExtractLaneJS,
+  'replaceLane', Uint32x4ReplaceLaneJS,
+  'add', Uint32x4AddJS,
+  'sub', Uint32x4SubJS,
+  'mul', Uint32x4MulJS,
+  'min', Uint32x4MinJS,
+  'max', Uint32x4MaxJS,
+  'and', Uint32x4AndJS,
+  'or', Uint32x4OrJS,
+  'xor', Uint32x4XorJS,
+  'not', Uint32x4NotJS,
+  'shiftLeftByScalar', Uint32x4ShiftLeftByScalarJS,
+  'shiftRightByScalar', Uint32x4ShiftRightByScalarJS,
+  'lessThan', Uint32x4LessThanJS,
+  'lessThanOrEqual', Uint32x4LessThanOrEqualJS,
+  'greaterThan', Uint32x4GreaterThanJS,
+  'greaterThanOrEqual', Uint32x4GreaterThanOrEqualJS,
+  'equal', Uint32x4EqualJS,
+  'notEqual', Uint32x4NotEqualJS,
+  'select', Uint32x4SelectJS,
+  'swizzle', Uint32x4SwizzleJS,
+  'shuffle', Uint32x4ShuffleJS,
+  'fromFloat32x4', Uint32x4FromFloat32x4JS,
+  'fromInt32x4', Uint32x4FromInt32x4JS,
+  'fromFloat32x4Bits', Uint32x4FromFloat32x4BitsJS,
+  'fromInt32x4Bits', Uint32x4FromInt32x4BitsJS,
+  'fromInt16x8Bits', Uint32x4FromInt16x8BitsJS,
+  'fromUint16x8Bits', Uint32x4FromUint16x8BitsJS,
+  'fromInt8x16Bits', Uint32x4FromInt8x16BitsJS,
+  'fromUint8x16Bits', Uint32x4FromUint8x16BitsJS,
+  'load', Uint32x4LoadJS,
+  'load1', Uint32x4Load1JS,
+  'load2', Uint32x4Load2JS,
+  'load3', Uint32x4Load3JS,
+  'store', Uint32x4StoreJS,
+  'store1', Uint32x4Store1JS,
+  'store2', Uint32x4Store2JS,
+  'store3', Uint32x4Store3JS,
+]);
+
+utils.InstallFunctions(GlobalBool32x4, DONT_ENUM, [
+  'splat', Bool32x4Splat,
+  'check', Bool32x4CheckJS,
+  'extractLane', Bool32x4ExtractLaneJS,
+  'replaceLane', Bool32x4ReplaceLaneJS,
+  'and', Bool32x4AndJS,
+  'or', Bool32x4OrJS,
+  'xor', Bool32x4XorJS,
+  'not', Bool32x4NotJS,
+  'anyTrue', Bool32x4AnyTrueJS,
+  'allTrue', Bool32x4AllTrueJS,
+  'swizzle', Bool32x4SwizzleJS,
+  'shuffle', Bool32x4ShuffleJS,
+]);
+
+utils.InstallFunctions(GlobalInt16x8, DONT_ENUM, [
+  'splat', Int16x8Splat,
+  'check', Int16x8CheckJS,
+  'extractLane', Int16x8ExtractLaneJS,
+  'replaceLane', Int16x8ReplaceLaneJS,
+  'neg', Int16x8NegJS,
+  'add', Int16x8AddJS,
+  'sub', Int16x8SubJS,
+  'addSaturate', Int16x8AddSaturateJS,
+  'subSaturate', Int16x8SubSaturateJS,
+  'mul', Int16x8MulJS,
+  'min', Int16x8MinJS,
+  'max', Int16x8MaxJS,
+  'and', Int16x8AndJS,
+  'or', Int16x8OrJS,
+  'xor', Int16x8XorJS,
+  'not', Int16x8NotJS,
+  'shiftLeftByScalar', Int16x8ShiftLeftByScalarJS,
+  'shiftRightByScalar', Int16x8ShiftRightByScalarJS,
+  'lessThan', Int16x8LessThanJS,
+  'lessThanOrEqual', Int16x8LessThanOrEqualJS,
+  'greaterThan', Int16x8GreaterThanJS,
+  'greaterThanOrEqual', Int16x8GreaterThanOrEqualJS,
+  'equal', Int16x8EqualJS,
+  'notEqual', Int16x8NotEqualJS,
+  'select', Int16x8SelectJS,
+  'swizzle', Int16x8SwizzleJS,
+  'shuffle', Int16x8ShuffleJS,
+  'fromUint16x8', Int16x8FromUint16x8JS,
+  'fromFloat32x4Bits', Int16x8FromFloat32x4BitsJS,
+  'fromInt32x4Bits', Int16x8FromInt32x4BitsJS,
+  'fromUint32x4Bits', Int16x8FromUint32x4BitsJS,
+  'fromUint16x8Bits', Int16x8FromUint16x8BitsJS,
+  'fromInt8x16Bits', Int16x8FromInt8x16BitsJS,
+  'fromUint8x16Bits', Int16x8FromUint8x16BitsJS,
+  'load', Int16x8LoadJS,
+  'store', Int16x8StoreJS,
+]);
+
+utils.InstallFunctions(GlobalUint16x8, DONT_ENUM, [
+  'splat', Uint16x8Splat,
+  'check', Uint16x8CheckJS,
+  'extractLane', Uint16x8ExtractLaneJS,
+  'replaceLane', Uint16x8ReplaceLaneJS,
+  'add', Uint16x8AddJS,
+  'sub', Uint16x8SubJS,
+  'addSaturate', Uint16x8AddSaturateJS,
+  'subSaturate', Uint16x8SubSaturateJS,
+  'mul', Uint16x8MulJS,
+  'min', Uint16x8MinJS,
+  'max', Uint16x8MaxJS,
+  'and', Uint16x8AndJS,
+  'or', Uint16x8OrJS,
+  'xor', Uint16x8XorJS,
+  'not', Uint16x8NotJS,
+  'shiftLeftByScalar', Uint16x8ShiftLeftByScalarJS,
+  'shiftRightByScalar', Uint16x8ShiftRightByScalarJS,
+  'lessThan', Uint16x8LessThanJS,
+  'lessThanOrEqual', Uint16x8LessThanOrEqualJS,
+  'greaterThan', Uint16x8GreaterThanJS,
+  'greaterThanOrEqual', Uint16x8GreaterThanOrEqualJS,
+  'equal', Uint16x8EqualJS,
+  'notEqual', Uint16x8NotEqualJS,
+  'select', Uint16x8SelectJS,
+  'swizzle', Uint16x8SwizzleJS,
+  'shuffle', Uint16x8ShuffleJS,
+  'fromInt16x8', Uint16x8FromInt16x8JS,
+  'fromFloat32x4Bits', Uint16x8FromFloat32x4BitsJS,
+  'fromInt32x4Bits', Uint16x8FromInt32x4BitsJS,
+  'fromUint32x4Bits', Uint16x8FromUint32x4BitsJS,
+  'fromInt16x8Bits', Uint16x8FromInt16x8BitsJS,
+  'fromInt8x16Bits', Uint16x8FromInt8x16BitsJS,
+  'fromUint8x16Bits', Uint16x8FromUint8x16BitsJS,
+  'load', Uint16x8LoadJS,
+  'store', Uint16x8StoreJS,
+]);
+
+utils.InstallFunctions(GlobalBool16x8, DONT_ENUM, [
+  'splat', Bool16x8Splat,
+  'check', Bool16x8CheckJS,
+  'extractLane', Bool16x8ExtractLaneJS,
+  'replaceLane', Bool16x8ReplaceLaneJS,
+  'and', Bool16x8AndJS,
+  'or', Bool16x8OrJS,
+  'xor', Bool16x8XorJS,
+  'not', Bool16x8NotJS,
+  'anyTrue', Bool16x8AnyTrueJS,
+  'allTrue', Bool16x8AllTrueJS,
+  'swizzle', Bool16x8SwizzleJS,
+  'shuffle', Bool16x8ShuffleJS,
+]);
+
+utils.InstallFunctions(GlobalInt8x16, DONT_ENUM, [
+  'splat', Int8x16Splat,
+  'check', Int8x16CheckJS,
+  'extractLane', Int8x16ExtractLaneJS,
+  'replaceLane', Int8x16ReplaceLaneJS,
+  'neg', Int8x16NegJS,
+  'add', Int8x16AddJS,
+  'sub', Int8x16SubJS,
+  'addSaturate', Int8x16AddSaturateJS,
+  'subSaturate', Int8x16SubSaturateJS,
+  'mul', Int8x16MulJS,
+  'min', Int8x16MinJS,
+  'max', Int8x16MaxJS,
+  'and', Int8x16AndJS,
+  'or', Int8x16OrJS,
+  'xor', Int8x16XorJS,
+  'not', Int8x16NotJS,
+  'shiftLeftByScalar', Int8x16ShiftLeftByScalarJS,
+  'shiftRightByScalar', Int8x16ShiftRightByScalarJS,
+  'lessThan', Int8x16LessThanJS,
+  'lessThanOrEqual', Int8x16LessThanOrEqualJS,
+  'greaterThan', Int8x16GreaterThanJS,
+  'greaterThanOrEqual', Int8x16GreaterThanOrEqualJS,
+  'equal', Int8x16EqualJS,
+  'notEqual', Int8x16NotEqualJS,
+  'select', Int8x16SelectJS,
+  'swizzle', Int8x16SwizzleJS,
+  'shuffle', Int8x16ShuffleJS,
+  'fromUint8x16', Int8x16FromUint8x16JS,
+  'fromFloat32x4Bits', Int8x16FromFloat32x4BitsJS,
+  'fromInt32x4Bits', Int8x16FromInt32x4BitsJS,
+  'fromUint32x4Bits', Int8x16FromUint32x4BitsJS,
+  'fromInt16x8Bits', Int8x16FromInt16x8BitsJS,
+  'fromUint16x8Bits', Int8x16FromUint16x8BitsJS,
+  'fromUint8x16Bits', Int8x16FromUint8x16BitsJS,
+  'load', Int8x16LoadJS,
+  'store', Int8x16StoreJS,
+]);
+
+utils.InstallFunctions(GlobalUint8x16, DONT_ENUM, [
+  'splat', Uint8x16Splat,
+  'check', Uint8x16CheckJS,
+  'extractLane', Uint8x16ExtractLaneJS,
+  'replaceLane', Uint8x16ReplaceLaneJS,
+  'add', Uint8x16AddJS,
+  'sub', Uint8x16SubJS,
+  'addSaturate', Uint8x16AddSaturateJS,
+  'subSaturate', Uint8x16SubSaturateJS,
+  'mul', Uint8x16MulJS,
+  'min', Uint8x16MinJS,
+  'max', Uint8x16MaxJS,
+  'and', Uint8x16AndJS,
+  'or', Uint8x16OrJS,
+  'xor', Uint8x16XorJS,
+  'not', Uint8x16NotJS,
+  'shiftLeftByScalar', Uint8x16ShiftLeftByScalarJS,
+  'shiftRightByScalar', Uint8x16ShiftRightByScalarJS,
+  'lessThan', Uint8x16LessThanJS,
+  'lessThanOrEqual', Uint8x16LessThanOrEqualJS,
+  'greaterThan', Uint8x16GreaterThanJS,
+  'greaterThanOrEqual', Uint8x16GreaterThanOrEqualJS,
+  'equal', Uint8x16EqualJS,
+  'notEqual', Uint8x16NotEqualJS,
+  'select', Uint8x16SelectJS,
+  'swizzle', Uint8x16SwizzleJS,
+  'shuffle', Uint8x16ShuffleJS,
+  'fromInt8x16', Uint8x16FromInt8x16JS,
+  'fromFloat32x4Bits', Uint8x16FromFloat32x4BitsJS,
+  'fromInt32x4Bits', Uint8x16FromInt32x4BitsJS,
+  'fromUint32x4Bits', Uint8x16FromUint32x4BitsJS,
+  'fromInt16x8Bits', Uint8x16FromInt16x8BitsJS,
+  'fromUint16x8Bits', Uint8x16FromUint16x8BitsJS,
+  'fromInt8x16Bits', Uint8x16FromInt8x16BitsJS,
+  'load', Uint8x16LoadJS,
+  'store', Uint8x16StoreJS,
+]);
+
+utils.InstallFunctions(GlobalBool8x16, DONT_ENUM, [
+  'splat', Bool8x16Splat,
+  'check', Bool8x16CheckJS,
+  'extractLane', Bool8x16ExtractLaneJS,
+  'replaceLane', Bool8x16ReplaceLaneJS,
+  'and', Bool8x16AndJS,
+  'or', Bool8x16OrJS,
+  'xor', Bool8x16XorJS,
+  'not', Bool8x16NotJS,
+  'anyTrue', Bool8x16AnyTrueJS,
+  'allTrue', Bool8x16AllTrueJS,
+  'swizzle', Bool8x16SwizzleJS,
+  'shuffle', Bool8x16ShuffleJS,
+]);
+
+utils.Export(function(to) {
+  to.Float32x4ToString = Float32x4ToString;
+  to.Int32x4ToString = Int32x4ToString;
+  to.Uint32x4ToString = Uint32x4ToString;
+  to.Bool32x4ToString = Bool32x4ToString;
+  to.Int16x8ToString = Int16x8ToString;
+  to.Uint16x8ToString = Uint16x8ToString;
+  to.Bool16x8ToString = Bool16x8ToString;
+  to.Int8x16ToString = Int8x16ToString;
+  to.Uint8x16ToString = Uint8x16ToString;
+  to.Bool8x16ToString = Bool8x16ToString;
+});
+
+})
diff --git a/src/js/harmony-species.js b/src/js/harmony-species.js
new file mode 100644
index 0000000..426ac46
--- /dev/null
+++ b/src/js/harmony-species.js
@@ -0,0 +1,60 @@
+// Copyright 2015 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.
+
+(function(global, utils, extrasUtils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+var GlobalArray = global.Array;
+// It is important that this file is run after src/js/typedarray.js,
+// otherwise GlobalTypedArray would be Object, and we would break
+// old versions of Zepto.
+var GlobalTypedArray = global.Uint8Array.__proto__;
+var GlobalMap = global.Map;
+var GlobalSet = global.Set;
+var GlobalArrayBuffer = global.ArrayBuffer;
+var GlobalPromise = global.Promise;
+var GlobalRegExp = global.RegExp;
+var speciesSymbol = utils.ImportNow("species_symbol");
+
+function ArraySpecies() {
+  return this;
+}
+
+function TypedArraySpecies() {
+  return this;
+}
+
+function MapSpecies() {
+  return this;
+}
+
+function SetSpecies() {
+  return this;
+}
+
+function ArrayBufferSpecies() {
+  return this;
+}
+
+function PromiseSpecies() {
+  return this;
+}
+
+function RegExpSpecies() {
+  return this;
+}
+
+utils.InstallGetter(GlobalArray, speciesSymbol, ArraySpecies, DONT_ENUM);
+utils.InstallGetter(GlobalTypedArray, speciesSymbol, TypedArraySpecies, DONT_ENUM);
+utils.InstallGetter(GlobalMap, speciesSymbol, MapSpecies, DONT_ENUM);
+utils.InstallGetter(GlobalSet, speciesSymbol, SetSpecies, DONT_ENUM);
+utils.InstallGetter(GlobalArrayBuffer, speciesSymbol, ArrayBufferSpecies,
+                    DONT_ENUM);
+utils.InstallGetter(GlobalPromise, speciesSymbol, PromiseSpecies, DONT_ENUM);
+utils.InstallGetter(GlobalRegExp, speciesSymbol, RegExpSpecies, DONT_ENUM);
+
+});
diff --git a/src/js/harmony-unicode-regexps.js b/src/js/harmony-unicode-regexps.js
new file mode 100644
index 0000000..b24bbdf
--- /dev/null
+++ b/src/js/harmony-unicode-regexps.js
@@ -0,0 +1,41 @@
+// 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.
+
+(function(global, utils) {
+
+'use strict';
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalRegExp = global.RegExp;
+var GlobalRegExpPrototype = GlobalRegExp.prototype;
+var MakeTypeError;
+
+utils.Import(function(from) {
+  MakeTypeError = from.MakeTypeError;
+});
+
+// -------------------------------------------------------------------
+
+// ES6 21.2.5.15.
+function RegExpGetUnicode() {
+  if (!IS_REGEXP(this)) {
+    // TODO(littledan): Remove this RegExp compat workaround
+    if (this === GlobalRegExpPrototype) {
+      %IncrementUseCounter(kRegExpPrototypeUnicodeGetter);
+      return UNDEFINED;
+    }
+    throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.unicode");
+  }
+  return !!REGEXP_UNICODE(this);
+}
+%FunctionSetName(RegExpGetUnicode, "RegExp.prototype.unicode");
+%SetNativeFlag(RegExpGetUnicode);
+
+utils.InstallGetter(GlobalRegExp.prototype, 'unicode', RegExpGetUnicode);
+
+})
diff --git a/src/js/i18n.js b/src/js/i18n.js
new file mode 100644
index 0000000..7e00fcd
--- /dev/null
+++ b/src/js/i18n.js
@@ -0,0 +1,2197 @@
+// Copyright 2013 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.
+
+// ECMAScript 402 API implementation.
+
+/**
+ * Intl object is a single object that has some named properties,
+ * all of which are constructors.
+ */
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var ArrayIndexOf;
+var ArrayJoin;
+var ArrayPush;
+var IsFinite;
+var IsNaN;
+var GlobalBoolean = global.Boolean;
+var GlobalDate = global.Date;
+var GlobalNumber = global.Number;
+var GlobalRegExp = global.RegExp;
+var GlobalString = global.String;
+var MakeError;
+var MakeRangeError;
+var MakeTypeError;
+var MathFloor;
+var ObjectDefineProperties = utils.ImportNow("ObjectDefineProperties");
+var ObjectDefineProperty = utils.ImportNow("ObjectDefineProperty");
+var patternSymbol = utils.ImportNow("intl_pattern_symbol");
+var RegExpTest;
+var resolvedSymbol = utils.ImportNow("intl_resolved_symbol");
+var StringIndexOf;
+var StringLastIndexOf;
+var StringMatch;
+var StringReplace;
+var StringSplit;
+var StringSubstr;
+var StringSubstring;
+
+utils.Import(function(from) {
+  ArrayIndexOf = from.ArrayIndexOf;
+  ArrayJoin = from.ArrayJoin;
+  ArrayPush = from.ArrayPush;
+  IsFinite = from.IsFinite;
+  IsNaN = from.IsNaN;
+  MakeError = from.MakeError;
+  MakeRangeError = from.MakeRangeError;
+  MakeTypeError = from.MakeTypeError;
+  MathFloor = from.MathFloor;
+  RegExpTest = from.RegExpTest;
+  StringIndexOf = from.StringIndexOf;
+  StringLastIndexOf = from.StringLastIndexOf;
+  StringMatch = from.StringMatch;
+  StringReplace = from.StringReplace;
+  StringSplit = from.StringSplit;
+  StringSubstr = from.StringSubstr;
+  StringSubstring = from.StringSubstring;
+});
+
+// -------------------------------------------------------------------
+
+var Intl = {};
+
+%AddNamedProperty(global, "Intl", Intl, DONT_ENUM);
+
+/**
+ * Caches available locales for each service.
+ */
+var AVAILABLE_LOCALES = {
+  'collator': UNDEFINED,
+  'numberformat': UNDEFINED,
+  'dateformat': UNDEFINED,
+  'breakiterator': UNDEFINED
+};
+
+/**
+ * Caches default ICU locale.
+ */
+var DEFAULT_ICU_LOCALE = UNDEFINED;
+
+/**
+ * Unicode extension regular expression.
+ */
+var UNICODE_EXTENSION_RE = UNDEFINED;
+
+function GetUnicodeExtensionRE() {
+  if (IS_UNDEFINED(UNDEFINED)) {
+    UNICODE_EXTENSION_RE = new GlobalRegExp('-u(-[a-z0-9]{2,8})+', 'g');
+  }
+  return UNICODE_EXTENSION_RE;
+}
+
+/**
+ * Matches any Unicode extension.
+ */
+var ANY_EXTENSION_RE = UNDEFINED;
+
+function GetAnyExtensionRE() {
+  if (IS_UNDEFINED(ANY_EXTENSION_RE)) {
+    ANY_EXTENSION_RE = new GlobalRegExp('-[a-z0-9]{1}-.*', 'g');
+  }
+  return ANY_EXTENSION_RE;
+}
+
+/**
+ * Replace quoted text (single quote, anything but the quote and quote again).
+ */
+var QUOTED_STRING_RE = UNDEFINED;
+
+function GetQuotedStringRE() {
+  if (IS_UNDEFINED(QUOTED_STRING_RE)) {
+    QUOTED_STRING_RE = new GlobalRegExp("'[^']+'", 'g');
+  }
+  return QUOTED_STRING_RE;
+}
+
+/**
+ * Matches valid service name.
+ */
+var SERVICE_RE = UNDEFINED;
+
+function GetServiceRE() {
+  if (IS_UNDEFINED(SERVICE_RE)) {
+    SERVICE_RE =
+        new GlobalRegExp('^(collator|numberformat|dateformat|breakiterator)$');
+  }
+  return SERVICE_RE;
+}
+
+/**
+ * Validates a language tag against bcp47 spec.
+ * Actual value is assigned on first run.
+ */
+var LANGUAGE_TAG_RE = UNDEFINED;
+
+function GetLanguageTagRE() {
+  if (IS_UNDEFINED(LANGUAGE_TAG_RE)) {
+    BuildLanguageTagREs();
+  }
+  return LANGUAGE_TAG_RE;
+}
+
+/**
+ * Helps find duplicate variants in the language tag.
+ */
+var LANGUAGE_VARIANT_RE = UNDEFINED;
+
+function GetLanguageVariantRE() {
+  if (IS_UNDEFINED(LANGUAGE_VARIANT_RE)) {
+    BuildLanguageTagREs();
+  }
+  return LANGUAGE_VARIANT_RE;
+}
+
+/**
+ * Helps find duplicate singletons in the language tag.
+ */
+var LANGUAGE_SINGLETON_RE = UNDEFINED;
+
+function GetLanguageSingletonRE() {
+  if (IS_UNDEFINED(LANGUAGE_SINGLETON_RE)) {
+    BuildLanguageTagREs();
+  }
+  return LANGUAGE_SINGLETON_RE;
+}
+
+/**
+ * Matches valid IANA time zone names.
+ */
+var TIMEZONE_NAME_CHECK_RE = UNDEFINED;
+
+function GetTimezoneNameCheckRE() {
+  if (IS_UNDEFINED(TIMEZONE_NAME_CHECK_RE)) {
+    TIMEZONE_NAME_CHECK_RE = new GlobalRegExp(
+        '^([A-Za-z]+)/([A-Za-z_-]+)((?:\/[A-Za-z_-]+)+)*$');
+  }
+  return TIMEZONE_NAME_CHECK_RE;
+}
+
+/**
+ * Matches valid location parts of IANA time zone names.
+ */
+var TIMEZONE_NAME_LOCATION_PART_RE = UNDEFINED;
+
+function GetTimezoneNameLocationPartRE() {
+  if (IS_UNDEFINED(TIMEZONE_NAME_LOCATION_PART_RE)) {
+    TIMEZONE_NAME_LOCATION_PART_RE =
+        new GlobalRegExp('^([A-Za-z]+)((?:[_-][A-Za-z]+)+)*$');
+  }
+  return TIMEZONE_NAME_LOCATION_PART_RE;
+}
+
+/**
+ * Adds bound method to the prototype of the given object.
+ */
+function addBoundMethod(obj, methodName, implementation, length) {
+  %CheckIsBootstrapping();
+  function getter() {
+    if (!%IsInitializedIntlObject(this)) {
+      throw MakeTypeError(kMethodCalledOnWrongObject, methodName);
+    }
+    var internalName = '__bound' + methodName + '__';
+    if (IS_UNDEFINED(this[internalName])) {
+      var that = this;
+      var boundMethod;
+      if (IS_UNDEFINED(length) || length === 2) {
+        boundMethod = function(x, y) {
+          if (!IS_UNDEFINED(new.target)) {
+            throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+          }
+          return implementation(that, x, y);
+        }
+      } else if (length === 1) {
+        boundMethod = function(x) {
+          if (!IS_UNDEFINED(new.target)) {
+            throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+          }
+          return implementation(that, x);
+        }
+      } else {
+        boundMethod = function() {
+          if (!IS_UNDEFINED(new.target)) {
+            throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+          }
+          // DateTimeFormat.format needs to be 0 arg method, but can stil
+          // receive optional dateValue param. If one was provided, pass it
+          // along.
+          if (%_ArgumentsLength() > 0) {
+            return implementation(that, %_Arguments(0));
+          } else {
+            return implementation(that);
+          }
+        }
+      }
+      %FunctionSetName(boundMethod, internalName);
+      %FunctionRemovePrototype(boundMethod);
+      %SetNativeFlag(boundMethod);
+      this[internalName] = boundMethod;
+    }
+    return this[internalName];
+  }
+
+  %FunctionSetName(getter, methodName);
+  %FunctionRemovePrototype(getter);
+  %SetNativeFlag(getter);
+
+  ObjectDefineProperty(obj.prototype, methodName, {
+    get: getter,
+    enumerable: false,
+    configurable: true
+  });
+}
+
+
+/**
+ * Returns an intersection of locales and service supported locales.
+ * Parameter locales is treated as a priority list.
+ */
+function supportedLocalesOf(service, locales, options) {
+  if (IS_NULL(%_Call(StringMatch, service, GetServiceRE()))) {
+    throw MakeError(kWrongServiceType, service);
+  }
+
+  // Provide defaults if matcher was not specified.
+  if (IS_UNDEFINED(options)) {
+    options = {};
+  } else {
+    options = TO_OBJECT(options);
+  }
+
+  var matcher = options.localeMatcher;
+  if (!IS_UNDEFINED(matcher)) {
+    matcher = GlobalString(matcher);
+    if (matcher !== 'lookup' && matcher !== 'best fit') {
+      throw MakeRangeError(kLocaleMatcher, matcher);
+    }
+  } else {
+    matcher = 'best fit';
+  }
+
+  var requestedLocales = initializeLocaleList(locales);
+
+  // Cache these, they don't ever change per service.
+  if (IS_UNDEFINED(AVAILABLE_LOCALES[service])) {
+    AVAILABLE_LOCALES[service] = getAvailableLocalesOf(service);
+  }
+
+  // Use either best fit or lookup algorithm to match locales.
+  if (matcher === 'best fit') {
+    return initializeLocaleList(bestFitSupportedLocalesOf(
+        requestedLocales, AVAILABLE_LOCALES[service]));
+  }
+
+  return initializeLocaleList(lookupSupportedLocalesOf(
+      requestedLocales, AVAILABLE_LOCALES[service]));
+}
+
+
+/**
+ * Returns the subset of the provided BCP 47 language priority list for which
+ * this service has a matching locale when using the BCP 47 Lookup algorithm.
+ * Locales appear in the same order in the returned list as in the input list.
+ */
+function lookupSupportedLocalesOf(requestedLocales, availableLocales) {
+  var matchedLocales = [];
+  for (var i = 0; i < requestedLocales.length; ++i) {
+    // Remove -u- extension.
+    var locale = %_Call(StringReplace,
+                        requestedLocales[i],
+                        GetUnicodeExtensionRE(),
+                        '');
+    do {
+      if (!IS_UNDEFINED(availableLocales[locale])) {
+        // Push requested locale not the resolved one.
+        %_Call(ArrayPush, matchedLocales, requestedLocales[i]);
+        break;
+      }
+      // Truncate locale if possible, if not break.
+      var pos = %_Call(StringLastIndexOf, locale, '-');
+      if (pos === -1) {
+        break;
+      }
+      locale = %_Call(StringSubstring, locale, 0, pos);
+    } while (true);
+  }
+
+  return matchedLocales;
+}
+
+
+/**
+ * Returns the subset of the provided BCP 47 language priority list for which
+ * this service has a matching locale when using the implementation
+ * dependent algorithm.
+ * Locales appear in the same order in the returned list as in the input list.
+ */
+function bestFitSupportedLocalesOf(requestedLocales, availableLocales) {
+  // TODO(cira): implement better best fit algorithm.
+  return lookupSupportedLocalesOf(requestedLocales, availableLocales);
+}
+
+
+/**
+ * Returns a getOption function that extracts property value for given
+ * options object. If property is missing it returns defaultValue. If value
+ * is out of range for that property it throws RangeError.
+ */
+function getGetOption(options, caller) {
+  if (IS_UNDEFINED(options)) throw MakeError(kDefaultOptionsMissing, caller);
+
+  var getOption = function getOption(property, type, values, defaultValue) {
+    if (!IS_UNDEFINED(options[property])) {
+      var value = options[property];
+      switch (type) {
+        case 'boolean':
+          value = GlobalBoolean(value);
+          break;
+        case 'string':
+          value = GlobalString(value);
+          break;
+        case 'number':
+          value = GlobalNumber(value);
+          break;
+        default:
+          throw MakeError(kWrongValueType);
+      }
+
+      if (!IS_UNDEFINED(values) && %_Call(ArrayIndexOf, values, value) === -1) {
+        throw MakeRangeError(kValueOutOfRange, value, caller, property);
+      }
+
+      return value;
+    }
+
+    return defaultValue;
+  }
+
+  return getOption;
+}
+
+
+/**
+ * Compares a BCP 47 language priority list requestedLocales against the locales
+ * in availableLocales and determines the best available language to meet the
+ * request. Two algorithms are available to match the locales: the Lookup
+ * algorithm described in RFC 4647 section 3.4, and an implementation dependent
+ * best-fit algorithm. Independent of the locale matching algorithm, options
+ * specified through Unicode locale extension sequences are negotiated
+ * separately, taking the caller's relevant extension keys and locale data as
+ * well as client-provided options into consideration. Returns an object with
+ * a locale property whose value is the language tag of the selected locale,
+ * and properties for each key in relevantExtensionKeys providing the selected
+ * value for that key.
+ */
+function resolveLocale(service, requestedLocales, options) {
+  requestedLocales = initializeLocaleList(requestedLocales);
+
+  var getOption = getGetOption(options, service);
+  var matcher = getOption('localeMatcher', 'string',
+                          ['lookup', 'best fit'], 'best fit');
+  var resolved;
+  if (matcher === 'lookup') {
+    resolved = lookupMatcher(service, requestedLocales);
+  } else {
+    resolved = bestFitMatcher(service, requestedLocales);
+  }
+
+  return resolved;
+}
+
+
+/**
+ * Returns best matched supported locale and extension info using basic
+ * lookup algorithm.
+ */
+function lookupMatcher(service, requestedLocales) {
+  if (IS_NULL(%_Call(StringMatch, service, GetServiceRE()))) {
+    throw MakeError(kWrongServiceType, service);
+  }
+
+  // Cache these, they don't ever change per service.
+  if (IS_UNDEFINED(AVAILABLE_LOCALES[service])) {
+    AVAILABLE_LOCALES[service] = getAvailableLocalesOf(service);
+  }
+
+  for (var i = 0; i < requestedLocales.length; ++i) {
+    // Remove all extensions.
+    var locale = %_Call(StringReplace, requestedLocales[i],
+                        GetAnyExtensionRE(), '');
+    do {
+      if (!IS_UNDEFINED(AVAILABLE_LOCALES[service][locale])) {
+        // Return the resolved locale and extension.
+        var extensionMatch =
+            %_Call(StringMatch, requestedLocales[i], GetUnicodeExtensionRE());
+        var extension = IS_NULL(extensionMatch) ? '' : extensionMatch[0];
+        return {'locale': locale, 'extension': extension, 'position': i};
+      }
+      // Truncate locale if possible.
+      var pos = %_Call(StringLastIndexOf, locale, '-');
+      if (pos === -1) {
+        break;
+      }
+      locale = %_Call(StringSubstring, locale, 0, pos);
+    } while (true);
+  }
+
+  // Didn't find a match, return default.
+  if (IS_UNDEFINED(DEFAULT_ICU_LOCALE)) {
+    DEFAULT_ICU_LOCALE = %GetDefaultICULocale();
+  }
+
+  return {'locale': DEFAULT_ICU_LOCALE, 'extension': '', 'position': -1};
+}
+
+
+/**
+ * Returns best matched supported locale and extension info using
+ * implementation dependend algorithm.
+ */
+function bestFitMatcher(service, requestedLocales) {
+  // TODO(cira): implement better best fit algorithm.
+  return lookupMatcher(service, requestedLocales);
+}
+
+
+/**
+ * Parses Unicode extension into key - value map.
+ * Returns empty object if the extension string is invalid.
+ * We are not concerned with the validity of the values at this point.
+ */
+function parseExtension(extension) {
+  var extensionSplit = %_Call(StringSplit, extension, '-');
+
+  // Assume ['', 'u', ...] input, but don't throw.
+  if (extensionSplit.length <= 2 ||
+      (extensionSplit[0] !== '' && extensionSplit[1] !== 'u')) {
+    return {};
+  }
+
+  // Key is {2}alphanum, value is {3,8}alphanum.
+  // Some keys may not have explicit values (booleans).
+  var extensionMap = {};
+  var previousKey = UNDEFINED;
+  for (var i = 2; i < extensionSplit.length; ++i) {
+    var length = extensionSplit[i].length;
+    var element = extensionSplit[i];
+    if (length === 2) {
+      extensionMap[element] = UNDEFINED;
+      previousKey = element;
+    } else if (length >= 3 && length <=8 && !IS_UNDEFINED(previousKey)) {
+      extensionMap[previousKey] = element;
+      previousKey = UNDEFINED;
+    } else {
+      // There is a value that's too long, or that doesn't have a key.
+      return {};
+    }
+  }
+
+  return extensionMap;
+}
+
+
+/**
+ * Populates internalOptions object with boolean key-value pairs
+ * from extensionMap and options.
+ * Returns filtered extension (number and date format constructors use
+ * Unicode extensions for passing parameters to ICU).
+ * It's used for extension-option pairs only, e.g. kn-normalization, but not
+ * for 'sensitivity' since it doesn't have extension equivalent.
+ * Extensions like nu and ca don't have options equivalent, so we place
+ * undefined in the map.property to denote that.
+ */
+function setOptions(inOptions, extensionMap, keyValues, getOption, outOptions) {
+  var extension = '';
+
+  var updateExtension = function updateExtension(key, value) {
+    return '-' + key + '-' + GlobalString(value);
+  }
+
+  var updateProperty = function updateProperty(property, type, value) {
+    if (type === 'boolean' && (typeof value === 'string')) {
+      value = (value === 'true') ? true : false;
+    }
+
+    if (!IS_UNDEFINED(property)) {
+      defineWEProperty(outOptions, property, value);
+    }
+  }
+
+  for (var key in keyValues) {
+    if (%HasOwnProperty(keyValues, key)) {
+      var value = UNDEFINED;
+      var map = keyValues[key];
+      if (!IS_UNDEFINED(map.property)) {
+        // This may return true if user specifies numeric: 'false', since
+        // Boolean('nonempty') === true.
+        value = getOption(map.property, map.type, map.values);
+      }
+      if (!IS_UNDEFINED(value)) {
+        updateProperty(map.property, map.type, value);
+        extension += updateExtension(key, value);
+        continue;
+      }
+      // User options didn't have it, check Unicode extension.
+      // Here we want to convert strings 'true', 'false' into proper Boolean
+      // values (not a user error).
+      if (%HasOwnProperty(extensionMap, key)) {
+        value = extensionMap[key];
+        if (!IS_UNDEFINED(value)) {
+          updateProperty(map.property, map.type, value);
+          extension += updateExtension(key, value);
+        } else if (map.type === 'boolean') {
+          // Boolean keys are allowed not to have values in Unicode extension.
+          // Those default to true.
+          updateProperty(map.property, map.type, true);
+          extension += updateExtension(key, true);
+        }
+      }
+    }
+  }
+
+  return extension === ''? '' : '-u' + extension;
+}
+
+
+/**
+ * Converts all OwnProperties into
+ * configurable: false, writable: false, enumerable: true.
+ */
+function freezeArray(array) {
+  var l = array.length;
+  for (var i = 0; i < l; i++) {
+    if (i in array) {
+      ObjectDefineProperty(array, i, {value: array[i],
+                                      configurable: false,
+                                      writable: false,
+                                      enumerable: true});
+    }
+  }
+
+  ObjectDefineProperty(array, 'length', {value: l, writable: false});
+  return array;
+}
+
+
+/**
+ * It's sometimes desireable to leave user requested locale instead of ICU
+ * supported one (zh-TW is equivalent to zh-Hant-TW, so we should keep shorter
+ * one, if that was what user requested).
+ * This function returns user specified tag if its maximized form matches ICU
+ * resolved locale. If not we return ICU result.
+ */
+function getOptimalLanguageTag(original, resolved) {
+  // Returns Array<Object>, where each object has maximized and base properties.
+  // Maximized: zh -> zh-Hans-CN
+  // Base: zh-CN-u-ca-gregory -> zh-CN
+  // Take care of grandfathered or simple cases.
+  if (original === resolved) {
+    return original;
+  }
+
+  var locales = %GetLanguageTagVariants([original, resolved]);
+  if (locales[0].maximized !== locales[1].maximized) {
+    return resolved;
+  }
+
+  // Preserve extensions of resolved locale, but swap base tags with original.
+  var resolvedBase = new GlobalRegExp('^' + locales[1].base);
+  return %_Call(StringReplace, resolved, resolvedBase, locales[0].base);
+}
+
+
+/**
+ * Returns an Object that contains all of supported locales for a given
+ * service.
+ * In addition to the supported locales we add xx-ZZ locale for each xx-Yyyy-ZZ
+ * that is supported. This is required by the spec.
+ */
+function getAvailableLocalesOf(service) {
+  var available = %AvailableLocalesOf(service);
+
+  for (var i in available) {
+    if (%HasOwnProperty(available, i)) {
+      var parts =
+          %_Call(StringMatch, i, /^([a-z]{2,3})-([A-Z][a-z]{3})-([A-Z]{2})$/);
+      if (parts !== null) {
+        // Build xx-ZZ. We don't care about the actual value,
+        // as long it's not undefined.
+        available[parts[1] + '-' + parts[3]] = null;
+      }
+    }
+  }
+
+  return available;
+}
+
+
+/**
+ * Defines a property and sets writable and enumerable to true.
+ * Configurable is false by default.
+ */
+function defineWEProperty(object, property, value) {
+  ObjectDefineProperty(object, property,
+                       {value: value, writable: true, enumerable: true});
+}
+
+
+/**
+ * Adds property to an object if the value is not undefined.
+ * Sets configurable descriptor to false.
+ */
+function addWEPropertyIfDefined(object, property, value) {
+  if (!IS_UNDEFINED(value)) {
+    defineWEProperty(object, property, value);
+  }
+}
+
+
+/**
+ * Defines a property and sets writable, enumerable and configurable to true.
+ */
+function defineWECProperty(object, property, value) {
+  ObjectDefineProperty(object, property, {value: value,
+                                          writable: true,
+                                          enumerable: true,
+                                          configurable: true});
+}
+
+
+/**
+ * Adds property to an object if the value is not undefined.
+ * Sets all descriptors to true.
+ */
+function addWECPropertyIfDefined(object, property, value) {
+  if (!IS_UNDEFINED(value)) {
+    defineWECProperty(object, property, value);
+  }
+}
+
+
+/**
+ * Returns titlecased word, aMeRricA -> America.
+ */
+function toTitleCaseWord(word) {
+  return %StringToUpperCase(%_Call(StringSubstr, word, 0, 1)) +
+         %StringToLowerCase(%_Call(StringSubstr, word, 1));
+}
+
+/**
+ * Returns titlecased location, bueNos_airES -> Buenos_Aires
+ * or ho_cHi_minH -> Ho_Chi_Minh. It is locale-agnostic and only
+ * deals with ASCII only characters.
+ * 'of', 'au' and 'es' are special-cased and lowercased.
+ */
+function toTitleCaseTimezoneLocation(location) {
+  var match = %_Call(StringMatch, location, GetTimezoneNameLocationPartRE());
+  if (IS_NULL(match)) throw MakeRangeError(kExpectedLocation, location);
+
+  var result = toTitleCaseWord(match[1]);
+  if (!IS_UNDEFINED(match[2]) && 2 < match.length) {
+    // The first character is a separator, '_' or '-'.
+    // None of IANA zone names has both '_' and '-'.
+    var separator = %_Call(StringSubstring, match[2], 0, 1);
+    var parts = %_Call(StringSplit, match[2], separator);
+    for (var i = 1; i < parts.length; i++) {
+      var part = parts[i]
+      var lowercasedPart = %StringToLowerCase(part);
+      result = result + separator +
+          ((lowercasedPart !== 'es' &&
+            lowercasedPart !== 'of' && lowercasedPart !== 'au') ?
+          toTitleCaseWord(part) : lowercasedPart);
+    }
+  }
+  return result;
+}
+
+/**
+ * Canonicalizes the language tag, or throws in case the tag is invalid.
+ */
+function canonicalizeLanguageTag(localeID) {
+  // null is typeof 'object' so we have to do extra check.
+  if (typeof localeID !== 'string' && typeof localeID !== 'object' ||
+      IS_NULL(localeID)) {
+    throw MakeTypeError(kLanguageID);
+  }
+
+  var localeString = GlobalString(localeID);
+
+  if (isValidLanguageTag(localeString) === false) {
+    throw MakeRangeError(kInvalidLanguageTag, localeString);
+  }
+
+  // This call will strip -kn but not -kn-true extensions.
+  // ICU bug filled - http://bugs.icu-project.org/trac/ticket/9265.
+  // TODO(cira): check if -u-kn-true-kc-true-kh-true still throws after
+  // upgrade to ICU 4.9.
+  var tag = %CanonicalizeLanguageTag(localeString);
+  if (tag === 'invalid-tag') {
+    throw MakeRangeError(kInvalidLanguageTag, localeString);
+  }
+
+  return tag;
+}
+
+
+/**
+ * Returns an array where all locales are canonicalized and duplicates removed.
+ * Throws on locales that are not well formed BCP47 tags.
+ */
+function initializeLocaleList(locales) {
+  var seen = [];
+  if (IS_UNDEFINED(locales)) {
+    // Constructor is called without arguments.
+    seen = [];
+  } else {
+    // We allow single string localeID.
+    if (typeof locales === 'string') {
+      %_Call(ArrayPush, seen, canonicalizeLanguageTag(locales));
+      return freezeArray(seen);
+    }
+
+    var o = TO_OBJECT(locales);
+    var len = TO_UINT32(o.length);
+
+    for (var k = 0; k < len; k++) {
+      if (k in o) {
+        var value = o[k];
+
+        var tag = canonicalizeLanguageTag(value);
+
+        if (%_Call(ArrayIndexOf, seen, tag) === -1) {
+          %_Call(ArrayPush, seen, tag);
+        }
+      }
+    }
+  }
+
+  return freezeArray(seen);
+}
+
+
+/**
+ * Validates the language tag. Section 2.2.9 of the bcp47 spec
+ * defines a valid tag.
+ *
+ * ICU is too permissible and lets invalid tags, like
+ * hant-cmn-cn, through.
+ *
+ * Returns false if the language tag is invalid.
+ */
+function isValidLanguageTag(locale) {
+  // Check if it's well-formed, including grandfadered tags.
+  if (!%_Call(RegExpTest, GetLanguageTagRE(), locale)) {
+    return false;
+  }
+
+  // Just return if it's a x- form. It's all private.
+  if (%_Call(StringIndexOf, locale, 'x-') === 0) {
+    return true;
+  }
+
+  // Check if there are any duplicate variants or singletons (extensions).
+
+  // Remove private use section.
+  locale = %_Call(StringSplit, locale, /-x-/)[0];
+
+  // Skip language since it can match variant regex, so we start from 1.
+  // We are matching i-klingon here, but that's ok, since i-klingon-klingon
+  // is not valid and would fail LANGUAGE_TAG_RE test.
+  var variants = [];
+  var extensions = [];
+  var parts = %_Call(StringSplit, locale, /-/);
+  for (var i = 1; i < parts.length; i++) {
+    var value = parts[i];
+    if (%_Call(RegExpTest, GetLanguageVariantRE(), value) &&
+        extensions.length === 0) {
+      if (%_Call(ArrayIndexOf, variants, value) === -1) {
+        %_Call(ArrayPush, variants, value);
+      } else {
+        return false;
+      }
+    }
+
+    if (%_Call(RegExpTest, GetLanguageSingletonRE(), value)) {
+      if (%_Call(ArrayIndexOf, extensions, value) === -1) {
+        %_Call(ArrayPush, extensions, value);
+      } else {
+        return false;
+      }
+    }
+  }
+
+  return true;
+ }
+
+
+/**
+ * Builds a regular expresion that validates the language tag
+ * against bcp47 spec.
+ * Uses http://tools.ietf.org/html/bcp47, section 2.1, ABNF.
+ * Runs on load and initializes the global REs.
+ */
+function BuildLanguageTagREs() {
+  var alpha = '[a-zA-Z]';
+  var digit = '[0-9]';
+  var alphanum = '(' + alpha + '|' + digit + ')';
+  var regular = '(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|' +
+                'zh-min|zh-min-nan|zh-xiang)';
+  var irregular = '(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|' +
+                  'i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|' +
+                  'i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)';
+  var grandfathered = '(' + irregular + '|' + regular + ')';
+  var privateUse = '(x(-' + alphanum + '{1,8})+)';
+
+  var singleton = '(' + digit + '|[A-WY-Za-wy-z])';
+  LANGUAGE_SINGLETON_RE = new GlobalRegExp('^' + singleton + '$', 'i');
+
+  var extension = '(' + singleton + '(-' + alphanum + '{2,8})+)';
+
+  var variant = '(' + alphanum + '{5,8}|(' + digit + alphanum + '{3}))';
+  LANGUAGE_VARIANT_RE = new GlobalRegExp('^' + variant + '$', 'i');
+
+  var region = '(' + alpha + '{2}|' + digit + '{3})';
+  var script = '(' + alpha + '{4})';
+  var extLang = '(' + alpha + '{3}(-' + alpha + '{3}){0,2})';
+  var language = '(' + alpha + '{2,3}(-' + extLang + ')?|' + alpha + '{4}|' +
+                 alpha + '{5,8})';
+  var langTag = language + '(-' + script + ')?(-' + region + ')?(-' +
+                variant + ')*(-' + extension + ')*(-' + privateUse + ')?';
+
+  var languageTag =
+      '^(' + langTag + '|' + privateUse + '|' + grandfathered + ')$';
+  LANGUAGE_TAG_RE = new GlobalRegExp(languageTag, 'i');
+}
+
+var resolvedAccessor = {
+  get() {
+    %IncrementUseCounter(kIntlResolved);
+    return this[resolvedSymbol];
+  },
+  set(value) {
+    this[resolvedSymbol] = value;
+  }
+};
+
+/**
+ * Initializes the given object so it's a valid Collator instance.
+ * Useful for subclassing.
+ */
+function initializeCollator(collator, locales, options) {
+  if (%IsInitializedIntlObject(collator)) {
+    throw MakeTypeError(kReinitializeIntl, "Collator");
+  }
+
+  if (IS_UNDEFINED(options)) {
+    options = {};
+  }
+
+  var getOption = getGetOption(options, 'collator');
+
+  var internalOptions = {};
+
+  defineWEProperty(internalOptions, 'usage', getOption(
+    'usage', 'string', ['sort', 'search'], 'sort'));
+
+  var sensitivity = getOption('sensitivity', 'string',
+                              ['base', 'accent', 'case', 'variant']);
+  if (IS_UNDEFINED(sensitivity) && internalOptions.usage === 'sort') {
+    sensitivity = 'variant';
+  }
+  defineWEProperty(internalOptions, 'sensitivity', sensitivity);
+
+  defineWEProperty(internalOptions, 'ignorePunctuation', getOption(
+    'ignorePunctuation', 'boolean', UNDEFINED, false));
+
+  var locale = resolveLocale('collator', locales, options);
+
+  // ICU can't take kb, kc... parameters through localeID, so we need to pass
+  // them as options.
+  // One exception is -co- which has to be part of the extension, but only for
+  // usage: sort, and its value can't be 'standard' or 'search'.
+  var extensionMap = parseExtension(locale.extension);
+
+  /**
+   * Map of Unicode extensions to option properties, and their values and types,
+   * for a collator.
+   */
+  var COLLATOR_KEY_MAP = {
+    'kn': {'property': 'numeric', 'type': 'boolean'},
+    'kf': {'property': 'caseFirst', 'type': 'string',
+           'values': ['false', 'lower', 'upper']}
+  };
+
+  setOptions(
+      options, extensionMap, COLLATOR_KEY_MAP, getOption, internalOptions);
+
+  var collation = 'default';
+  var extension = '';
+  if (%HasOwnProperty(extensionMap, 'co') && internalOptions.usage === 'sort') {
+
+    /**
+     * Allowed -u-co- values. List taken from:
+     * http://unicode.org/repos/cldr/trunk/common/bcp47/collation.xml
+     */
+    var ALLOWED_CO_VALUES = [
+      'big5han', 'dict', 'direct', 'ducet', 'gb2312', 'phonebk', 'phonetic',
+      'pinyin', 'reformed', 'searchjl', 'stroke', 'trad', 'unihan', 'zhuyin'
+    ];
+
+    if (%_Call(ArrayIndexOf, ALLOWED_CO_VALUES, extensionMap.co) !== -1) {
+      extension = '-u-co-' + extensionMap.co;
+      // ICU can't tell us what the collation is, so save user's input.
+      collation = extensionMap.co;
+    }
+  } else if (internalOptions.usage === 'search') {
+    extension = '-u-co-search';
+  }
+  defineWEProperty(internalOptions, 'collation', collation);
+
+  var requestedLocale = locale.locale + extension;
+
+  // We define all properties C++ code may produce, to prevent security
+  // problems. If malicious user decides to redefine Object.prototype.locale
+  // we can't just use plain x.locale = 'us' or in C++ Set("locale", "us").
+  // ObjectDefineProperties will either succeed defining or throw an error.
+  var resolved = ObjectDefineProperties({}, {
+    caseFirst: {writable: true},
+    collation: {value: internalOptions.collation, writable: true},
+    ignorePunctuation: {writable: true},
+    locale: {writable: true},
+    numeric: {writable: true},
+    requestedLocale: {value: requestedLocale, writable: true},
+    sensitivity: {writable: true},
+    strength: {writable: true},
+    usage: {value: internalOptions.usage, writable: true}
+  });
+
+  var internalCollator = %CreateCollator(requestedLocale,
+                                         internalOptions,
+                                         resolved);
+
+  // Writable, configurable and enumerable are set to false by default.
+  %MarkAsInitializedIntlObjectOfType(collator, 'collator', internalCollator);
+  collator[resolvedSymbol] = resolved;
+  ObjectDefineProperty(collator, 'resolved', resolvedAccessor);
+
+  return collator;
+}
+
+
+/**
+ * Constructs Intl.Collator object given optional locales and options
+ * parameters.
+ *
+ * @constructor
+ */
+%AddNamedProperty(Intl, 'Collator', function() {
+    var locales = %_Arguments(0);
+    var options = %_Arguments(1);
+
+    if (!this || this === Intl) {
+      // Constructor is called as a function.
+      return new Intl.Collator(locales, options);
+    }
+
+    return initializeCollator(TO_OBJECT(this), locales, options);
+  },
+  DONT_ENUM
+);
+
+
+/**
+ * Collator resolvedOptions method.
+ */
+%AddNamedProperty(Intl.Collator.prototype, 'resolvedOptions', function() {
+    if (!IS_UNDEFINED(new.target)) {
+      throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+    }
+
+    if (!%IsInitializedIntlObjectOfType(this, 'collator')) {
+      throw MakeTypeError(kResolvedOptionsCalledOnNonObject, "Collator");
+    }
+
+    var coll = this;
+    var locale = getOptimalLanguageTag(coll[resolvedSymbol].requestedLocale,
+                                       coll[resolvedSymbol].locale);
+
+    return {
+      locale: locale,
+      usage: coll[resolvedSymbol].usage,
+      sensitivity: coll[resolvedSymbol].sensitivity,
+      ignorePunctuation: coll[resolvedSymbol].ignorePunctuation,
+      numeric: coll[resolvedSymbol].numeric,
+      caseFirst: coll[resolvedSymbol].caseFirst,
+      collation: coll[resolvedSymbol].collation
+    };
+  },
+  DONT_ENUM
+);
+%FunctionSetName(Intl.Collator.prototype.resolvedOptions, 'resolvedOptions');
+%FunctionRemovePrototype(Intl.Collator.prototype.resolvedOptions);
+%SetNativeFlag(Intl.Collator.prototype.resolvedOptions);
+
+
+/**
+ * Returns the subset of the given locale list for which this locale list
+ * has a matching (possibly fallback) locale. Locales appear in the same
+ * order in the returned list as in the input list.
+ * Options are optional parameter.
+ */
+%AddNamedProperty(Intl.Collator, 'supportedLocalesOf', function(locales) {
+    if (!IS_UNDEFINED(new.target)) {
+      throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+    }
+
+    return supportedLocalesOf('collator', locales, %_Arguments(1));
+  },
+  DONT_ENUM
+);
+%FunctionSetName(Intl.Collator.supportedLocalesOf, 'supportedLocalesOf');
+%FunctionRemovePrototype(Intl.Collator.supportedLocalesOf);
+%SetNativeFlag(Intl.Collator.supportedLocalesOf);
+
+
+/**
+ * When the compare method is called with two arguments x and y, it returns a
+ * Number other than NaN that represents the result of a locale-sensitive
+ * String comparison of x with y.
+ * The result is intended to order String values in the sort order specified
+ * by the effective locale and collation options computed during construction
+ * of this Collator object, and will be negative, zero, or positive, depending
+ * on whether x comes before y in the sort order, the Strings are equal under
+ * the sort order, or x comes after y in the sort order, respectively.
+ */
+function compare(collator, x, y) {
+  return %InternalCompare(%GetImplFromInitializedIntlObject(collator),
+                          GlobalString(x), GlobalString(y));
+};
+
+
+addBoundMethod(Intl.Collator, 'compare', compare, 2);
+
+/**
+ * Verifies that the input is a well-formed ISO 4217 currency code.
+ * Don't uppercase to test. It could convert invalid code into a valid one.
+ * For example \u00DFP (Eszett+P) becomes SSP.
+ */
+function isWellFormedCurrencyCode(currency) {
+  return typeof currency == "string" &&
+      currency.length == 3 &&
+      %_Call(StringMatch, currency, /[^A-Za-z]/) == null;
+}
+
+
+/**
+ * Returns the valid digit count for a property, or throws RangeError on
+ * a value out of the range.
+ */
+function getNumberOption(options, property, min, max, fallback) {
+  var value = options[property];
+  if (!IS_UNDEFINED(value)) {
+    value = GlobalNumber(value);
+    if (IsNaN(value) || value < min || value > max) {
+      throw MakeRangeError(kPropertyValueOutOfRange, property);
+    }
+    return MathFloor(value);
+  }
+
+  return fallback;
+}
+
+var patternAccessor = {
+  get() {
+    %IncrementUseCounter(kIntlPattern);
+    return this[patternSymbol];
+  },
+  set(value) {
+    this[patternSymbol] = value;
+  }
+};
+
+/**
+ * Initializes the given object so it's a valid NumberFormat instance.
+ * Useful for subclassing.
+ */
+function initializeNumberFormat(numberFormat, locales, options) {
+  if (%IsInitializedIntlObject(numberFormat)) {
+    throw MakeTypeError(kReinitializeIntl, "NumberFormat");
+  }
+
+  if (IS_UNDEFINED(options)) {
+    options = {};
+  }
+
+  var getOption = getGetOption(options, 'numberformat');
+
+  var locale = resolveLocale('numberformat', locales, options);
+
+  var internalOptions = {};
+  defineWEProperty(internalOptions, 'style', getOption(
+    'style', 'string', ['decimal', 'percent', 'currency'], 'decimal'));
+
+  var currency = getOption('currency', 'string');
+  if (!IS_UNDEFINED(currency) && !isWellFormedCurrencyCode(currency)) {
+    throw MakeRangeError(kInvalidCurrencyCode, currency);
+  }
+
+  if (internalOptions.style === 'currency' && IS_UNDEFINED(currency)) {
+    throw MakeTypeError(kCurrencyCode);
+  }
+
+  var currencyDisplay = getOption(
+      'currencyDisplay', 'string', ['code', 'symbol', 'name'], 'symbol');
+  if (internalOptions.style === 'currency') {
+    defineWEProperty(internalOptions, 'currency', %StringToUpperCase(currency));
+    defineWEProperty(internalOptions, 'currencyDisplay', currencyDisplay);
+  }
+
+  // Digit ranges.
+  var mnid = getNumberOption(options, 'minimumIntegerDigits', 1, 21, 1);
+  defineWEProperty(internalOptions, 'minimumIntegerDigits', mnid);
+
+  var mnfd = options['minimumFractionDigits'];
+  var mxfd = options['maximumFractionDigits'];
+  if (!IS_UNDEFINED(mnfd) || internalOptions.style !== 'currency') {
+    mnfd = getNumberOption(options, 'minimumFractionDigits', 0, 20, 0);
+    defineWEProperty(internalOptions, 'minimumFractionDigits', mnfd);
+  }
+
+  if (!IS_UNDEFINED(mxfd) || internalOptions.style !== 'currency') {
+    var min_mxfd = internalOptions.style === 'percent' ? 0 : 3;
+    mnfd = IS_UNDEFINED(mnfd) ? 0 : mnfd;
+    var fallback_limit = (mnfd > min_mxfd) ? mnfd : min_mxfd;
+    mxfd = getNumberOption(options, 'maximumFractionDigits', mnfd, 20, fallback_limit);
+    defineWEProperty(internalOptions, 'maximumFractionDigits', mxfd);
+  }
+
+  var mnsd = options['minimumSignificantDigits'];
+  var mxsd = options['maximumSignificantDigits'];
+  if (!IS_UNDEFINED(mnsd) || !IS_UNDEFINED(mxsd)) {
+    mnsd = getNumberOption(options, 'minimumSignificantDigits', 1, 21, 0);
+    defineWEProperty(internalOptions, 'minimumSignificantDigits', mnsd);
+
+    mxsd = getNumberOption(options, 'maximumSignificantDigits', mnsd, 21, 21);
+    defineWEProperty(internalOptions, 'maximumSignificantDigits', mxsd);
+  }
+
+  // Grouping.
+  defineWEProperty(internalOptions, 'useGrouping', getOption(
+    'useGrouping', 'boolean', UNDEFINED, true));
+
+  // ICU prefers options to be passed using -u- extension key/values for
+  // number format, so we need to build that.
+  var extensionMap = parseExtension(locale.extension);
+
+  /**
+   * Map of Unicode extensions to option properties, and their values and types,
+   * for a number format.
+   */
+  var NUMBER_FORMAT_KEY_MAP = {
+    'nu': {'property': UNDEFINED, 'type': 'string'}
+  };
+
+  var extension = setOptions(options, extensionMap, NUMBER_FORMAT_KEY_MAP,
+                             getOption, internalOptions);
+
+  var requestedLocale = locale.locale + extension;
+  var resolved = ObjectDefineProperties({}, {
+    currency: {writable: true},
+    currencyDisplay: {writable: true},
+    locale: {writable: true},
+    maximumFractionDigits: {writable: true},
+    minimumFractionDigits: {writable: true},
+    minimumIntegerDigits: {writable: true},
+    numberingSystem: {writable: true},
+    pattern: patternAccessor,
+    requestedLocale: {value: requestedLocale, writable: true},
+    style: {value: internalOptions.style, writable: true},
+    useGrouping: {writable: true}
+  });
+  if (%HasOwnProperty(internalOptions, 'minimumSignificantDigits')) {
+    defineWEProperty(resolved, 'minimumSignificantDigits', UNDEFINED);
+  }
+  if (%HasOwnProperty(internalOptions, 'maximumSignificantDigits')) {
+    defineWEProperty(resolved, 'maximumSignificantDigits', UNDEFINED);
+  }
+  var formatter = %CreateNumberFormat(requestedLocale,
+                                      internalOptions,
+                                      resolved);
+
+  if (internalOptions.style === 'currency') {
+    ObjectDefineProperty(resolved, 'currencyDisplay', {value: currencyDisplay,
+                                                       writable: true});
+  }
+
+  %MarkAsInitializedIntlObjectOfType(numberFormat, 'numberformat', formatter);
+  numberFormat[resolvedSymbol] = resolved;
+  ObjectDefineProperty(numberFormat, 'resolved', resolvedAccessor);
+
+  return numberFormat;
+}
+
+
+/**
+ * Constructs Intl.NumberFormat object given optional locales and options
+ * parameters.
+ *
+ * @constructor
+ */
+%AddNamedProperty(Intl, 'NumberFormat', function() {
+    var locales = %_Arguments(0);
+    var options = %_Arguments(1);
+
+    if (!this || this === Intl) {
+      // Constructor is called as a function.
+      return new Intl.NumberFormat(locales, options);
+    }
+
+    return initializeNumberFormat(TO_OBJECT(this), locales, options);
+  },
+  DONT_ENUM
+);
+
+
+/**
+ * NumberFormat resolvedOptions method.
+ */
+%AddNamedProperty(Intl.NumberFormat.prototype, 'resolvedOptions', function() {
+    if (!IS_UNDEFINED(new.target)) {
+      throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+    }
+
+    if (!%IsInitializedIntlObjectOfType(this, 'numberformat')) {
+      throw MakeTypeError(kResolvedOptionsCalledOnNonObject, "NumberFormat");
+    }
+
+    var format = this;
+    var locale = getOptimalLanguageTag(format[resolvedSymbol].requestedLocale,
+                                       format[resolvedSymbol].locale);
+
+    var result = {
+      locale: locale,
+      numberingSystem: format[resolvedSymbol].numberingSystem,
+      style: format[resolvedSymbol].style,
+      useGrouping: format[resolvedSymbol].useGrouping,
+      minimumIntegerDigits: format[resolvedSymbol].minimumIntegerDigits,
+      minimumFractionDigits: format[resolvedSymbol].minimumFractionDigits,
+      maximumFractionDigits: format[resolvedSymbol].maximumFractionDigits,
+    };
+
+    if (result.style === 'currency') {
+      defineWECProperty(result, 'currency', format[resolvedSymbol].currency);
+      defineWECProperty(result, 'currencyDisplay',
+                        format[resolvedSymbol].currencyDisplay);
+    }
+
+    if (%HasOwnProperty(format[resolvedSymbol], 'minimumSignificantDigits')) {
+      defineWECProperty(result, 'minimumSignificantDigits',
+                        format[resolvedSymbol].minimumSignificantDigits);
+    }
+
+    if (%HasOwnProperty(format[resolvedSymbol], 'maximumSignificantDigits')) {
+      defineWECProperty(result, 'maximumSignificantDigits',
+                        format[resolvedSymbol].maximumSignificantDigits);
+    }
+
+    return result;
+  },
+  DONT_ENUM
+);
+%FunctionSetName(Intl.NumberFormat.prototype.resolvedOptions,
+                 'resolvedOptions');
+%FunctionRemovePrototype(Intl.NumberFormat.prototype.resolvedOptions);
+%SetNativeFlag(Intl.NumberFormat.prototype.resolvedOptions);
+
+
+/**
+ * Returns the subset of the given locale list for which this locale list
+ * has a matching (possibly fallback) locale. Locales appear in the same
+ * order in the returned list as in the input list.
+ * Options are optional parameter.
+ */
+%AddNamedProperty(Intl.NumberFormat, 'supportedLocalesOf', function(locales) {
+    if (!IS_UNDEFINED(new.target)) {
+      throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+    }
+
+    return supportedLocalesOf('numberformat', locales, %_Arguments(1));
+  },
+  DONT_ENUM
+);
+%FunctionSetName(Intl.NumberFormat.supportedLocalesOf, 'supportedLocalesOf');
+%FunctionRemovePrototype(Intl.NumberFormat.supportedLocalesOf);
+%SetNativeFlag(Intl.NumberFormat.supportedLocalesOf);
+
+
+/**
+ * Returns a String value representing the result of calling ToNumber(value)
+ * according to the effective locale and the formatting options of this
+ * NumberFormat.
+ */
+function formatNumber(formatter, value) {
+  // Spec treats -0 and +0 as 0.
+  var number = TO_NUMBER(value) + 0;
+
+  return %InternalNumberFormat(%GetImplFromInitializedIntlObject(formatter),
+                               number);
+}
+
+
+/**
+ * Returns a Number that represents string value that was passed in.
+ */
+function parseNumber(formatter, value) {
+  return %InternalNumberParse(%GetImplFromInitializedIntlObject(formatter),
+                              GlobalString(value));
+}
+
+
+addBoundMethod(Intl.NumberFormat, 'format', formatNumber, 1);
+addBoundMethod(Intl.NumberFormat, 'v8Parse', parseNumber, 1);
+
+/**
+ * Returns a string that matches LDML representation of the options object.
+ */
+function toLDMLString(options) {
+  var getOption = getGetOption(options, 'dateformat');
+
+  var ldmlString = '';
+
+  var option = getOption('weekday', 'string', ['narrow', 'short', 'long']);
+  ldmlString += appendToLDMLString(
+      option, {narrow: 'EEEEE', short: 'EEE', long: 'EEEE'});
+
+  option = getOption('era', 'string', ['narrow', 'short', 'long']);
+  ldmlString += appendToLDMLString(
+      option, {narrow: 'GGGGG', short: 'GGG', long: 'GGGG'});
+
+  option = getOption('year', 'string', ['2-digit', 'numeric']);
+  ldmlString += appendToLDMLString(option, {'2-digit': 'yy', 'numeric': 'y'});
+
+  option = getOption('month', 'string',
+                     ['2-digit', 'numeric', 'narrow', 'short', 'long']);
+  ldmlString += appendToLDMLString(option, {'2-digit': 'MM', 'numeric': 'M',
+          'narrow': 'MMMMM', 'short': 'MMM', 'long': 'MMMM'});
+
+  option = getOption('day', 'string', ['2-digit', 'numeric']);
+  ldmlString += appendToLDMLString(
+      option, {'2-digit': 'dd', 'numeric': 'd'});
+
+  var hr12 = getOption('hour12', 'boolean');
+  option = getOption('hour', 'string', ['2-digit', 'numeric']);
+  if (IS_UNDEFINED(hr12)) {
+    ldmlString += appendToLDMLString(option, {'2-digit': 'jj', 'numeric': 'j'});
+  } else if (hr12 === true) {
+    ldmlString += appendToLDMLString(option, {'2-digit': 'hh', 'numeric': 'h'});
+  } else {
+    ldmlString += appendToLDMLString(option, {'2-digit': 'HH', 'numeric': 'H'});
+  }
+
+  option = getOption('minute', 'string', ['2-digit', 'numeric']);
+  ldmlString += appendToLDMLString(option, {'2-digit': 'mm', 'numeric': 'm'});
+
+  option = getOption('second', 'string', ['2-digit', 'numeric']);
+  ldmlString += appendToLDMLString(option, {'2-digit': 'ss', 'numeric': 's'});
+
+  option = getOption('timeZoneName', 'string', ['short', 'long']);
+  ldmlString += appendToLDMLString(option, {short: 'z', long: 'zzzz'});
+
+  return ldmlString;
+}
+
+
+/**
+ * Returns either LDML equivalent of the current option or empty string.
+ */
+function appendToLDMLString(option, pairs) {
+  if (!IS_UNDEFINED(option)) {
+    return pairs[option];
+  } else {
+    return '';
+  }
+}
+
+
+/**
+ * Returns object that matches LDML representation of the date.
+ */
+function fromLDMLString(ldmlString) {
+  // First remove '' quoted text, so we lose 'Uhr' strings.
+  ldmlString = %_Call(StringReplace, ldmlString, GetQuotedStringRE(), '');
+
+  var options = {};
+  var match = %_Call(StringMatch, ldmlString, /E{3,5}/g);
+  options = appendToDateTimeObject(
+      options, 'weekday', match, {EEEEE: 'narrow', EEE: 'short', EEEE: 'long'});
+
+  match = %_Call(StringMatch, ldmlString, /G{3,5}/g);
+  options = appendToDateTimeObject(
+      options, 'era', match, {GGGGG: 'narrow', GGG: 'short', GGGG: 'long'});
+
+  match = %_Call(StringMatch, ldmlString, /y{1,2}/g);
+  options = appendToDateTimeObject(
+      options, 'year', match, {y: 'numeric', yy: '2-digit'});
+
+  match = %_Call(StringMatch, ldmlString, /M{1,5}/g);
+  options = appendToDateTimeObject(options, 'month', match, {MM: '2-digit',
+      M: 'numeric', MMMMM: 'narrow', MMM: 'short', MMMM: 'long'});
+
+  // Sometimes we get L instead of M for month - standalone name.
+  match = %_Call(StringMatch, ldmlString, /L{1,5}/g);
+  options = appendToDateTimeObject(options, 'month', match, {LL: '2-digit',
+      L: 'numeric', LLLLL: 'narrow', LLL: 'short', LLLL: 'long'});
+
+  match = %_Call(StringMatch, ldmlString, /d{1,2}/g);
+  options = appendToDateTimeObject(
+      options, 'day', match, {d: 'numeric', dd: '2-digit'});
+
+  match = %_Call(StringMatch, ldmlString, /h{1,2}/g);
+  if (match !== null) {
+    options['hour12'] = true;
+  }
+  options = appendToDateTimeObject(
+      options, 'hour', match, {h: 'numeric', hh: '2-digit'});
+
+  match = %_Call(StringMatch, ldmlString, /H{1,2}/g);
+  if (match !== null) {
+    options['hour12'] = false;
+  }
+  options = appendToDateTimeObject(
+      options, 'hour', match, {H: 'numeric', HH: '2-digit'});
+
+  match = %_Call(StringMatch, ldmlString, /m{1,2}/g);
+  options = appendToDateTimeObject(
+      options, 'minute', match, {m: 'numeric', mm: '2-digit'});
+
+  match = %_Call(StringMatch, ldmlString, /s{1,2}/g);
+  options = appendToDateTimeObject(
+      options, 'second', match, {s: 'numeric', ss: '2-digit'});
+
+  match = %_Call(StringMatch, ldmlString, /z|zzzz/g);
+  options = appendToDateTimeObject(
+      options, 'timeZoneName', match, {z: 'short', zzzz: 'long'});
+
+  return options;
+}
+
+
+function appendToDateTimeObject(options, option, match, pairs) {
+  if (IS_NULL(match)) {
+    if (!%HasOwnProperty(options, option)) {
+      defineWEProperty(options, option, UNDEFINED);
+    }
+    return options;
+  }
+
+  var property = match[0];
+  defineWEProperty(options, option, pairs[property]);
+
+  return options;
+}
+
+
+/**
+ * Returns options with at least default values in it.
+ */
+function toDateTimeOptions(options, required, defaults) {
+  if (IS_UNDEFINED(options)) {
+    options = {};
+  } else {
+    options = TO_OBJECT(options);
+  }
+
+  var needsDefault = true;
+  if ((required === 'date' || required === 'any') &&
+      (!IS_UNDEFINED(options.weekday) || !IS_UNDEFINED(options.year) ||
+       !IS_UNDEFINED(options.month) || !IS_UNDEFINED(options.day))) {
+    needsDefault = false;
+  }
+
+  if ((required === 'time' || required === 'any') &&
+      (!IS_UNDEFINED(options.hour) || !IS_UNDEFINED(options.minute) ||
+       !IS_UNDEFINED(options.second))) {
+    needsDefault = false;
+  }
+
+  if (needsDefault && (defaults === 'date' || defaults === 'all')) {
+    ObjectDefineProperty(options, 'year', {value: 'numeric',
+                                           writable: true,
+                                           enumerable: true,
+                                           configurable: true});
+    ObjectDefineProperty(options, 'month', {value: 'numeric',
+                                            writable: true,
+                                            enumerable: true,
+                                            configurable: true});
+    ObjectDefineProperty(options, 'day', {value: 'numeric',
+                                          writable: true,
+                                          enumerable: true,
+                                          configurable: true});
+  }
+
+  if (needsDefault && (defaults === 'time' || defaults === 'all')) {
+    ObjectDefineProperty(options, 'hour', {value: 'numeric',
+                                           writable: true,
+                                           enumerable: true,
+                                           configurable: true});
+    ObjectDefineProperty(options, 'minute', {value: 'numeric',
+                                             writable: true,
+                                             enumerable: true,
+                                             configurable: true});
+    ObjectDefineProperty(options, 'second', {value: 'numeric',
+                                             writable: true,
+                                             enumerable: true,
+                                             configurable: true});
+  }
+
+  return options;
+}
+
+
+/**
+ * Initializes the given object so it's a valid DateTimeFormat instance.
+ * Useful for subclassing.
+ */
+function initializeDateTimeFormat(dateFormat, locales, options) {
+
+  if (%IsInitializedIntlObject(dateFormat)) {
+    throw MakeTypeError(kReinitializeIntl, "DateTimeFormat");
+  }
+
+  if (IS_UNDEFINED(options)) {
+    options = {};
+  }
+
+  var locale = resolveLocale('dateformat', locales, options);
+
+  options = toDateTimeOptions(options, 'any', 'date');
+
+  var getOption = getGetOption(options, 'dateformat');
+
+  // We implement only best fit algorithm, but still need to check
+  // if the formatMatcher values are in range.
+  var matcher = getOption('formatMatcher', 'string',
+                          ['basic', 'best fit'], 'best fit');
+
+  // Build LDML string for the skeleton that we pass to the formatter.
+  var ldmlString = toLDMLString(options);
+
+  // Filter out supported extension keys so we know what to put in resolved
+  // section later on.
+  // We need to pass calendar and number system to the method.
+  var tz = canonicalizeTimeZoneID(options.timeZone);
+
+  // ICU prefers options to be passed using -u- extension key/values, so
+  // we need to build that.
+  var internalOptions = {};
+  var extensionMap = parseExtension(locale.extension);
+
+  /**
+   * Map of Unicode extensions to option properties, and their values and types,
+   * for a date/time format.
+   */
+  var DATETIME_FORMAT_KEY_MAP = {
+    'ca': {'property': UNDEFINED, 'type': 'string'},
+    'nu': {'property': UNDEFINED, 'type': 'string'}
+  };
+
+  var extension = setOptions(options, extensionMap, DATETIME_FORMAT_KEY_MAP,
+                             getOption, internalOptions);
+
+  var requestedLocale = locale.locale + extension;
+  var resolved = ObjectDefineProperties({}, {
+    calendar: {writable: true},
+    day: {writable: true},
+    era: {writable: true},
+    hour12: {writable: true},
+    hour: {writable: true},
+    locale: {writable: true},
+    minute: {writable: true},
+    month: {writable: true},
+    numberingSystem: {writable: true},
+    [patternSymbol]: {writable: true},
+    pattern: patternAccessor,
+    requestedLocale: {value: requestedLocale, writable: true},
+    second: {writable: true},
+    timeZone: {writable: true},
+    timeZoneName: {writable: true},
+    tz: {value: tz, writable: true},
+    weekday: {writable: true},
+    year: {writable: true}
+  });
+
+  var formatter = %CreateDateTimeFormat(
+    requestedLocale, {skeleton: ldmlString, timeZone: tz}, resolved);
+
+  if (resolved.timeZone === "Etc/Unknown") {
+    throw MakeRangeError(kUnsupportedTimeZone, tz);
+  }
+
+  %MarkAsInitializedIntlObjectOfType(dateFormat, 'dateformat', formatter);
+  dateFormat[resolvedSymbol] = resolved;
+  ObjectDefineProperty(dateFormat, 'resolved', resolvedAccessor);
+
+  return dateFormat;
+}
+
+
+/**
+ * Constructs Intl.DateTimeFormat object given optional locales and options
+ * parameters.
+ *
+ * @constructor
+ */
+%AddNamedProperty(Intl, 'DateTimeFormat', function() {
+    var locales = %_Arguments(0);
+    var options = %_Arguments(1);
+
+    if (!this || this === Intl) {
+      // Constructor is called as a function.
+      return new Intl.DateTimeFormat(locales, options);
+    }
+
+    return initializeDateTimeFormat(TO_OBJECT(this), locales, options);
+  },
+  DONT_ENUM
+);
+
+
+/**
+ * DateTimeFormat resolvedOptions method.
+ */
+%AddNamedProperty(Intl.DateTimeFormat.prototype, 'resolvedOptions', function() {
+    if (!IS_UNDEFINED(new.target)) {
+      throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+    }
+
+    if (!%IsInitializedIntlObjectOfType(this, 'dateformat')) {
+      throw MakeTypeError(kResolvedOptionsCalledOnNonObject, "DateTimeFormat");
+    }
+
+    /**
+     * Maps ICU calendar names into LDML type.
+     */
+    var ICU_CALENDAR_MAP = {
+      'gregorian': 'gregory',
+      'japanese': 'japanese',
+      'buddhist': 'buddhist',
+      'roc': 'roc',
+      'persian': 'persian',
+      'islamic-civil': 'islamicc',
+      'islamic': 'islamic',
+      'hebrew': 'hebrew',
+      'chinese': 'chinese',
+      'indian': 'indian',
+      'coptic': 'coptic',
+      'ethiopic': 'ethiopic',
+      'ethiopic-amete-alem': 'ethioaa'
+    };
+
+    var format = this;
+    var fromPattern = fromLDMLString(format[resolvedSymbol][patternSymbol]);
+    var userCalendar = ICU_CALENDAR_MAP[format[resolvedSymbol].calendar];
+    if (IS_UNDEFINED(userCalendar)) {
+      // Use ICU name if we don't have a match. It shouldn't happen, but
+      // it would be too strict to throw for this.
+      userCalendar = format[resolvedSymbol].calendar;
+    }
+
+    var locale = getOptimalLanguageTag(format[resolvedSymbol].requestedLocale,
+                                       format[resolvedSymbol].locale);
+
+    var result = {
+      locale: locale,
+      numberingSystem: format[resolvedSymbol].numberingSystem,
+      calendar: userCalendar,
+      timeZone: format[resolvedSymbol].timeZone
+    };
+
+    addWECPropertyIfDefined(result, 'timeZoneName', fromPattern.timeZoneName);
+    addWECPropertyIfDefined(result, 'era', fromPattern.era);
+    addWECPropertyIfDefined(result, 'year', fromPattern.year);
+    addWECPropertyIfDefined(result, 'month', fromPattern.month);
+    addWECPropertyIfDefined(result, 'day', fromPattern.day);
+    addWECPropertyIfDefined(result, 'weekday', fromPattern.weekday);
+    addWECPropertyIfDefined(result, 'hour12', fromPattern.hour12);
+    addWECPropertyIfDefined(result, 'hour', fromPattern.hour);
+    addWECPropertyIfDefined(result, 'minute', fromPattern.minute);
+    addWECPropertyIfDefined(result, 'second', fromPattern.second);
+
+    return result;
+  },
+  DONT_ENUM
+);
+%FunctionSetName(Intl.DateTimeFormat.prototype.resolvedOptions,
+                 'resolvedOptions');
+%FunctionRemovePrototype(Intl.DateTimeFormat.prototype.resolvedOptions);
+%SetNativeFlag(Intl.DateTimeFormat.prototype.resolvedOptions);
+
+
+/**
+ * Returns the subset of the given locale list for which this locale list
+ * has a matching (possibly fallback) locale. Locales appear in the same
+ * order in the returned list as in the input list.
+ * Options are optional parameter.
+ */
+%AddNamedProperty(Intl.DateTimeFormat, 'supportedLocalesOf', function(locales) {
+    if (!IS_UNDEFINED(new.target)) {
+      throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+    }
+
+    return supportedLocalesOf('dateformat', locales, %_Arguments(1));
+  },
+  DONT_ENUM
+);
+%FunctionSetName(Intl.DateTimeFormat.supportedLocalesOf, 'supportedLocalesOf');
+%FunctionRemovePrototype(Intl.DateTimeFormat.supportedLocalesOf);
+%SetNativeFlag(Intl.DateTimeFormat.supportedLocalesOf);
+
+
+/**
+ * Returns a String value representing the result of calling ToNumber(date)
+ * according to the effective locale and the formatting options of this
+ * DateTimeFormat.
+ */
+function formatDate(formatter, dateValue) {
+  var dateMs;
+  if (IS_UNDEFINED(dateValue)) {
+    dateMs = %DateCurrentTime();
+  } else {
+    dateMs = TO_NUMBER(dateValue);
+  }
+
+  if (!IsFinite(dateMs)) throw MakeRangeError(kDateRange);
+
+  return %InternalDateFormat(%GetImplFromInitializedIntlObject(formatter),
+                             new GlobalDate(dateMs));
+}
+
+
+/**
+ * Returns a Date object representing the result of calling ToString(value)
+ * according to the effective locale and the formatting options of this
+ * DateTimeFormat.
+ * Returns undefined if date string cannot be parsed.
+ */
+function parseDate(formatter, value) {
+  return %InternalDateParse(%GetImplFromInitializedIntlObject(formatter),
+                            GlobalString(value));
+}
+
+
+// 0 because date is optional argument.
+addBoundMethod(Intl.DateTimeFormat, 'format', formatDate, 0);
+addBoundMethod(Intl.DateTimeFormat, 'v8Parse', parseDate, 1);
+
+
+/**
+ * Returns canonical Area/Location(/Location) name, or throws an exception
+ * if the zone name is invalid IANA name.
+ */
+function canonicalizeTimeZoneID(tzID) {
+  // Skip undefined zones.
+  if (IS_UNDEFINED(tzID)) {
+    return tzID;
+  }
+
+  // Special case handling (UTC, GMT).
+  var upperID = %StringToUpperCase(tzID);
+  if (upperID === 'UTC' || upperID === 'GMT' ||
+      upperID === 'ETC/UTC' || upperID === 'ETC/GMT') {
+    return 'UTC';
+  }
+
+  // TODO(jshin): Add support for Etc/GMT[+-]([1-9]|1[0-2])
+
+  // We expect only _, '-' and / beside ASCII letters.
+  // All inputs should conform to Area/Location(/Location)* from now on.
+  var match = %_Call(StringMatch, tzID, GetTimezoneNameCheckRE());
+  if (IS_NULL(match)) throw MakeRangeError(kExpectedTimezoneID, tzID);
+
+  var result = toTitleCaseTimezoneLocation(match[1]) + '/' +
+               toTitleCaseTimezoneLocation(match[2]);
+
+  if (!IS_UNDEFINED(match[3]) && 3 < match.length) {
+    var locations = %_Call(StringSplit, match[3], '/');
+    // The 1st element is empty. Starts with i=1.
+    for (var i = 1; i < locations.length; i++) {
+      result = result + '/' + toTitleCaseTimezoneLocation(locations[i]);
+    }
+  }
+
+  return result;
+}
+
+/**
+ * Initializes the given object so it's a valid BreakIterator instance.
+ * Useful for subclassing.
+ */
+function initializeBreakIterator(iterator, locales, options) {
+  if (%IsInitializedIntlObject(iterator)) {
+    throw MakeTypeError(kReinitializeIntl, "v8BreakIterator");
+  }
+
+  if (IS_UNDEFINED(options)) {
+    options = {};
+  }
+
+  var getOption = getGetOption(options, 'breakiterator');
+
+  var internalOptions = {};
+
+  defineWEProperty(internalOptions, 'type', getOption(
+    'type', 'string', ['character', 'word', 'sentence', 'line'], 'word'));
+
+  var locale = resolveLocale('breakiterator', locales, options);
+  var resolved = ObjectDefineProperties({}, {
+    requestedLocale: {value: locale.locale, writable: true},
+    type: {value: internalOptions.type, writable: true},
+    locale: {writable: true}
+  });
+
+  var internalIterator = %CreateBreakIterator(locale.locale,
+                                              internalOptions,
+                                              resolved);
+
+  %MarkAsInitializedIntlObjectOfType(iterator, 'breakiterator',
+                                     internalIterator);
+  iterator[resolvedSymbol] = resolved;
+  ObjectDefineProperty(iterator, 'resolved', resolvedAccessor);
+
+  return iterator;
+}
+
+
+/**
+ * Constructs Intl.v8BreakIterator object given optional locales and options
+ * parameters.
+ *
+ * @constructor
+ */
+%AddNamedProperty(Intl, 'v8BreakIterator', function() {
+    var locales = %_Arguments(0);
+    var options = %_Arguments(1);
+
+    if (!this || this === Intl) {
+      // Constructor is called as a function.
+      return new Intl.v8BreakIterator(locales, options);
+    }
+
+    return initializeBreakIterator(TO_OBJECT(this), locales, options);
+  },
+  DONT_ENUM
+);
+
+
+/**
+ * BreakIterator resolvedOptions method.
+ */
+%AddNamedProperty(Intl.v8BreakIterator.prototype, 'resolvedOptions',
+  function() {
+    if (!IS_UNDEFINED(new.target)) {
+      throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+    }
+
+    if (!%IsInitializedIntlObjectOfType(this, 'breakiterator')) {
+      throw MakeTypeError(kResolvedOptionsCalledOnNonObject, "v8BreakIterator");
+    }
+
+    var segmenter = this;
+    var locale =
+        getOptimalLanguageTag(segmenter[resolvedSymbol].requestedLocale,
+                              segmenter[resolvedSymbol].locale);
+
+    return {
+      locale: locale,
+      type: segmenter[resolvedSymbol].type
+    };
+  },
+  DONT_ENUM
+);
+%FunctionSetName(Intl.v8BreakIterator.prototype.resolvedOptions,
+                 'resolvedOptions');
+%FunctionRemovePrototype(Intl.v8BreakIterator.prototype.resolvedOptions);
+%SetNativeFlag(Intl.v8BreakIterator.prototype.resolvedOptions);
+
+
+/**
+ * Returns the subset of the given locale list for which this locale list
+ * has a matching (possibly fallback) locale. Locales appear in the same
+ * order in the returned list as in the input list.
+ * Options are optional parameter.
+ */
+%AddNamedProperty(Intl.v8BreakIterator, 'supportedLocalesOf',
+  function(locales) {
+    if (!IS_UNDEFINED(new.target)) {
+      throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+    }
+
+    return supportedLocalesOf('breakiterator', locales, %_Arguments(1));
+  },
+  DONT_ENUM
+);
+%FunctionSetName(Intl.v8BreakIterator.supportedLocalesOf, 'supportedLocalesOf');
+%FunctionRemovePrototype(Intl.v8BreakIterator.supportedLocalesOf);
+%SetNativeFlag(Intl.v8BreakIterator.supportedLocalesOf);
+
+
+/**
+ * Adopts text to segment using the iterator. Old text, if present,
+ * gets discarded.
+ */
+function adoptText(iterator, text) {
+  %BreakIteratorAdoptText(%GetImplFromInitializedIntlObject(iterator),
+                          GlobalString(text));
+}
+
+
+/**
+ * Returns index of the first break in the string and moves current pointer.
+ */
+function first(iterator) {
+  return %BreakIteratorFirst(%GetImplFromInitializedIntlObject(iterator));
+}
+
+
+/**
+ * Returns the index of the next break and moves the pointer.
+ */
+function next(iterator) {
+  return %BreakIteratorNext(%GetImplFromInitializedIntlObject(iterator));
+}
+
+
+/**
+ * Returns index of the current break.
+ */
+function current(iterator) {
+  return %BreakIteratorCurrent(%GetImplFromInitializedIntlObject(iterator));
+}
+
+
+/**
+ * Returns type of the current break.
+ */
+function breakType(iterator) {
+  return %BreakIteratorBreakType(%GetImplFromInitializedIntlObject(iterator));
+}
+
+
+addBoundMethod(Intl.v8BreakIterator, 'adoptText', adoptText, 1);
+addBoundMethod(Intl.v8BreakIterator, 'first', first, 0);
+addBoundMethod(Intl.v8BreakIterator, 'next', next, 0);
+addBoundMethod(Intl.v8BreakIterator, 'current', current, 0);
+addBoundMethod(Intl.v8BreakIterator, 'breakType', breakType, 0);
+
+// Save references to Intl objects and methods we use, for added security.
+var savedObjects = {
+  'collator': Intl.Collator,
+  'numberformat': Intl.NumberFormat,
+  'dateformatall': Intl.DateTimeFormat,
+  'dateformatdate': Intl.DateTimeFormat,
+  'dateformattime': Intl.DateTimeFormat
+};
+
+
+// Default (created with undefined locales and options parameters) collator,
+// number and date format instances. They'll be created as needed.
+var defaultObjects = {
+  'collator': UNDEFINED,
+  'numberformat': UNDEFINED,
+  'dateformatall': UNDEFINED,
+  'dateformatdate': UNDEFINED,
+  'dateformattime': UNDEFINED,
+};
+
+
+/**
+ * Returns cached or newly created instance of a given service.
+ * We cache only default instances (where no locales or options are provided).
+ */
+function cachedOrNewService(service, locales, options, defaults) {
+  var useOptions = (IS_UNDEFINED(defaults)) ? options : defaults;
+  if (IS_UNDEFINED(locales) && IS_UNDEFINED(options)) {
+    if (IS_UNDEFINED(defaultObjects[service])) {
+      defaultObjects[service] = new savedObjects[service](locales, useOptions);
+    }
+    return defaultObjects[service];
+  }
+  return new savedObjects[service](locales, useOptions);
+}
+
+
+function OverrideFunction(object, name, f) {
+  %CheckIsBootstrapping();
+  ObjectDefineProperty(object, name, { value: f,
+                                       writeable: true,
+                                       configurable: true,
+                                       enumerable: false });
+  %FunctionSetName(f, name);
+  %FunctionRemovePrototype(f);
+  %SetNativeFlag(f);
+}
+
+/**
+ * Compares this and that, and returns less than 0, 0 or greater than 0 value.
+ * Overrides the built-in method.
+ */
+OverrideFunction(GlobalString.prototype, 'localeCompare', function(that) {
+    if (!IS_UNDEFINED(new.target)) {
+      throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+    }
+
+    if (IS_NULL_OR_UNDEFINED(this)) {
+      throw MakeTypeError(kMethodInvokedOnNullOrUndefined);
+    }
+
+    var locales = %_Arguments(1);
+    var options = %_Arguments(2);
+    var collator = cachedOrNewService('collator', locales, options);
+    return compare(collator, this, that);
+  }
+);
+
+
+/**
+ * Unicode normalization. This method is called with one argument that
+ * specifies the normalization form.
+ * If none is specified, "NFC" is assumed.
+ * If the form is not one of "NFC", "NFD", "NFKC", or "NFKD", then throw
+ * a RangeError Exception.
+ */
+
+OverrideFunction(GlobalString.prototype, 'normalize', function() {
+    if (!IS_UNDEFINED(new.target)) {
+      throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+    }
+
+    CHECK_OBJECT_COERCIBLE(this, "String.prototype.normalize");
+    var s = TO_STRING(this);
+
+    var formArg = %_Arguments(0);
+    var form = IS_UNDEFINED(formArg) ? 'NFC' : TO_STRING(formArg);
+
+    var NORMALIZATION_FORMS = ['NFC', 'NFD', 'NFKC', 'NFKD'];
+
+    var normalizationForm = %_Call(ArrayIndexOf, NORMALIZATION_FORMS, form);
+    if (normalizationForm === -1) {
+      throw MakeRangeError(kNormalizationForm,
+          %_Call(ArrayJoin, NORMALIZATION_FORMS, ', '));
+    }
+
+    return %StringNormalize(s, normalizationForm);
+  }
+);
+
+
+/**
+ * Formats a Number object (this) using locale and options values.
+ * If locale or options are omitted, defaults are used.
+ */
+OverrideFunction(GlobalNumber.prototype, 'toLocaleString', function() {
+    if (!IS_UNDEFINED(new.target)) {
+      throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+    }
+
+    if (!(this instanceof GlobalNumber) && typeof(this) !== 'number') {
+      throw MakeTypeError(kMethodInvokedOnWrongType, "Number");
+    }
+
+    var locales = %_Arguments(0);
+    var options = %_Arguments(1);
+    var numberFormat = cachedOrNewService('numberformat', locales, options);
+    return formatNumber(numberFormat, this);
+  }
+);
+
+
+/**
+ * Returns actual formatted date or fails if date parameter is invalid.
+ */
+function toLocaleDateTime(date, locales, options, required, defaults, service) {
+  if (!(date instanceof GlobalDate)) {
+    throw MakeTypeError(kMethodInvokedOnWrongType, "Date");
+  }
+
+  if (IsNaN(date)) return 'Invalid Date';
+
+  var internalOptions = toDateTimeOptions(options, required, defaults);
+
+  var dateFormat =
+      cachedOrNewService(service, locales, options, internalOptions);
+
+  return formatDate(dateFormat, date);
+}
+
+
+/**
+ * Formats a Date object (this) using locale and options values.
+ * If locale or options are omitted, defaults are used - both date and time are
+ * present in the output.
+ */
+OverrideFunction(GlobalDate.prototype, 'toLocaleString', function() {
+    if (!IS_UNDEFINED(new.target)) {
+      throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+    }
+
+    var locales = %_Arguments(0);
+    var options = %_Arguments(1);
+    return toLocaleDateTime(
+        this, locales, options, 'any', 'all', 'dateformatall');
+  }
+);
+
+
+/**
+ * Formats a Date object (this) using locale and options values.
+ * If locale or options are omitted, defaults are used - only date is present
+ * in the output.
+ */
+OverrideFunction(GlobalDate.prototype, 'toLocaleDateString', function() {
+    if (!IS_UNDEFINED(new.target)) {
+      throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+    }
+
+    var locales = %_Arguments(0);
+    var options = %_Arguments(1);
+    return toLocaleDateTime(
+        this, locales, options, 'date', 'date', 'dateformatdate');
+  }
+);
+
+
+/**
+ * Formats a Date object (this) using locale and options values.
+ * If locale or options are omitted, defaults are used - only time is present
+ * in the output.
+ */
+OverrideFunction(GlobalDate.prototype, 'toLocaleTimeString', function() {
+    if (!IS_UNDEFINED(new.target)) {
+      throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+    }
+
+    var locales = %_Arguments(0);
+    var options = %_Arguments(1);
+    return toLocaleDateTime(
+        this, locales, options, 'time', 'time', 'dateformattime');
+  }
+);
+
+})
diff --git a/src/js/iterator-prototype.js b/src/js/iterator-prototype.js
new file mode 100644
index 0000000..6f25019
--- /dev/null
+++ b/src/js/iterator-prototype.js
@@ -0,0 +1,21 @@
+// Copyright 2015 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.
+
+(function(global, utils) {
+  "use strict";
+  %CheckIsBootstrapping();
+
+  var GlobalObject = global.Object;
+  var IteratorPrototype = utils.ImportNow("IteratorPrototype");
+  var iteratorSymbol = utils.ImportNow("iterator_symbol");
+
+  // 25.1.2.1 %IteratorPrototype% [ @@iterator ] ( )
+  function IteratorPrototypeIterator() {
+    return this;
+  }
+
+  utils.SetFunctionName(IteratorPrototypeIterator, iteratorSymbol);
+  %AddNamedProperty(IteratorPrototype, iteratorSymbol,
+      IteratorPrototypeIterator, DONT_ENUM);
+})
diff --git a/src/js/json.js b/src/js/json.js
new file mode 100644
index 0000000..b8836ea
--- /dev/null
+++ b/src/js/json.js
@@ -0,0 +1,280 @@
+// Copyright 2009 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.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalDate = global.Date;
+var GlobalJSON = global.JSON;
+var GlobalSet = global.Set;
+var InternalArray = utils.InternalArray;
+var MakeTypeError;
+var MaxSimple;
+var MinSimple;
+var ObjectHasOwnProperty;
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+
+utils.Import(function(from) {
+  MakeTypeError = from.MakeTypeError;
+  MaxSimple = from.MaxSimple;
+  MinSimple = from.MinSimple;
+  ObjectHasOwnProperty = from.ObjectHasOwnProperty;
+});
+
+// -------------------------------------------------------------------
+
+function CreateDataProperty(o, p, v) {
+  var desc = {value: v, enumerable: true, writable: true, configurable: true};
+  return %reflect_define_property(o, p, desc);
+}
+
+
+function InternalizeJSONProperty(holder, name, reviver) {
+  var val = holder[name];
+  if (IS_RECEIVER(val)) {
+    if (%is_arraylike(val)) {
+      var length = TO_LENGTH(val.length);
+      for (var i = 0; i < length; i++) {
+        var newElement =
+            InternalizeJSONProperty(val, %_NumberToString(i), reviver);
+        if (IS_UNDEFINED(newElement)) {
+          %reflect_delete_property(val, i);
+        } else {
+          CreateDataProperty(val, i, newElement);
+        }
+      }
+    } else {
+      for (var p of %object_keys(val)) {
+        var newElement = InternalizeJSONProperty(val, p, reviver);
+        if (IS_UNDEFINED(newElement)) {
+          %reflect_delete_property(val, p);
+        } else {
+          CreateDataProperty(val, p, newElement);
+        }
+      }
+    }
+  }
+  return %_Call(reviver, holder, name, val);
+}
+
+
+function JSONParse(text, reviver) {
+  var unfiltered = %ParseJson(text);
+  if (IS_CALLABLE(reviver)) {
+    return InternalizeJSONProperty({'': unfiltered}, '', reviver);
+  } else {
+    return unfiltered;
+  }
+}
+
+
+function SerializeArray(value, replacer, stack, indent, gap) {
+  if (!%PushIfAbsent(stack, value)) throw MakeTypeError(kCircularStructure);
+  var stepback = indent;
+  indent += gap;
+  var partial = new InternalArray();
+  var len = TO_LENGTH(value.length);
+  for (var i = 0; i < len; i++) {
+    var strP = JSONSerialize(%_NumberToString(i), value, replacer, stack,
+                             indent, gap);
+    if (IS_UNDEFINED(strP)) {
+      strP = "null";
+    }
+    partial.push(strP);
+  }
+  var final;
+  if (gap == "") {
+    final = "[" + partial.join(",") + "]";
+  } else if (partial.length > 0) {
+    var separator = ",\n" + indent;
+    final = "[\n" + indent + partial.join(separator) + "\n" +
+        stepback + "]";
+  } else {
+    final = "[]";
+  }
+  stack.pop();
+  return final;
+}
+
+
+function SerializeObject(value, replacer, stack, indent, gap) {
+  if (!%PushIfAbsent(stack, value)) throw MakeTypeError(kCircularStructure);
+  var stepback = indent;
+  indent += gap;
+  var partial = new InternalArray();
+  if (IS_ARRAY(replacer)) {
+    var length = replacer.length;
+    for (var i = 0; i < length; i++) {
+      var p = replacer[i];
+      var strP = JSONSerialize(p, value, replacer, stack, indent, gap);
+      if (!IS_UNDEFINED(strP)) {
+        var member = %QuoteJSONString(p) + ":";
+        if (gap != "") member += " ";
+        member += strP;
+        partial.push(member);
+      }
+    }
+  } else {
+    for (var p of %object_keys(value)) {
+      var strP = JSONSerialize(p, value, replacer, stack, indent, gap);
+      if (!IS_UNDEFINED(strP)) {
+        var member = %QuoteJSONString(p) + ":";
+        if (gap != "") member += " ";
+        member += strP;
+        partial.push(member);
+      }
+    }
+  }
+  var final;
+  if (gap == "") {
+    final = "{" + partial.join(",") + "}";
+  } else if (partial.length > 0) {
+    var separator = ",\n" + indent;
+    final = "{\n" + indent + partial.join(separator) + "\n" +
+        stepback + "}";
+  } else {
+    final = "{}";
+  }
+  stack.pop();
+  return final;
+}
+
+
+function JSONSerialize(key, holder, replacer, stack, indent, gap) {
+  var value = holder[key];
+  if (IS_RECEIVER(value)) {
+    var toJSON = value.toJSON;
+    if (IS_CALLABLE(toJSON)) {
+      value = %_Call(toJSON, value, key);
+    }
+  }
+  if (IS_CALLABLE(replacer)) {
+    value = %_Call(replacer, holder, key, value);
+  }
+  if (IS_STRING(value)) {
+    return %QuoteJSONString(value);
+  } else if (IS_NUMBER(value)) {
+    return JSON_NUMBER_TO_STRING(value);
+  } else if (IS_BOOLEAN(value)) {
+    return value ? "true" : "false";
+  } else if (IS_NULL(value)) {
+    return "null";
+  } else if (IS_RECEIVER(value) && !IS_CALLABLE(value)) {
+    // Non-callable object. If it's a primitive wrapper, it must be unwrapped.
+    if (%is_arraylike(value)) {
+      return SerializeArray(value, replacer, stack, indent, gap);
+    } else if (IS_NUMBER_WRAPPER(value)) {
+      value = TO_NUMBER(value);
+      return JSON_NUMBER_TO_STRING(value);
+    } else if (IS_STRING_WRAPPER(value)) {
+      return %QuoteJSONString(TO_STRING(value));
+    } else if (IS_BOOLEAN_WRAPPER(value)) {
+      return %_ValueOf(value) ? "true" : "false";
+    } else {
+      return SerializeObject(value, replacer, stack, indent, gap);
+    }
+  }
+  // Undefined or a callable object.
+  return UNDEFINED;
+}
+
+
+function JSONStringify(value, replacer, space) {
+  if (%_ArgumentsLength() == 1 && !IS_PROXY(value)) {
+    return %BasicJSONStringify(value);
+  }
+  if (!IS_CALLABLE(replacer) && %is_arraylike(replacer)) {
+    var property_list = new InternalArray();
+    var seen_properties = new GlobalSet();
+    var length = TO_LENGTH(replacer.length);
+    for (var i = 0; i < length; i++) {
+      var v = replacer[i];
+      var item;
+      if (IS_STRING(v)) {
+        item = v;
+      } else if (IS_NUMBER(v)) {
+        item = %_NumberToString(v);
+      } else if (IS_STRING_WRAPPER(v) || IS_NUMBER_WRAPPER(v)) {
+        item = TO_STRING(v);
+      } else {
+        continue;
+      }
+      if (!seen_properties.has(item)) {
+        property_list.push(item);
+        seen_properties.add(item);
+      }
+    }
+    replacer = property_list;
+  }
+  if (IS_OBJECT(space)) {
+    // Unwrap 'space' if it is wrapped
+    if (IS_NUMBER_WRAPPER(space)) {
+      space = TO_NUMBER(space);
+    } else if (IS_STRING_WRAPPER(space)) {
+      space = TO_STRING(space);
+    }
+  }
+  var gap;
+  if (IS_NUMBER(space)) {
+    space = MaxSimple(0, MinSimple(TO_INTEGER(space), 10));
+    gap = %_SubString("          ", 0, space);
+  } else if (IS_STRING(space)) {
+    if (space.length > 10) {
+      gap = %_SubString(space, 0, 10);
+    } else {
+      gap = space;
+    }
+  } else {
+    gap = "";
+  }
+  return JSONSerialize('', {'': value}, replacer, new InternalArray(), "", gap);
+}
+
+// -------------------------------------------------------------------
+
+%AddNamedProperty(GlobalJSON, toStringTagSymbol, "JSON", READ_ONLY | DONT_ENUM);
+
+// Set up non-enumerable properties of the JSON object.
+utils.InstallFunctions(GlobalJSON, DONT_ENUM, [
+  "parse", JSONParse,
+  "stringify", JSONStringify
+]);
+
+// -------------------------------------------------------------------
+// Date.toJSON
+
+// 20.3.4.37 Date.prototype.toJSON ( key )
+function DateToJSON(key) {
+  var o = TO_OBJECT(this);
+  var tv = TO_PRIMITIVE_NUMBER(o);
+  if (IS_NUMBER(tv) && !NUMBER_IS_FINITE(tv)) {
+    return null;
+  }
+  return o.toISOString();
+}
+
+// Set up non-enumerable functions of the Date prototype object.
+utils.InstallFunctions(GlobalDate.prototype, DONT_ENUM, [
+  "toJSON", DateToJSON
+]);
+
+// -------------------------------------------------------------------
+// JSON Builtins
+
+function JsonSerializeAdapter(key, object) {
+  var holder = {};
+  holder[key] = object;
+  // No need to pass the actual holder since there is no replacer function.
+  return JSONSerialize(key, holder, UNDEFINED, new InternalArray(), "", "");
+}
+
+%InstallToContext(["json_serialize_adapter", JsonSerializeAdapter]);
+
+})
diff --git a/src/js/macros.py b/src/js/macros.py
new file mode 100644
index 0000000..3bcc8c1
--- /dev/null
+++ b/src/js/macros.py
@@ -0,0 +1,269 @@
+# Copyright 2006-2009 the V8 project authors. All rights reserved.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+#       copyright notice, this list of conditions and the following
+#       disclaimer in the documentation and/or other materials provided
+#       with the distribution.
+#     * Neither the name of Google Inc. nor the names of its
+#       contributors may be used to endorse or promote products derived
+#       from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Dictionary that is passed as defines for js2c.py.
+# Used for defines that must be defined for all native JS files.
+
+define NONE        = 0;
+define READ_ONLY   = 1;
+define DONT_ENUM   = 2;
+define DONT_DELETE = 4;
+define NEW_ONE_BYTE_STRING = true;
+define NEW_TWO_BYTE_STRING = false;
+
+# Constants used for getter and setter operations.
+define GETTER = 0;
+define SETTER = 1;
+
+# Safe maximum number of arguments to push to stack, when multiplied by
+# pointer size. Used by Function.prototype.apply(), Reflect.apply() and
+# Reflect.construct().
+define kSafeArgumentsLength = 0x800000;
+
+# 2^53 - 1
+define kMaxSafeInteger = 9007199254740991;
+
+# 2^32 - 1
+define kMaxUint32 = 4294967295;
+
+# Strict mode flags for passing to %SetProperty
+define kSloppyMode = 0;
+define kStrictMode = 1;
+
+# Native cache ids.
+define STRING_TO_REGEXP_CACHE_ID = 0;
+
+# Type query macros.
+#
+# Note: We have special support for typeof(foo) === 'bar' in the compiler.
+#       It will *not* generate a runtime typeof call for the most important
+#       values of 'bar'.
+macro IS_ARRAY(arg)             = (%_IsArray(arg));
+macro IS_ARRAYBUFFER(arg)       = (%_ClassOf(arg) === 'ArrayBuffer');
+macro IS_BOOLEAN(arg)           = (typeof(arg) === 'boolean');
+macro IS_BOOLEAN_WRAPPER(arg)   = (%_ClassOf(arg) === 'Boolean');
+macro IS_DATAVIEW(arg)          = (%_ClassOf(arg) === 'DataView');
+macro IS_DATE(arg)              = (%_IsDate(arg));
+macro IS_ERROR(arg)             = (%_ClassOf(arg) === 'Error');
+macro IS_FUNCTION(arg)          = (%_IsFunction(arg));
+macro IS_GENERATOR(arg)         = (%_ClassOf(arg) === 'Generator');
+macro IS_GLOBAL(arg)            = (%_ClassOf(arg) === 'global');
+macro IS_MAP(arg)               = (%_ClassOf(arg) === 'Map');
+macro IS_MAP_ITERATOR(arg)      = (%_ClassOf(arg) === 'Map Iterator');
+macro IS_NULL(arg)              = (arg === null);
+macro IS_NULL_OR_UNDEFINED(arg) = (arg == null);
+macro IS_NUMBER(arg)            = (typeof(arg) === 'number');
+macro IS_NUMBER_WRAPPER(arg)    = (%_ClassOf(arg) === 'Number');
+macro IS_OBJECT(arg)            = (typeof(arg) === 'object');
+macro IS_PROXY(arg)             = (%_IsJSProxy(arg));
+macro IS_REGEXP(arg)            = (%_IsRegExp(arg));
+macro IS_SCRIPT(arg)            = (%_ClassOf(arg) === 'Script');
+macro IS_SET(arg)               = (%_ClassOf(arg) === 'Set');
+macro IS_SET_ITERATOR(arg)      = (%_ClassOf(arg) === 'Set Iterator');
+macro IS_SHAREDARRAYBUFFER(arg) = (%_ClassOf(arg) === 'SharedArrayBuffer');
+macro IS_SIMD_VALUE(arg)        = (%_IsSimdValue(arg));
+macro IS_STRING(arg)            = (typeof(arg) === 'string');
+macro IS_STRING_WRAPPER(arg)    = (%_ClassOf(arg) === 'String');
+macro IS_STRONG(arg)            = (%IsStrong(arg));
+macro IS_SYMBOL(arg)            = (typeof(arg) === 'symbol');
+macro IS_SYMBOL_WRAPPER(arg)    = (%_ClassOf(arg) === 'Symbol');
+macro IS_UNDEFINED(arg)         = (arg === (void 0));
+macro IS_WEAKMAP(arg)           = (%_ClassOf(arg) === 'WeakMap');
+macro IS_WEAKSET(arg)           = (%_ClassOf(arg) === 'WeakSet');
+
+# Macro for ES queries of the type: "Type(O) is Object."
+macro IS_RECEIVER(arg) = (%_IsJSReceiver(arg));
+
+# Macro for ES queries of the type: "IsCallable(O)"
+macro IS_CALLABLE(arg) = (typeof(arg) === 'function');
+
+# Macro for ES6 CheckObjectCoercible
+# Will throw a TypeError of the form "[functionName] called on null or undefined".
+macro CHECK_OBJECT_COERCIBLE(arg, functionName) = if (IS_NULL(%IS_VAR(arg)) || IS_UNDEFINED(arg)) throw MakeTypeError(kCalledOnNullOrUndefined, functionName);
+
+# Inline macros. Use %IS_VAR to make sure arg is evaluated only once.
+macro NUMBER_IS_NAN(arg) = (!%_IsSmi(%IS_VAR(arg)) && !(arg == arg));
+macro NUMBER_IS_FINITE(arg) = (%_IsSmi(%IS_VAR(arg)) || ((arg == arg) && (arg != 1/0) && (arg != -1/0)));
+macro TO_BOOLEAN(arg) = (!!(arg));
+macro TO_INTEGER(arg) = (%_ToInteger(arg));
+macro TO_INTEGER_MAP_MINUS_ZERO(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : %NumberToIntegerMapMinusZero(arg));
+macro TO_INT32(arg) = ((arg) | 0);
+macro TO_UINT32(arg) = ((arg) >>> 0);
+macro TO_LENGTH(arg) = (%_ToLength(arg));
+macro TO_LENGTH_OR_UINT32(arg) = (FLAG_harmony_tolength ? TO_LENGTH(arg) : TO_UINT32(arg));
+macro TO_LENGTH_OR_INTEGER(arg) = (FLAG_harmony_tolength ? TO_LENGTH(arg) : TO_INTEGER(arg));
+macro TO_STRING(arg) = (%_ToString(arg));
+macro TO_NUMBER(arg) = (%_ToNumber(arg));
+macro TO_OBJECT(arg) = (%_ToObject(arg));
+macro TO_PRIMITIVE(arg) = (%_ToPrimitive(arg));
+macro TO_PRIMITIVE_NUMBER(arg) = (%_ToPrimitive_Number(arg));
+macro TO_PRIMITIVE_STRING(arg) = (%_ToPrimitive_String(arg));
+macro TO_NAME(arg) = (%_ToName(arg));
+macro JSON_NUMBER_TO_STRING(arg) = ((%_IsSmi(%IS_VAR(arg)) || arg - arg == 0) ? %_NumberToString(arg) : "null");
+macro HAS_OWN_PROPERTY(arg, index) = (%_Call(ObjectHasOwnProperty, arg, index));
+macro HAS_INDEX(array, index, is_array) = ((is_array && %_HasFastPackedElements(%IS_VAR(array))) ? (index < array.length) : (index in array));
+
+# Private names.
+macro IS_PRIVATE(sym) = (%SymbolIsPrivate(sym));
+macro HAS_PRIVATE(obj, sym) = (%HasOwnProperty(obj, sym));
+macro HAS_DEFINED_PRIVATE(obj, sym) = (!IS_UNDEFINED(obj[sym]));
+macro GET_PRIVATE(obj, sym) = (obj[sym]);
+macro SET_PRIVATE(obj, sym, val) = (obj[sym] = val);
+
+# Constants.  The compiler constant folds them.
+define INFINITY = (1/0);
+define UNDEFINED = (void 0);
+
+# Macros implemented in Python.
+python macro CHAR_CODE(str) = ord(str[1]);
+
+# Constants used on an array to implement the properties of the RegExp object.
+define REGEXP_NUMBER_OF_CAPTURES = 0;
+define REGEXP_FIRST_CAPTURE = 3;
+
+# Macros for internal slot access.
+macro REGEXP_GLOBAL(regexp) = (%_RegExpFlags(regexp) & 1);
+macro REGEXP_IGNORE_CASE(regexp) = (%_RegExpFlags(regexp) & 2);
+macro REGEXP_MULTILINE(regexp) = (%_RegExpFlags(regexp) & 4);
+macro REGEXP_STICKY(regexp) = (%_RegExpFlags(regexp) & 8);
+macro REGEXP_UNICODE(regexp) = (%_RegExpFlags(regexp) & 16);
+macro REGEXP_SOURCE(regexp) = (%_RegExpSource(regexp));
+
+# We can't put macros in macros so we use constants here.
+# REGEXP_NUMBER_OF_CAPTURES
+macro NUMBER_OF_CAPTURES(array) = ((array)[0]);
+
+# Last input and last subject of regexp matches.
+define LAST_SUBJECT_INDEX = 1;
+macro LAST_SUBJECT(array) = ((array)[1]);
+macro LAST_INPUT(array) = ((array)[2]);
+
+# REGEXP_FIRST_CAPTURE
+macro CAPTURE(index) = (3 + (index));
+define CAPTURE0 = 3;
+define CAPTURE1 = 4;
+
+# For the regexp capture override array.  This has the same
+# format as the arguments to a function called from
+# String.prototype.replace.
+macro OVERRIDE_MATCH(override) = ((override)[0]);
+macro OVERRIDE_POS(override) = ((override)[(override).length - 2]);
+macro OVERRIDE_SUBJECT(override) = ((override)[(override).length - 1]);
+# 1-based so index of 1 returns the first capture
+macro OVERRIDE_CAPTURE(override, index) = ((override)[(index)]);
+
+# PropertyDescriptor return value indices - must match
+# PropertyDescriptorIndices in runtime-object.cc.
+define IS_ACCESSOR_INDEX = 0;
+define VALUE_INDEX = 1;
+define GETTER_INDEX = 2;
+define SETTER_INDEX = 3;
+define WRITABLE_INDEX = 4;
+define ENUMERABLE_INDEX = 5;
+define CONFIGURABLE_INDEX = 6;
+
+# For messages.js
+# Matches Script::Type from objects.h
+define TYPE_NATIVE = 0;
+define TYPE_EXTENSION = 1;
+define TYPE_NORMAL = 2;
+
+# Matches Script::CompilationType from objects.h
+define COMPILATION_TYPE_HOST = 0;
+define COMPILATION_TYPE_EVAL = 1;
+define COMPILATION_TYPE_JSON = 2;
+
+# Matches Messages::kNoLineNumberInfo from v8.h
+define kNoLineNumberInfo = 0;
+
+# Must match PropertyFilter in property-details.h
+define PROPERTY_FILTER_NONE = 0;
+define PROPERTY_FILTER_ONLY_ENUMERABLE = 2;
+define PROPERTY_FILTER_SKIP_STRINGS = 8;
+define PROPERTY_FILTER_SKIP_SYMBOLS = 16;
+
+# Use for keys, values and entries iterators.
+define ITERATOR_KIND_KEYS = 1;
+define ITERATOR_KIND_VALUES = 2;
+define ITERATOR_KIND_ENTRIES = 3;
+
+macro FIXED_ARRAY_GET(array, index) = (%_FixedArrayGet(array, (index) | 0));
+macro FIXED_ARRAY_SET(array, index, value) = (%_FixedArraySet(array, (index) | 0, value));
+# TODO(adamk): Find a more robust way to force Smi representation.
+macro FIXED_ARRAY_SET_SMI(array, index, value) = (FIXED_ARRAY_SET(array, index, (value) | 0));
+
+macro ORDERED_HASH_TABLE_BUCKET_COUNT(table) = (FIXED_ARRAY_GET(table, 0));
+macro ORDERED_HASH_TABLE_ELEMENT_COUNT(table) = (FIXED_ARRAY_GET(table, 1));
+macro ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, count) = (FIXED_ARRAY_SET_SMI(table, 1, count));
+macro ORDERED_HASH_TABLE_DELETED_COUNT(table) = (FIXED_ARRAY_GET(table, 2));
+macro ORDERED_HASH_TABLE_SET_DELETED_COUNT(table, count) = (FIXED_ARRAY_SET_SMI(table, 2, count));
+macro ORDERED_HASH_TABLE_BUCKET_AT(table, bucket) = (FIXED_ARRAY_GET(table, 3 + (bucket)));
+macro ORDERED_HASH_TABLE_SET_BUCKET_AT(table, bucket, entry) = (FIXED_ARRAY_SET(table, 3 + (bucket), entry));
+
+macro ORDERED_HASH_TABLE_HASH_TO_BUCKET(hash, numBuckets) = (hash & ((numBuckets) - 1));
+
+macro ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets) = (3 + (numBuckets) + ((entry) << 1));
+macro ORDERED_HASH_SET_KEY_AT(table, entry, numBuckets) = (FIXED_ARRAY_GET(table, ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets)));
+macro ORDERED_HASH_SET_CHAIN_AT(table, entry, numBuckets) = (FIXED_ARRAY_GET(table, ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets) + 1));
+
+macro ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets) = (3 + (numBuckets) + ((entry) * 3));
+macro ORDERED_HASH_MAP_KEY_AT(table, entry, numBuckets) = (FIXED_ARRAY_GET(table, ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets)));
+macro ORDERED_HASH_MAP_VALUE_AT(table, entry, numBuckets) = (FIXED_ARRAY_GET(table, ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets) + 1));
+macro ORDERED_HASH_MAP_CHAIN_AT(table, entry, numBuckets) = (FIXED_ARRAY_GET(table, ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets) + 2));
+
+# Must match OrderedHashTable::kNotFound.
+define NOT_FOUND = -1;
+
+# Check whether debug is active.
+define DEBUG_IS_ACTIVE = (%_DebugIsActive() != 0);
+macro DEBUG_PREPARE_STEP_IN_IF_STEPPING(function) = if (%_DebugIsActive() != 0) %DebugPrepareStepInIfStepping(function);
+
+# SharedFlag equivalents
+define kNotShared = false;
+define kShared = true;
+
+# UseCounters from include/v8.h
+define kUseAsm = 0;
+define kBreakIterator = 1;
+define kLegacyConst = 2;
+define kMarkDequeOverflow = 3;
+define kStoreBufferOverflow = 4;
+define kSlotsBufferOverflow = 5;
+define kObjectObserve = 6;
+define kForcedGC = 7;
+define kSloppyMode = 8;
+define kStrictMode = 9;
+define kStrongMode = 10;
+define kRegExpPrototypeStickyGetter = 11;
+define kRegExpPrototypeToString = 12;
+define kRegExpPrototypeUnicodeGetter = 13;
+define kIntlV8Parse = 14;
+define kIntlPattern = 15;
+define kIntlResolved = 16;
+define kPromiseChain = 17;
+define kPromiseAccept = 18;
+define kPromiseDefer = 19;
diff --git a/src/js/math.js b/src/js/math.js
new file mode 100644
index 0000000..990a7e9
--- /dev/null
+++ b/src/js/math.js
@@ -0,0 +1,356 @@
+// Copyright 2012 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.
+
+(function(global, utils) {
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+define kRandomBatchSize = 64;
+// The first two slots are reserved to persist PRNG state.
+define kRandomNumberStart = 2;
+
+var GlobalFloat64Array = global.Float64Array;
+var GlobalMath = global.Math;
+var GlobalObject = global.Object;
+var InternalArray = utils.InternalArray;
+var NaN = %GetRootNaN();
+var nextRandomIndex = kRandomBatchSize;
+var randomNumbers = UNDEFINED;
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+
+//-------------------------------------------------------------------
+
+// ECMA 262 - 15.8.2.1
+function MathAbs(x) {
+  x = +x;
+  return (x > 0) ? x : 0 - x;
+}
+
+// ECMA 262 - 15.8.2.2
+function MathAcosJS(x) {
+  return %_MathAcos(+x);
+}
+
+// ECMA 262 - 15.8.2.3
+function MathAsinJS(x) {
+  return %_MathAsin(+x);
+}
+
+// ECMA 262 - 15.8.2.4
+function MathAtanJS(x) {
+  return %_MathAtan(+x);
+}
+
+// ECMA 262 - 15.8.2.5
+// The naming of y and x matches the spec, as does the order in which
+// ToNumber (valueOf) is called.
+function MathAtan2JS(y, x) {
+  y = +y;
+  x = +x;
+  return %_MathAtan2(y, x);
+}
+
+// ECMA 262 - 15.8.2.6
+function MathCeil(x) {
+  return -%_MathFloor(-x);
+}
+
+// ECMA 262 - 15.8.2.8
+function MathExp(x) {
+  return %MathExpRT(TO_NUMBER(x));
+}
+
+// ECMA 262 - 15.8.2.9
+function MathFloorJS(x) {
+  return %_MathFloor(+x);
+}
+
+// ECMA 262 - 15.8.2.10
+function MathLog(x) {
+  return %_MathLogRT(TO_NUMBER(x));
+}
+
+// ECMA 262 - 15.8.2.11
+function MathMax(arg1, arg2) {  // length == 2
+  var length = %_ArgumentsLength();
+  if (length == 2) {
+    arg1 = TO_NUMBER(arg1);
+    arg2 = TO_NUMBER(arg2);
+    if (arg2 > arg1) return arg2;
+    if (arg1 > arg2) return arg1;
+    if (arg1 == arg2) {
+      // Make sure -0 is considered less than +0.
+      return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg2 : arg1;
+    }
+    // All comparisons failed, one of the arguments must be NaN.
+    return NaN;
+  }
+  var r = -INFINITY;
+  for (var i = 0; i < length; i++) {
+    var n = %_Arguments(i);
+    n = TO_NUMBER(n);
+    // Make sure +0 is considered greater than -0.
+    if (NUMBER_IS_NAN(n) || n > r || (r === 0 && n === 0 && %_IsMinusZero(r))) {
+      r = n;
+    }
+  }
+  return r;
+}
+
+// ECMA 262 - 15.8.2.12
+function MathMin(arg1, arg2) {  // length == 2
+  var length = %_ArgumentsLength();
+  if (length == 2) {
+    arg1 = TO_NUMBER(arg1);
+    arg2 = TO_NUMBER(arg2);
+    if (arg2 > arg1) return arg1;
+    if (arg1 > arg2) return arg2;
+    if (arg1 == arg2) {
+      // Make sure -0 is considered less than +0.
+      return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg1 : arg2;
+    }
+    // All comparisons failed, one of the arguments must be NaN.
+    return NaN;
+  }
+  var r = INFINITY;
+  for (var i = 0; i < length; i++) {
+    var n = %_Arguments(i);
+    n = TO_NUMBER(n);
+    // Make sure -0 is considered less than +0.
+    if (NUMBER_IS_NAN(n) || n < r || (r === 0 && n === 0 && %_IsMinusZero(n))) {
+      r = n;
+    }
+  }
+  return r;
+}
+
+// ECMA 262 - 15.8.2.13
+function MathPowJS(x, y) {
+  return %_MathPow(TO_NUMBER(x), TO_NUMBER(y));
+}
+
+// ECMA 262 - 15.8.2.14
+function MathRandom() {
+  if (nextRandomIndex >= kRandomBatchSize) {
+    randomNumbers = %GenerateRandomNumbers(randomNumbers);
+    nextRandomIndex = kRandomNumberStart;
+  }
+  return randomNumbers[nextRandomIndex++];
+}
+
+function MathRandomRaw() {
+  if (nextRandomIndex >= kRandomBatchSize) {
+    randomNumbers = %GenerateRandomNumbers(randomNumbers);
+    nextRandomIndex = kRandomNumberStart;
+  }
+  return %_DoubleLo(randomNumbers[nextRandomIndex++]) & 0x3FFFFFFF;
+}
+
+// ECMA 262 - 15.8.2.15
+function MathRound(x) {
+  return %RoundNumber(TO_NUMBER(x));
+}
+
+// ECMA 262 - 15.8.2.17
+function MathSqrtJS(x) {
+  return %_MathSqrt(+x);
+}
+
+// Non-standard extension.
+function MathImul(x, y) {
+  return %NumberImul(TO_NUMBER(x), TO_NUMBER(y));
+}
+
+// ES6 draft 09-27-13, section 20.2.2.28.
+function MathSign(x) {
+  x = +x;
+  if (x > 0) return 1;
+  if (x < 0) return -1;
+  // -0, 0 or NaN.
+  return x;
+}
+
+// ES6 draft 09-27-13, section 20.2.2.34.
+function MathTrunc(x) {
+  x = +x;
+  if (x > 0) return %_MathFloor(x);
+  if (x < 0) return -%_MathFloor(-x);
+  // -0, 0 or NaN.
+  return x;
+}
+
+// ES6 draft 09-27-13, section 20.2.2.5.
+function MathAsinh(x) {
+  x = TO_NUMBER(x);
+  // Idempotent for NaN, +/-0 and +/-Infinity.
+  if (x === 0 || !NUMBER_IS_FINITE(x)) return x;
+  if (x > 0) return MathLog(x + %_MathSqrt(x * x + 1));
+  // This is to prevent numerical errors caused by large negative x.
+  return -MathLog(-x + %_MathSqrt(x * x + 1));
+}
+
+// ES6 draft 09-27-13, section 20.2.2.3.
+function MathAcosh(x) {
+  x = TO_NUMBER(x);
+  if (x < 1) return NaN;
+  // Idempotent for NaN and +Infinity.
+  if (!NUMBER_IS_FINITE(x)) return x;
+  return MathLog(x + %_MathSqrt(x + 1) * %_MathSqrt(x - 1));
+}
+
+// ES6 draft 09-27-13, section 20.2.2.7.
+function MathAtanh(x) {
+  x = TO_NUMBER(x);
+  // Idempotent for +/-0.
+  if (x === 0) return x;
+  // Returns NaN for NaN and +/- Infinity.
+  if (!NUMBER_IS_FINITE(x)) return NaN;
+  return 0.5 * MathLog((1 + x) / (1 - x));
+}
+
+// ES6 draft 09-27-13, section 20.2.2.17.
+function MathHypot(x, y) {  // Function length is 2.
+  // We may want to introduce fast paths for two arguments and when
+  // normalization to avoid overflow is not necessary.  For now, we
+  // simply assume the general case.
+  var length = %_ArgumentsLength();
+  var args = new InternalArray(length);
+  var max = 0;
+  for (var i = 0; i < length; i++) {
+    var n = %_Arguments(i);
+    n = TO_NUMBER(n);
+    if (n === INFINITY || n === -INFINITY) return INFINITY;
+    n = MathAbs(n);
+    if (n > max) max = n;
+    args[i] = n;
+  }
+
+  // Kahan summation to avoid rounding errors.
+  // Normalize the numbers to the largest one to avoid overflow.
+  if (max === 0) max = 1;
+  var sum = 0;
+  var compensation = 0;
+  for (var i = 0; i < length; i++) {
+    var n = args[i] / max;
+    var summand = n * n - compensation;
+    var preliminary = sum + summand;
+    compensation = (preliminary - sum) - summand;
+    sum = preliminary;
+  }
+  return %_MathSqrt(sum) * max;
+}
+
+// ES6 draft 09-27-13, section 20.2.2.16.
+function MathFroundJS(x) {
+  return %MathFround(TO_NUMBER(x));
+}
+
+// ES6 draft 07-18-14, section 20.2.2.11
+function MathClz32JS(x) {
+  return %_MathClz32(x >>> 0);
+}
+
+// ES6 draft 09-27-13, section 20.2.2.9.
+// Cube root approximation, refer to: http://metamerist.com/cbrt/cbrt.htm
+// Using initial approximation adapted from Kahan's cbrt and 4 iterations
+// of Newton's method.
+function MathCbrt(x) {
+  x = TO_NUMBER(x);
+  if (x == 0 || !NUMBER_IS_FINITE(x)) return x;
+  return x >= 0 ? CubeRoot(x) : -CubeRoot(-x);
+}
+
+macro NEWTON_ITERATION_CBRT(x, approx)
+  (1.0 / 3.0) * (x / (approx * approx) + 2 * approx);
+endmacro
+
+function CubeRoot(x) {
+  var approx_hi = MathFloorJS(%_DoubleHi(x) / 3) + 0x2A9F7893;
+  var approx = %_ConstructDouble(approx_hi | 0, 0);
+  approx = NEWTON_ITERATION_CBRT(x, approx);
+  approx = NEWTON_ITERATION_CBRT(x, approx);
+  approx = NEWTON_ITERATION_CBRT(x, approx);
+  return NEWTON_ITERATION_CBRT(x, approx);
+}
+
+// -------------------------------------------------------------------
+
+%AddNamedProperty(GlobalMath, toStringTagSymbol, "Math", READ_ONLY | DONT_ENUM);
+
+// Set up math constants.
+utils.InstallConstants(GlobalMath, [
+  // ECMA-262, section 15.8.1.1.
+  "E", 2.7182818284590452354,
+  // ECMA-262, section 15.8.1.2.
+  "LN10", 2.302585092994046,
+  // ECMA-262, section 15.8.1.3.
+  "LN2", 0.6931471805599453,
+  // ECMA-262, section 15.8.1.4.
+  "LOG2E", 1.4426950408889634,
+  "LOG10E", 0.4342944819032518,
+  "PI", 3.1415926535897932,
+  "SQRT1_2", 0.7071067811865476,
+  "SQRT2", 1.4142135623730951
+]);
+
+// Set up non-enumerable functions of the Math object and
+// set their names.
+utils.InstallFunctions(GlobalMath, DONT_ENUM, [
+  "random", MathRandom,
+  "abs", MathAbs,
+  "acos", MathAcosJS,
+  "asin", MathAsinJS,
+  "atan", MathAtanJS,
+  "ceil", MathCeil,
+  "exp", MathExp,
+  "floor", MathFloorJS,
+  "log", MathLog,
+  "round", MathRound,
+  "sqrt", MathSqrtJS,
+  "atan2", MathAtan2JS,
+  "pow", MathPowJS,
+  "max", MathMax,
+  "min", MathMin,
+  "imul", MathImul,
+  "sign", MathSign,
+  "trunc", MathTrunc,
+  "asinh", MathAsinh,
+  "acosh", MathAcosh,
+  "atanh", MathAtanh,
+  "hypot", MathHypot,
+  "fround", MathFroundJS,
+  "clz32", MathClz32JS,
+  "cbrt", MathCbrt
+]);
+
+%SetForceInlineFlag(MathAbs);
+%SetForceInlineFlag(MathAcosJS);
+%SetForceInlineFlag(MathAsinJS);
+%SetForceInlineFlag(MathAtanJS);
+%SetForceInlineFlag(MathAtan2JS);
+%SetForceInlineFlag(MathCeil);
+%SetForceInlineFlag(MathClz32JS);
+%SetForceInlineFlag(MathFloorJS);
+%SetForceInlineFlag(MathRandom);
+%SetForceInlineFlag(MathSign);
+%SetForceInlineFlag(MathSqrtJS);
+%SetForceInlineFlag(MathTrunc);
+
+// -------------------------------------------------------------------
+// Exports
+
+utils.Export(function(to) {
+  to.MathAbs = MathAbs;
+  to.MathExp = MathExp;
+  to.MathFloor = MathFloorJS;
+  to.IntRandom = MathRandomRaw;
+  to.MathMax = MathMax;
+  to.MathMin = MathMin;
+});
+
+})
diff --git a/src/js/messages.js b/src/js/messages.js
new file mode 100644
index 0000000..feb14d3
--- /dev/null
+++ b/src/js/messages.js
@@ -0,0 +1,1014 @@
+// Copyright 2012 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.
+
+// -------------------------------------------------------------------
+
+(function(global, utils) {
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var ArrayJoin;
+var Bool16x8ToString;
+var Bool32x4ToString;
+var Bool8x16ToString;
+var callSiteReceiverSymbol =
+    utils.ImportNow("call_site_receiver_symbol");
+var callSiteFunctionSymbol =
+    utils.ImportNow("call_site_function_symbol");
+var callSitePositionSymbol =
+    utils.ImportNow("call_site_position_symbol");
+var callSiteStrictSymbol =
+    utils.ImportNow("call_site_strict_symbol");
+var FLAG_harmony_tostring;
+var Float32x4ToString;
+var formattedStackTraceSymbol =
+    utils.ImportNow("formatted_stack_trace_symbol");
+var GlobalObject = global.Object;
+var Int16x8ToString;
+var Int32x4ToString;
+var Int8x16ToString;
+var InternalArray = utils.InternalArray;
+var internalErrorSymbol = utils.ImportNow("internal_error_symbol");
+var ObjectDefineProperty;
+var ObjectToString = utils.ImportNow("object_to_string");
+var Script = utils.ImportNow("Script");
+var stackTraceSymbol = utils.ImportNow("stack_trace_symbol");
+var StringCharAt;
+var StringIndexOf;
+var StringSubstring;
+var SymbolToString;
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+var Uint16x8ToString;
+var Uint32x4ToString;
+var Uint8x16ToString;
+
+utils.Import(function(from) {
+  ArrayJoin = from.ArrayJoin;
+  Bool16x8ToString = from.Bool16x8ToString;
+  Bool32x4ToString = from.Bool32x4ToString;
+  Bool8x16ToString = from.Bool8x16ToString;
+  Float32x4ToString = from.Float32x4ToString;
+  Int16x8ToString = from.Int16x8ToString;
+  Int32x4ToString = from.Int32x4ToString;
+  Int8x16ToString = from.Int8x16ToString;
+  ObjectDefineProperty = from.ObjectDefineProperty;
+  StringCharAt = from.StringCharAt;
+  StringIndexOf = from.StringIndexOf;
+  StringSubstring = from.StringSubstring;
+  SymbolToString = from.SymbolToString;
+  Uint16x8ToString = from.Uint16x8ToString;
+  Uint32x4ToString = from.Uint32x4ToString;
+  Uint8x16ToString = from.Uint8x16ToString;
+});
+
+utils.ImportFromExperimental(function(from) {
+  FLAG_harmony_tostring = from.FLAG_harmony_tostring;
+});
+
+// -------------------------------------------------------------------
+
+var GlobalError;
+var GlobalTypeError;
+var GlobalRangeError;
+var GlobalURIError;
+var GlobalSyntaxError;
+var GlobalReferenceError;
+var GlobalEvalError;
+
+
+function NoSideEffectsObjectToString() {
+  if (IS_UNDEFINED(this)) return "[object Undefined]";
+  if (IS_NULL(this)) return "[object Null]";
+  var O = TO_OBJECT(this);
+  var builtinTag = %_ClassOf(O);
+  var tag;
+  if (FLAG_harmony_tostring) {
+    tag = %GetDataProperty(O, toStringTagSymbol);
+    if (!IS_STRING(tag)) {
+      tag = builtinTag;
+    }
+  } else {
+    tag = builtinTag;
+  }
+  return `[object ${tag}]`;
+}
+
+function IsErrorObject(obj) {
+  return HAS_PRIVATE(obj, stackTraceSymbol);
+}
+
+function NoSideEffectsErrorToString() {
+  var name = %GetDataProperty(this, "name");
+  var message = %GetDataProperty(this, "message");
+  name = IS_UNDEFINED(name) ? "Error" : NoSideEffectsToString(name);
+  message = IS_UNDEFINED(message) ? "" : NoSideEffectsToString(message);
+  if (name == "") return message;
+  if (message == "") return name;
+  return `${name}: ${message}`;
+}
+
+function NoSideEffectsToString(obj) {
+  if (IS_STRING(obj)) return obj;
+  if (IS_NUMBER(obj)) return %_NumberToString(obj);
+  if (IS_BOOLEAN(obj)) return obj ? 'true' : 'false';
+  if (IS_UNDEFINED(obj)) return 'undefined';
+  if (IS_NULL(obj)) return 'null';
+  if (IS_FUNCTION(obj)) {
+    var str = %FunctionToString(obj);
+    if (str.length > 128) {
+      str = %_SubString(str, 0, 111) + "...<omitted>..." +
+            %_SubString(str, str.length - 2, str.length);
+    }
+    return str;
+  }
+  if (IS_SYMBOL(obj)) return %_Call(SymbolToString, obj);
+  if (IS_SIMD_VALUE(obj)) {
+    switch (typeof(obj)) {
+      case 'float32x4': return %_Call(Float32x4ToString, obj);
+      case 'int32x4':   return %_Call(Int32x4ToString, obj);
+      case 'int16x8':   return %_Call(Int16x8ToString, obj);
+      case 'int8x16':   return %_Call(Int8x16ToString, obj);
+      case 'uint32x4':   return %_Call(Uint32x4ToString, obj);
+      case 'uint16x8':   return %_Call(Uint16x8ToString, obj);
+      case 'uint8x16':   return %_Call(Uint8x16ToString, obj);
+      case 'bool32x4':  return %_Call(Bool32x4ToString, obj);
+      case 'bool16x8':  return %_Call(Bool16x8ToString, obj);
+      case 'bool8x16':  return %_Call(Bool8x16ToString, obj);
+    }
+  }
+
+  if (IS_RECEIVER(obj)) {
+    // When internally formatting error objects, use a side-effects-free version
+    // of Error.prototype.toString independent of the actually installed
+    // toString method.
+    if (IsErrorObject(obj) ||
+        %GetDataProperty(obj, "toString") === ErrorToString) {
+      return %_Call(NoSideEffectsErrorToString, obj);
+    }
+
+    if (%GetDataProperty(obj, "toString") === ObjectToString) {
+      var constructor = %GetDataProperty(obj, "constructor");
+      if (IS_FUNCTION(constructor)) {
+        var constructor_name = %FunctionGetName(constructor);
+        if (constructor_name != "") return `#<${constructor_name}>`;
+      }
+    }
+  }
+
+  return %_Call(NoSideEffectsObjectToString, obj);
+}
+
+
+function MakeGenericError(constructor, type, arg0, arg1, arg2) {
+  var error = new constructor(FormatMessage(type, arg0, arg1, arg2));
+  error[internalErrorSymbol] = true;
+  return error;
+}
+
+
+/**
+ * Set up the Script function and constructor.
+ */
+%FunctionSetInstanceClassName(Script, 'Script');
+%AddNamedProperty(Script.prototype, 'constructor', Script,
+                  DONT_ENUM | DONT_DELETE | READ_ONLY);
+%SetCode(Script, function(x) {
+  // Script objects can only be created by the VM.
+  throw MakeError(kUnsupported);
+});
+
+
+// Helper functions; called from the runtime system.
+function FormatMessage(type, arg0, arg1, arg2) {
+  var arg0 = NoSideEffectsToString(arg0);
+  var arg1 = NoSideEffectsToString(arg1);
+  var arg2 = NoSideEffectsToString(arg2);
+  try {
+    return %FormatMessageString(type, arg0, arg1, arg2);
+  } catch (e) {
+    return "<error>";
+  }
+}
+
+
+function GetLineNumber(message) {
+  var start_position = %MessageGetStartPosition(message);
+  if (start_position == -1) return kNoLineNumberInfo;
+  var script = %MessageGetScript(message);
+  var location = script.locationFromPosition(start_position, true);
+  if (location == null) return kNoLineNumberInfo;
+  return location.line + 1;
+}
+
+
+//Returns the offset of the given position within the containing line.
+function GetColumnNumber(message) {
+  var script = %MessageGetScript(message);
+  var start_position = %MessageGetStartPosition(message);
+  var location = script.locationFromPosition(start_position, true);
+  if (location == null) return -1;
+  return location.column;
+}
+
+
+// Returns the source code line containing the given source
+// position, or the empty string if the position is invalid.
+function GetSourceLine(message) {
+  var script = %MessageGetScript(message);
+  var start_position = %MessageGetStartPosition(message);
+  var location = script.locationFromPosition(start_position, true);
+  if (location == null) return "";
+  return location.sourceText();
+}
+
+
+/**
+ * Find a line number given a specific source position.
+ * @param {number} position The source position.
+ * @return {number} 0 if input too small, -1 if input too large,
+       else the line number.
+ */
+function ScriptLineFromPosition(position) {
+  var lower = 0;
+  var upper = this.lineCount() - 1;
+  var line_ends = this.line_ends;
+
+  // We'll never find invalid positions so bail right away.
+  if (position > line_ends[upper]) {
+    return -1;
+  }
+
+  // This means we don't have to safe-guard indexing line_ends[i - 1].
+  if (position <= line_ends[0]) {
+    return 0;
+  }
+
+  // Binary search to find line # from position range.
+  while (upper >= 1) {
+    var i = (lower + upper) >> 1;
+
+    if (position > line_ends[i]) {
+      lower = i + 1;
+    } else if (position <= line_ends[i - 1]) {
+      upper = i - 1;
+    } else {
+      return i;
+    }
+  }
+
+  return -1;
+}
+
+/**
+ * Get information on a specific source position.
+ * @param {number} position The source position
+ * @param {boolean} include_resource_offset Set to true to have the resource
+ *     offset added to the location
+ * @return {SourceLocation}
+ *     If line is negative or not in the source null is returned.
+ */
+function ScriptLocationFromPosition(position,
+                                    include_resource_offset) {
+  var line = this.lineFromPosition(position);
+  if (line == -1) return null;
+
+  // Determine start, end and column.
+  var line_ends = this.line_ends;
+  var start = line == 0 ? 0 : line_ends[line - 1] + 1;
+  var end = line_ends[line];
+  if (end > 0 && %_Call(StringCharAt, this.source, end - 1) == '\r') {
+    end--;
+  }
+  var column = position - start;
+
+  // Adjust according to the offset within the resource.
+  if (include_resource_offset) {
+    line += this.line_offset;
+    if (line == this.line_offset) {
+      column += this.column_offset;
+    }
+  }
+
+  return new SourceLocation(this, position, line, column, start, end);
+}
+
+
+/**
+ * Get information on a specific source line and column possibly offset by a
+ * fixed source position. This function is used to find a source position from
+ * a line and column position. The fixed source position offset is typically
+ * used to find a source position in a function based on a line and column in
+ * the source for the function alone. The offset passed will then be the
+ * start position of the source for the function within the full script source.
+ * @param {number} opt_line The line within the source. Default value is 0
+ * @param {number} opt_column The column in within the line. Default value is 0
+ * @param {number} opt_offset_position The offset from the begining of the
+ *     source from where the line and column calculation starts.
+ *     Default value is 0
+ * @return {SourceLocation}
+ *     If line is negative or not in the source null is returned.
+ */
+function ScriptLocationFromLine(opt_line, opt_column, opt_offset_position) {
+  // Default is the first line in the script. Lines in the script is relative
+  // to the offset within the resource.
+  var line = 0;
+  if (!IS_UNDEFINED(opt_line)) {
+    line = opt_line - this.line_offset;
+  }
+
+  // Default is first column. If on the first line add the offset within the
+  // resource.
+  var column = opt_column || 0;
+  if (line == 0) {
+    column -= this.column_offset;
+  }
+
+  var offset_position = opt_offset_position || 0;
+  if (line < 0 || column < 0 || offset_position < 0) return null;
+  if (line == 0) {
+    return this.locationFromPosition(offset_position + column, false);
+  } else {
+    // Find the line where the offset position is located.
+    var offset_line = this.lineFromPosition(offset_position);
+
+    if (offset_line == -1 || offset_line + line >= this.lineCount()) {
+      return null;
+    }
+
+    return this.locationFromPosition(
+        this.line_ends[offset_line + line - 1] + 1 + column);  // line > 0 here.
+  }
+}
+
+
+/**
+ * Get a slice of source code from the script. The boundaries for the slice is
+ * specified in lines.
+ * @param {number} opt_from_line The first line (zero bound) in the slice.
+ *     Default is 0
+ * @param {number} opt_to_column The last line (zero bound) in the slice (non
+ *     inclusive). Default is the number of lines in the script
+ * @return {SourceSlice} The source slice or null of the parameters where
+ *     invalid
+ */
+function ScriptSourceSlice(opt_from_line, opt_to_line) {
+  var from_line = IS_UNDEFINED(opt_from_line) ? this.line_offset
+                                              : opt_from_line;
+  var to_line = IS_UNDEFINED(opt_to_line) ? this.line_offset + this.lineCount()
+                                          : opt_to_line;
+
+  // Adjust according to the offset within the resource.
+  from_line -= this.line_offset;
+  to_line -= this.line_offset;
+  if (from_line < 0) from_line = 0;
+  if (to_line > this.lineCount()) to_line = this.lineCount();
+
+  // Check parameters.
+  if (from_line >= this.lineCount() ||
+      to_line < 0 ||
+      from_line > to_line) {
+    return null;
+  }
+
+  var line_ends = this.line_ends;
+  var from_position = from_line == 0 ? 0 : line_ends[from_line - 1] + 1;
+  var to_position = to_line == 0 ? 0 : line_ends[to_line - 1] + 1;
+
+  // Return a source slice with line numbers re-adjusted to the resource.
+  return new SourceSlice(this,
+                         from_line + this.line_offset,
+                         to_line + this.line_offset,
+                          from_position, to_position);
+}
+
+
+function ScriptSourceLine(opt_line) {
+  // Default is the first line in the script. Lines in the script are relative
+  // to the offset within the resource.
+  var line = 0;
+  if (!IS_UNDEFINED(opt_line)) {
+    line = opt_line - this.line_offset;
+  }
+
+  // Check parameter.
+  if (line < 0 || this.lineCount() <= line) {
+    return null;
+  }
+
+  // Return the source line.
+  var line_ends = this.line_ends;
+  var start = line == 0 ? 0 : line_ends[line - 1] + 1;
+  var end = line_ends[line];
+  return %_Call(StringSubstring, this.source, start, end);
+}
+
+
+/**
+ * Returns the number of source lines.
+ * @return {number}
+ *     Number of source lines.
+ */
+function ScriptLineCount() {
+  // Return number of source lines.
+  return this.line_ends.length;
+}
+
+
+/**
+ * Returns the position of the nth line end.
+ * @return {number}
+ *     Zero-based position of the nth line end in the script.
+ */
+function ScriptLineEnd(n) {
+  return this.line_ends[n];
+}
+
+
+/**
+ * If sourceURL comment is available returns sourceURL comment contents.
+ * Otherwise, script name is returned. See
+ * http://fbug.googlecode.com/svn/branches/firebug1.1/docs/ReleaseNotes_1.1.txt
+ * and Source Map Revision 3 proposal for details on using //# sourceURL and
+ * deprecated //@ sourceURL comment to identify scripts that don't have name.
+ *
+ * @return {?string} script name if present, value for //# sourceURL comment or
+ * deprecated //@ sourceURL comment otherwise.
+ */
+function ScriptNameOrSourceURL() {
+  if (this.source_url) return this.source_url;
+  return this.name;
+}
+
+
+utils.SetUpLockedPrototype(Script, [
+    "source",
+    "name",
+    "source_url",
+    "source_mapping_url",
+    "line_ends",
+    "line_offset",
+    "column_offset"
+  ], [
+    "lineFromPosition", ScriptLineFromPosition,
+    "locationFromPosition", ScriptLocationFromPosition,
+    "locationFromLine", ScriptLocationFromLine,
+    "sourceSlice", ScriptSourceSlice,
+    "sourceLine", ScriptSourceLine,
+    "lineCount", ScriptLineCount,
+    "nameOrSourceURL", ScriptNameOrSourceURL,
+    "lineEnd", ScriptLineEnd
+  ]
+);
+
+
+/**
+ * Class for source location. A source location is a position within some
+ * source with the following properties:
+ *   script   : script object for the source
+ *   line     : source line number
+ *   column   : source column within the line
+ *   position : position within the source
+ *   start    : position of start of source context (inclusive)
+ *   end      : position of end of source context (not inclusive)
+ * Source text for the source context is the character interval
+ * [start, end[. In most cases end will point to a newline character.
+ * It might point just past the final position of the source if the last
+ * source line does not end with a newline character.
+ * @param {Script} script The Script object for which this is a location
+ * @param {number} position Source position for the location
+ * @param {number} line The line number for the location
+ * @param {number} column The column within the line for the location
+ * @param {number} start Source position for start of source context
+ * @param {number} end Source position for end of source context
+ * @constructor
+ */
+function SourceLocation(script, position, line, column, start, end) {
+  this.script = script;
+  this.position = position;
+  this.line = line;
+  this.column = column;
+  this.start = start;
+  this.end = end;
+}
+
+
+/**
+ * Get the source text for a SourceLocation
+ * @return {String}
+ *     Source text for this location.
+ */
+function SourceLocationSourceText() {
+  return %_Call(StringSubstring, this.script.source, this.start, this.end);
+}
+
+
+utils.SetUpLockedPrototype(SourceLocation,
+  ["script", "position", "line", "column", "start", "end"],
+  ["sourceText", SourceLocationSourceText]
+);
+
+
+/**
+ * Class for a source slice. A source slice is a part of a script source with
+ * the following properties:
+ *   script        : script object for the source
+ *   from_line     : line number for the first line in the slice
+ *   to_line       : source line number for the last line in the slice
+ *   from_position : position of the first character in the slice
+ *   to_position   : position of the last character in the slice
+ * The to_line and to_position are not included in the slice, that is the lines
+ * in the slice are [from_line, to_line[. Likewise the characters in the slice
+ * are [from_position, to_position[.
+ * @param {Script} script The Script object for the source slice
+ * @param {number} from_line
+ * @param {number} to_line
+ * @param {number} from_position
+ * @param {number} to_position
+ * @constructor
+ */
+function SourceSlice(script, from_line, to_line, from_position, to_position) {
+  this.script = script;
+  this.from_line = from_line;
+  this.to_line = to_line;
+  this.from_position = from_position;
+  this.to_position = to_position;
+}
+
+/**
+ * Get the source text for a SourceSlice
+ * @return {String} Source text for this slice. The last line will include
+ *     the line terminating characters (if any)
+ */
+function SourceSliceSourceText() {
+  return %_Call(StringSubstring,
+                this.script.source,
+                this.from_position,
+                this.to_position);
+}
+
+utils.SetUpLockedPrototype(SourceSlice,
+  ["script", "from_line", "to_line", "from_position", "to_position"],
+  ["sourceText", SourceSliceSourceText]
+);
+
+
+function GetStackTraceLine(recv, fun, pos, isGlobal) {
+  return new CallSite(recv, fun, pos, false).toString();
+}
+
+// ----------------------------------------------------------------------------
+// Error implementation
+
+function CallSite(receiver, fun, pos, strict_mode) {
+  if (!IS_FUNCTION(fun)) {
+    throw MakeTypeError(kCallSiteExpectsFunction, typeof fun);
+  }
+
+  if (IS_UNDEFINED(new.target)) {
+    return new CallSite(receiver, fun, pos, strict_mode);
+  }
+
+  SET_PRIVATE(this, callSiteReceiverSymbol, receiver);
+  SET_PRIVATE(this, callSiteFunctionSymbol, fun);
+  SET_PRIVATE(this, callSitePositionSymbol, TO_INT32(pos));
+  SET_PRIVATE(this, callSiteStrictSymbol, TO_BOOLEAN(strict_mode));
+}
+
+function CallSiteGetThis() {
+  return GET_PRIVATE(this, callSiteStrictSymbol)
+      ? UNDEFINED : GET_PRIVATE(this, callSiteReceiverSymbol);
+}
+
+function CallSiteGetFunction() {
+  return GET_PRIVATE(this, callSiteStrictSymbol)
+      ? UNDEFINED : GET_PRIVATE(this, callSiteFunctionSymbol);
+}
+
+function CallSiteGetPosition() {
+  return GET_PRIVATE(this, callSitePositionSymbol);
+}
+
+function CallSiteGetTypeName() {
+  return GetTypeName(GET_PRIVATE(this, callSiteReceiverSymbol), false);
+}
+
+function CallSiteIsToplevel() {
+  return %CallSiteIsToplevelRT(this);
+}
+
+function CallSiteIsEval() {
+  return %CallSiteIsEvalRT(this);
+}
+
+function CallSiteGetEvalOrigin() {
+  var script = %FunctionGetScript(GET_PRIVATE(this, callSiteFunctionSymbol));
+  return FormatEvalOrigin(script);
+}
+
+function CallSiteGetScriptNameOrSourceURL() {
+  return %CallSiteGetScriptNameOrSourceUrlRT(this);
+}
+
+function CallSiteGetFunctionName() {
+  // See if the function knows its own name
+  return %CallSiteGetFunctionNameRT(this);
+}
+
+function CallSiteGetMethodName() {
+  // See if we can find a unique property on the receiver that holds
+  // this function.
+  return %CallSiteGetMethodNameRT(this);
+}
+
+function CallSiteGetFileName() {
+  return %CallSiteGetFileNameRT(this);
+}
+
+function CallSiteGetLineNumber() {
+  return %CallSiteGetLineNumberRT(this);
+}
+
+function CallSiteGetColumnNumber() {
+  return %CallSiteGetColumnNumberRT(this);
+}
+
+function CallSiteIsNative() {
+  return %CallSiteIsNativeRT(this);
+}
+
+function CallSiteIsConstructor() {
+  return %CallSiteIsConstructorRT(this);
+}
+
+function CallSiteToString() {
+  var fileName;
+  var fileLocation = "";
+  if (this.isNative()) {
+    fileLocation = "native";
+  } else {
+    fileName = this.getScriptNameOrSourceURL();
+    if (!fileName && this.isEval()) {
+      fileLocation = this.getEvalOrigin();
+      fileLocation += ", ";  // Expecting source position to follow.
+    }
+
+    if (fileName) {
+      fileLocation += fileName;
+    } else {
+      // Source code does not originate from a file and is not native, but we
+      // can still get the source position inside the source string, e.g. in
+      // an eval string.
+      fileLocation += "<anonymous>";
+    }
+    var lineNumber = this.getLineNumber();
+    if (lineNumber != null) {
+      fileLocation += ":" + lineNumber;
+      var columnNumber = this.getColumnNumber();
+      if (columnNumber) {
+        fileLocation += ":" + columnNumber;
+      }
+    }
+  }
+
+  var line = "";
+  var functionName = this.getFunctionName();
+  var addSuffix = true;
+  var isConstructor = this.isConstructor();
+  var isMethodCall = !(this.isToplevel() || isConstructor);
+  if (isMethodCall) {
+    var typeName = GetTypeName(GET_PRIVATE(this, callSiteReceiverSymbol), true);
+    var methodName = this.getMethodName();
+    if (functionName) {
+      if (typeName && %_Call(StringIndexOf, functionName, typeName) != 0) {
+        line += typeName + ".";
+      }
+      line += functionName;
+      if (methodName &&
+          (%_Call(StringIndexOf, functionName, "." + methodName) !=
+           functionName.length - methodName.length - 1)) {
+        line += " [as " + methodName + "]";
+      }
+    } else {
+      line += typeName + "." + (methodName || "<anonymous>");
+    }
+  } else if (isConstructor) {
+    line += "new " + (functionName || "<anonymous>");
+  } else if (functionName) {
+    line += functionName;
+  } else {
+    line += fileLocation;
+    addSuffix = false;
+  }
+  if (addSuffix) {
+    line += " (" + fileLocation + ")";
+  }
+  return line;
+}
+
+utils.SetUpLockedPrototype(CallSite, ["receiver", "fun", "pos"], [
+  "getThis", CallSiteGetThis,
+  "getTypeName", CallSiteGetTypeName,
+  "isToplevel", CallSiteIsToplevel,
+  "isEval", CallSiteIsEval,
+  "getEvalOrigin", CallSiteGetEvalOrigin,
+  "getScriptNameOrSourceURL", CallSiteGetScriptNameOrSourceURL,
+  "getFunction", CallSiteGetFunction,
+  "getFunctionName", CallSiteGetFunctionName,
+  "getMethodName", CallSiteGetMethodName,
+  "getFileName", CallSiteGetFileName,
+  "getLineNumber", CallSiteGetLineNumber,
+  "getColumnNumber", CallSiteGetColumnNumber,
+  "isNative", CallSiteIsNative,
+  "getPosition", CallSiteGetPosition,
+  "isConstructor", CallSiteIsConstructor,
+  "toString", CallSiteToString
+]);
+
+
+function FormatEvalOrigin(script) {
+  var sourceURL = script.nameOrSourceURL();
+  if (sourceURL) {
+    return sourceURL;
+  }
+
+  var eval_origin = "eval at ";
+  if (script.eval_from_function_name) {
+    eval_origin += script.eval_from_function_name;
+  } else {
+    eval_origin +=  "<anonymous>";
+  }
+
+  var eval_from_script = script.eval_from_script;
+  if (eval_from_script) {
+    if (eval_from_script.compilation_type == COMPILATION_TYPE_EVAL) {
+      // eval script originated from another eval.
+      eval_origin += " (" + FormatEvalOrigin(eval_from_script) + ")";
+    } else {
+      // eval script originated from "real" source.
+      if (eval_from_script.name) {
+        eval_origin += " (" + eval_from_script.name;
+        var location = eval_from_script.locationFromPosition(
+            script.eval_from_script_position, true);
+        if (location) {
+          eval_origin += ":" + (location.line + 1);
+          eval_origin += ":" + (location.column + 1);
+        }
+        eval_origin += ")";
+      } else {
+        eval_origin += " (unknown source)";
+      }
+    }
+  }
+
+  return eval_origin;
+}
+
+
+function FormatErrorString(error) {
+  try {
+    return %_Call(ErrorToString, error);
+  } catch (e) {
+    try {
+      return "<error: " + e + ">";
+    } catch (ee) {
+      return "<error>";
+    }
+  }
+}
+
+
+function GetStackFrames(raw_stack) {
+  var frames = new InternalArray();
+  var sloppy_frames = raw_stack[0];
+  for (var i = 1; i < raw_stack.length; i += 4) {
+    var recv = raw_stack[i];
+    var fun = raw_stack[i + 1];
+    var code = raw_stack[i + 2];
+    var pc = raw_stack[i + 3];
+    var pos = %_IsSmi(code) ? code : %FunctionGetPositionForOffset(code, pc);
+    sloppy_frames--;
+    frames.push(new CallSite(recv, fun, pos, (sloppy_frames < 0)));
+  }
+  return frames;
+}
+
+
+// Flag to prevent recursive call of Error.prepareStackTrace.
+var formatting_custom_stack_trace = false;
+
+
+function FormatStackTrace(obj, raw_stack) {
+  var frames = GetStackFrames(raw_stack);
+  if (IS_FUNCTION(GlobalError.prepareStackTrace) &&
+      !formatting_custom_stack_trace) {
+    var array = [];
+    %MoveArrayContents(frames, array);
+    formatting_custom_stack_trace = true;
+    var stack_trace = UNDEFINED;
+    try {
+      stack_trace = GlobalError.prepareStackTrace(obj, array);
+    } catch (e) {
+      throw e;  // The custom formatting function threw.  Rethrow.
+    } finally {
+      formatting_custom_stack_trace = false;
+    }
+    return stack_trace;
+  }
+
+  var lines = new InternalArray();
+  lines.push(FormatErrorString(obj));
+  for (var i = 0; i < frames.length; i++) {
+    var frame = frames[i];
+    var line;
+    try {
+      line = frame.toString();
+    } catch (e) {
+      try {
+        line = "<error: " + e + ">";
+      } catch (ee) {
+        // Any code that reaches this point is seriously nasty!
+        line = "<error>";
+      }
+    }
+    lines.push("    at " + line);
+  }
+  return %_Call(ArrayJoin, lines, "\n");
+}
+
+
+function GetTypeName(receiver, requireConstructor) {
+  if (IS_NULL_OR_UNDEFINED(receiver)) return null;
+  if (IS_PROXY(receiver)) return "Proxy";
+
+  var constructor = %GetDataProperty(TO_OBJECT(receiver), "constructor");
+  if (!IS_FUNCTION(constructor)) {
+    return requireConstructor ? null : %_Call(NoSideEffectsToString, receiver);
+  }
+  return %FunctionGetName(constructor);
+}
+
+
+// Format the stack trace if not yet done, and return it.
+// Cache the formatted stack trace on the holder.
+var StackTraceGetter = function() {
+  var formatted_stack_trace = UNDEFINED;
+  var holder = this;
+  while (holder) {
+    var formatted_stack_trace =
+      GET_PRIVATE(holder, formattedStackTraceSymbol);
+    if (IS_UNDEFINED(formatted_stack_trace)) {
+      // No formatted stack trace available.
+      var stack_trace = GET_PRIVATE(holder, stackTraceSymbol);
+      if (IS_UNDEFINED(stack_trace)) {
+        // Neither formatted nor structured stack trace available.
+        // Look further up the prototype chain.
+        holder = %_GetPrototype(holder);
+        continue;
+      }
+      formatted_stack_trace = FormatStackTrace(holder, stack_trace);
+      SET_PRIVATE(holder, stackTraceSymbol, UNDEFINED);
+      SET_PRIVATE(holder, formattedStackTraceSymbol, formatted_stack_trace);
+    }
+    return formatted_stack_trace;
+  }
+  return UNDEFINED;
+};
+
+
+// If the receiver equals the holder, set the formatted stack trace that the
+// getter returns.
+var StackTraceSetter = function(v) {
+  if (IsErrorObject(this)) {
+    SET_PRIVATE(this, stackTraceSymbol, UNDEFINED);
+    SET_PRIVATE(this, formattedStackTraceSymbol, v);
+  }
+};
+
+
+// Use a dummy function since we do not actually want to capture a stack trace
+// when constructing the initial Error prototytpes.
+var captureStackTrace = function() {};
+
+
+// Set up special error type constructors.
+function SetUpError(error_function) {
+  %FunctionSetInstanceClassName(error_function, 'Error');
+  var name = error_function.name;
+  var prototype = new GlobalObject();
+  if (name !== 'Error') {
+    %InternalSetPrototype(error_function, GlobalError);
+    %InternalSetPrototype(prototype, GlobalError.prototype);
+  }
+  %FunctionSetPrototype(error_function, prototype);
+
+  %AddNamedProperty(error_function.prototype, 'name', name, DONT_ENUM);
+  %AddNamedProperty(error_function.prototype, 'message', '', DONT_ENUM);
+  %AddNamedProperty(
+      error_function.prototype, 'constructor', error_function, DONT_ENUM);
+
+  %SetCode(error_function, function(m) {
+    if (IS_UNDEFINED(new.target)) return new error_function(m);
+
+    try { captureStackTrace(this, error_function); } catch (e) { }
+    // Define all the expected properties directly on the error
+    // object. This avoids going through getters and setters defined
+    // on prototype objects.
+    if (!IS_UNDEFINED(m)) {
+      %AddNamedProperty(this, 'message', TO_STRING(m), DONT_ENUM);
+    }
+  });
+
+  %SetNativeFlag(error_function);
+  return error_function;
+};
+
+GlobalError = SetUpError(global.Error);
+GlobalEvalError = SetUpError(global.EvalError);
+GlobalRangeError = SetUpError(global.RangeError);
+GlobalReferenceError = SetUpError(global.ReferenceError);
+GlobalSyntaxError = SetUpError(global.SyntaxError);
+GlobalTypeError = SetUpError(global.TypeError);
+GlobalURIError = SetUpError(global.URIError);
+
+utils.InstallFunctions(GlobalError.prototype, DONT_ENUM,
+                       ['toString', ErrorToString]);
+
+function ErrorToString() {
+  if (!IS_RECEIVER(this)) {
+    throw MakeTypeError(kCalledOnNonObject, "Error.prototype.toString");
+  }
+
+  var name = this.name;
+  name = IS_UNDEFINED(name) ? "Error" : TO_STRING(name);
+
+  var message = this.message;
+  message = IS_UNDEFINED(message) ? "" : TO_STRING(message);
+
+  if (name == "") return message;
+  if (message == "") return name;
+  return `${name}: ${message}`
+}
+
+function MakeError(type, arg0, arg1, arg2) {
+  return MakeGenericError(GlobalError, type, arg0, arg1, arg2);
+}
+
+function MakeRangeError(type, arg0, arg1, arg2) {
+  return MakeGenericError(GlobalRangeError, type, arg0, arg1, arg2);
+}
+
+function MakeSyntaxError(type, arg0, arg1, arg2) {
+  return MakeGenericError(GlobalSyntaxError, type, arg0, arg1, arg2);
+}
+
+function MakeTypeError(type, arg0, arg1, arg2) {
+  return MakeGenericError(GlobalTypeError, type, arg0, arg1, arg2);
+}
+
+function MakeURIError() {
+  return MakeGenericError(GlobalURIError, kURIMalformed);
+}
+
+// Boilerplate for exceptions for stack overflows. Used from
+// Isolate::StackOverflow().
+var StackOverflowBoilerplate = MakeRangeError(kStackOverflow);
+utils.InstallGetterSetter(StackOverflowBoilerplate, 'stack',
+                          StackTraceGetter, StackTraceSetter)
+
+// Define actual captureStackTrace function after everything has been set up.
+captureStackTrace = function captureStackTrace(obj, cons_opt) {
+  // Define accessors first, as this may fail and throw.
+  ObjectDefineProperty(obj, 'stack', { get: StackTraceGetter,
+                                       set: StackTraceSetter,
+                                       configurable: true });
+  %CollectStackTrace(obj, cons_opt ? cons_opt : captureStackTrace);
+};
+
+GlobalError.captureStackTrace = captureStackTrace;
+
+%InstallToContext([
+  "get_stack_trace_line_fun", GetStackTraceLine,
+  "make_error_function", MakeGenericError,
+  "make_range_error", MakeRangeError,
+  "make_type_error", MakeTypeError,
+  "message_get_column_number", GetColumnNumber,
+  "message_get_line_number", GetLineNumber,
+  "message_get_source_line", GetSourceLine,
+  "no_side_effects_to_string_fun", NoSideEffectsToString,
+  "stack_overflow_boilerplate", StackOverflowBoilerplate,
+]);
+
+utils.Export(function(to) {
+  to.ErrorToString = ErrorToString;
+  to.MakeError = MakeError;
+  to.MakeRangeError = MakeRangeError;
+  to.MakeSyntaxError = MakeSyntaxError;
+  to.MakeTypeError = MakeTypeError;
+  to.MakeURIError = MakeURIError;
+});
+
+});
diff --git a/src/js/object-observe.js b/src/js/object-observe.js
new file mode 100644
index 0000000..5e256bf
--- /dev/null
+++ b/src/js/object-observe.js
@@ -0,0 +1,717 @@
+// Copyright 2012 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.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GetHash;
+var GlobalArray = global.Array;
+var GlobalObject = global.Object;
+var InternalArray = utils.InternalArray;
+var MakeTypeError;
+
+utils.Import(function(from) {
+  GetHash = from.GetHash;
+  MakeTypeError = from.MakeTypeError;
+});
+
+// -------------------------------------------------------------------
+
+// Overview:
+//
+// This file contains all of the routing and accounting for Object.observe.
+// User code will interact with these mechanisms via the Object.observe APIs
+// and, as a side effect of mutation objects which are observed. The V8 runtime
+// (both C++ and JS) will interact with these mechanisms primarily by enqueuing
+// proper change records for objects which were mutated. The Object.observe
+// routing and accounting consists primarily of three participants
+//
+// 1) ObjectInfo. This represents the observed state of a given object. It
+//    records what callbacks are observing the object, with what options, and
+//    what "change types" are in progress on the object (i.e. via
+//    notifier.performChange).
+//
+// 2) CallbackInfo. This represents a callback used for observation. It holds
+//    the records which must be delivered to the callback, as well as the global
+//    priority of the callback (which determines delivery order between
+//    callbacks).
+//
+// 3) observationState.pendingObservers. This is the set of observers which
+//    have change records which must be delivered. During "normal" delivery
+//    (i.e. not Object.deliverChangeRecords), this is the mechanism by which
+//    callbacks are invoked in the proper order until there are no more
+//    change records pending to a callback.
+//
+// Note that in order to reduce allocation and processing costs, the
+// implementation of (1) and (2) have "optimized" states which represent
+// common cases which can be handled more efficiently.
+
+var observationState;
+
+var notifierPrototype = {};
+
+// We have to wait until after bootstrapping to grab a reference to the
+// observationState object, since it's not possible to serialize that
+// reference into the snapshot.
+function GetObservationStateJS() {
+  if (IS_UNDEFINED(observationState)) {
+    observationState = %GetObservationState();
+  }
+
+  // TODO(adamk): Consider moving this code into heap.cc
+  if (IS_UNDEFINED(observationState.callbackInfoMap)) {
+    observationState.callbackInfoMap = %ObservationWeakMapCreate();
+    observationState.objectInfoMap = %ObservationWeakMapCreate();
+    observationState.notifierObjectInfoMap = %ObservationWeakMapCreate();
+    observationState.pendingObservers = null;
+    observationState.nextCallbackPriority = 0;
+    observationState.lastMicrotaskId = 0;
+  }
+
+  return observationState;
+}
+
+
+function GetPendingObservers() {
+  return GetObservationStateJS().pendingObservers;
+}
+
+
+function SetPendingObservers(pendingObservers) {
+  GetObservationStateJS().pendingObservers = pendingObservers;
+}
+
+
+function GetNextCallbackPriority() {
+  return GetObservationStateJS().nextCallbackPriority++;
+}
+
+
+function nullProtoObject() {
+  return { __proto__: null };
+}
+
+
+function TypeMapCreate() {
+  return nullProtoObject();
+}
+
+
+function TypeMapAddType(typeMap, type, ignoreDuplicate) {
+  typeMap[type] = ignoreDuplicate ? 1 : (typeMap[type] || 0) + 1;
+}
+
+
+function TypeMapRemoveType(typeMap, type) {
+  typeMap[type]--;
+}
+
+
+function TypeMapCreateFromList(typeList, length) {
+  var typeMap = TypeMapCreate();
+  for (var i = 0; i < length; i++) {
+    TypeMapAddType(typeMap, typeList[i], true);
+  }
+  return typeMap;
+}
+
+
+function TypeMapHasType(typeMap, type) {
+  return !!typeMap[type];
+}
+
+
+function TypeMapIsDisjointFrom(typeMap1, typeMap2) {
+  if (!typeMap1 || !typeMap2)
+    return true;
+
+  for (var type in typeMap1) {
+    if (TypeMapHasType(typeMap1, type) && TypeMapHasType(typeMap2, type))
+      return false;
+  }
+
+  return true;
+}
+
+
+var defaultAcceptTypes = (function() {
+  var defaultTypes = [
+    'add',
+    'update',
+    'delete',
+    'setPrototype',
+    'reconfigure',
+    'preventExtensions'
+  ];
+  return TypeMapCreateFromList(defaultTypes, defaultTypes.length);
+})();
+
+
+// An Observer is a registration to observe an object by a callback with
+// a given set of accept types. If the set of accept types is the default
+// set for Object.observe, the observer is represented as a direct reference
+// to the callback. An observer never changes its accept types and thus never
+// needs to "normalize".
+function ObserverCreate(callback, acceptList) {
+  if (IS_UNDEFINED(acceptList))
+    return callback;
+  var observer = nullProtoObject();
+  observer.callback = callback;
+  observer.accept = acceptList;
+  return observer;
+}
+
+
+function ObserverGetCallback(observer) {
+  return IS_CALLABLE(observer) ? observer : observer.callback;
+}
+
+
+function ObserverGetAcceptTypes(observer) {
+  return IS_CALLABLE(observer) ? defaultAcceptTypes : observer.accept;
+}
+
+
+function ObserverIsActive(observer, objectInfo) {
+  return TypeMapIsDisjointFrom(ObjectInfoGetPerformingTypes(objectInfo),
+                               ObserverGetAcceptTypes(observer));
+}
+
+
+function ObjectInfoGetOrCreate(object) {
+  var objectInfo = ObjectInfoGet(object);
+  if (IS_UNDEFINED(objectInfo)) {
+    if (!IS_PROXY(object)) {
+      %SetIsObserved(object);
+    }
+    objectInfo = {
+      object: object,
+      changeObservers: null,
+      notifier: null,
+      performing: null,
+      performingCount: 0,
+    };
+    %WeakCollectionSet(GetObservationStateJS().objectInfoMap,
+                       object, objectInfo, GetHash(object));
+  }
+  return objectInfo;
+}
+
+
+function ObjectInfoGet(object) {
+  return %WeakCollectionGet(GetObservationStateJS().objectInfoMap, object,
+                            GetHash(object));
+}
+
+
+function ObjectInfoGetFromNotifier(notifier) {
+  return %WeakCollectionGet(GetObservationStateJS().notifierObjectInfoMap,
+                            notifier, GetHash(notifier));
+}
+
+
+function ObjectInfoGetNotifier(objectInfo) {
+  if (IS_NULL(objectInfo.notifier)) {
+    var notifier = { __proto__: notifierPrototype };
+    objectInfo.notifier = notifier;
+    %WeakCollectionSet(GetObservationStateJS().notifierObjectInfoMap,
+                       notifier, objectInfo, GetHash(notifier));
+  }
+
+  return objectInfo.notifier;
+}
+
+
+function ChangeObserversIsOptimized(changeObservers) {
+  return IS_CALLABLE(changeObservers) ||
+         IS_CALLABLE(changeObservers.callback);
+}
+
+
+// The set of observers on an object is called 'changeObservers'. The first
+// observer is referenced directly via objectInfo.changeObservers. When a second
+// is added, changeObservers "normalizes" to become a mapping of callback
+// priority -> observer and is then stored on objectInfo.changeObservers.
+function ObjectInfoNormalizeChangeObservers(objectInfo) {
+  if (ChangeObserversIsOptimized(objectInfo.changeObservers)) {
+    var observer = objectInfo.changeObservers;
+    var callback = ObserverGetCallback(observer);
+    var callbackInfo = CallbackInfoGet(callback);
+    var priority = CallbackInfoGetPriority(callbackInfo);
+    objectInfo.changeObservers = nullProtoObject();
+    objectInfo.changeObservers[priority] = observer;
+  }
+}
+
+
+function ObjectInfoAddObserver(objectInfo, callback, acceptList) {
+  var callbackInfo = CallbackInfoGetOrCreate(callback);
+  var observer = ObserverCreate(callback, acceptList);
+
+  if (!objectInfo.changeObservers) {
+    objectInfo.changeObservers = observer;
+    return;
+  }
+
+  ObjectInfoNormalizeChangeObservers(objectInfo);
+  var priority = CallbackInfoGetPriority(callbackInfo);
+  objectInfo.changeObservers[priority] = observer;
+}
+
+function ObjectInfoRemoveObserver(objectInfo, callback) {
+  if (!objectInfo.changeObservers)
+    return;
+
+  if (ChangeObserversIsOptimized(objectInfo.changeObservers)) {
+    if (callback === ObserverGetCallback(objectInfo.changeObservers))
+      objectInfo.changeObservers = null;
+    return;
+  }
+
+  var callbackInfo = CallbackInfoGet(callback);
+  var priority = CallbackInfoGetPriority(callbackInfo);
+  objectInfo.changeObservers[priority] = null;
+}
+
+function ObjectInfoHasActiveObservers(objectInfo) {
+  if (IS_UNDEFINED(objectInfo) || !objectInfo.changeObservers)
+    return false;
+
+  if (ChangeObserversIsOptimized(objectInfo.changeObservers))
+    return ObserverIsActive(objectInfo.changeObservers, objectInfo);
+
+  for (var priority in objectInfo.changeObservers) {
+    var observer = objectInfo.changeObservers[priority];
+    if (!IS_NULL(observer) && ObserverIsActive(observer, objectInfo))
+      return true;
+  }
+
+  return false;
+}
+
+
+function ObjectInfoAddPerformingType(objectInfo, type) {
+  objectInfo.performing = objectInfo.performing || TypeMapCreate();
+  TypeMapAddType(objectInfo.performing, type);
+  objectInfo.performingCount++;
+}
+
+
+function ObjectInfoRemovePerformingType(objectInfo, type) {
+  objectInfo.performingCount--;
+  TypeMapRemoveType(objectInfo.performing, type);
+}
+
+
+function ObjectInfoGetPerformingTypes(objectInfo) {
+  return objectInfo.performingCount > 0 ? objectInfo.performing : null;
+}
+
+
+function ConvertAcceptListToTypeMap(arg) {
+  // We use undefined as a sentinel for the default accept list.
+  if (IS_UNDEFINED(arg))
+    return arg;
+
+  if (!IS_RECEIVER(arg)) throw MakeTypeError(kObserveInvalidAccept);
+
+  var len = TO_INTEGER(arg.length);
+  if (len < 0) len = 0;
+
+  return TypeMapCreateFromList(arg, len);
+}
+
+
+// CallbackInfo's optimized state is just a number which represents its global
+// priority. When a change record must be enqueued for the callback, it
+// normalizes. When delivery clears any pending change records, it re-optimizes.
+function CallbackInfoGet(callback) {
+  return %WeakCollectionGet(GetObservationStateJS().callbackInfoMap, callback,
+                            GetHash(callback));
+}
+
+
+function CallbackInfoSet(callback, callbackInfo) {
+  %WeakCollectionSet(GetObservationStateJS().callbackInfoMap,
+                     callback, callbackInfo, GetHash(callback));
+}
+
+
+function CallbackInfoGetOrCreate(callback) {
+  var callbackInfo = CallbackInfoGet(callback);
+  if (!IS_UNDEFINED(callbackInfo))
+    return callbackInfo;
+
+  var priority = GetNextCallbackPriority();
+  CallbackInfoSet(callback, priority);
+  return priority;
+}
+
+
+function CallbackInfoGetPriority(callbackInfo) {
+  if (IS_NUMBER(callbackInfo))
+    return callbackInfo;
+  else
+    return callbackInfo.priority;
+}
+
+
+function CallbackInfoNormalize(callback) {
+  var callbackInfo = CallbackInfoGet(callback);
+  if (IS_NUMBER(callbackInfo)) {
+    var priority = callbackInfo;
+    callbackInfo = new InternalArray;
+    callbackInfo.priority = priority;
+    CallbackInfoSet(callback, callbackInfo);
+  }
+  return callbackInfo;
+}
+
+
+function ObjectObserve(object, callback, acceptList) {
+  if (!IS_RECEIVER(object))
+    throw MakeTypeError(kObserveNonObject, "observe", "observe");
+  if (%IsJSGlobalProxy(object))
+    throw MakeTypeError(kObserveGlobalProxy, "observe");
+  if (%IsAccessCheckNeeded(object))
+    throw MakeTypeError(kObserveAccessChecked, "observe");
+  if (!IS_CALLABLE(callback))
+    throw MakeTypeError(kObserveNonFunction, "observe");
+  if (%object_is_frozen(callback))
+    throw MakeTypeError(kObserveCallbackFrozen);
+
+  var objectObserveFn = %GetObjectContextObjectObserve(object);
+  return objectObserveFn(object, callback, acceptList);
+}
+
+
+function NativeObjectObserve(object, callback, acceptList) {
+  var objectInfo = ObjectInfoGetOrCreate(object);
+  var typeList = ConvertAcceptListToTypeMap(acceptList);
+  ObjectInfoAddObserver(objectInfo, callback, typeList);
+  return object;
+}
+
+
+function ObjectUnobserve(object, callback) {
+  if (!IS_RECEIVER(object))
+    throw MakeTypeError(kObserveNonObject, "unobserve", "unobserve");
+  if (%IsJSGlobalProxy(object))
+    throw MakeTypeError(kObserveGlobalProxy, "unobserve");
+  if (!IS_CALLABLE(callback))
+    throw MakeTypeError(kObserveNonFunction, "unobserve");
+
+  var objectInfo = ObjectInfoGet(object);
+  if (IS_UNDEFINED(objectInfo))
+    return object;
+
+  ObjectInfoRemoveObserver(objectInfo, callback);
+  return object;
+}
+
+
+function ArrayObserve(object, callback) {
+  return ObjectObserve(object, callback, ['add',
+                                          'update',
+                                          'delete',
+                                          'splice']);
+}
+
+
+function ArrayUnobserve(object, callback) {
+  return ObjectUnobserve(object, callback);
+}
+
+
+function ObserverEnqueueIfActive(observer, objectInfo, changeRecord) {
+  if (!ObserverIsActive(observer, objectInfo) ||
+      !TypeMapHasType(ObserverGetAcceptTypes(observer), changeRecord.type)) {
+    return;
+  }
+
+  var callback = ObserverGetCallback(observer);
+  if (!%ObserverObjectAndRecordHaveSameOrigin(callback, changeRecord.object,
+                                              changeRecord)) {
+    return;
+  }
+
+  var callbackInfo = CallbackInfoNormalize(callback);
+  if (IS_NULL(GetPendingObservers())) {
+    SetPendingObservers(nullProtoObject());
+    if (DEBUG_IS_ACTIVE) {
+      var id = ++GetObservationStateJS().lastMicrotaskId;
+      var name = "Object.observe";
+      %EnqueueMicrotask(function() {
+        %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name });
+        ObserveMicrotaskRunner();
+        %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name });
+      });
+      %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name });
+    } else {
+      %EnqueueMicrotask(ObserveMicrotaskRunner);
+    }
+  }
+  GetPendingObservers()[callbackInfo.priority] = callback;
+  callbackInfo.push(changeRecord);
+}
+
+
+function ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord, type) {
+  if (!ObjectInfoHasActiveObservers(objectInfo))
+    return;
+
+  var hasType = !IS_UNDEFINED(type);
+  var newRecord = hasType ?
+      { object: objectInfo.object, type: type } :
+      { object: objectInfo.object };
+
+  for (var prop in changeRecord) {
+    if (prop === 'object' || (hasType && prop === 'type')) continue;
+    %DefineDataPropertyUnchecked(
+        newRecord, prop, changeRecord[prop], READ_ONLY + DONT_DELETE);
+  }
+  %object_freeze(newRecord);
+
+  ObjectInfoEnqueueInternalChangeRecord(objectInfo, newRecord);
+}
+
+
+function ObjectInfoEnqueueInternalChangeRecord(objectInfo, changeRecord) {
+  // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+  if (IS_SYMBOL(changeRecord.name)) return;
+
+  if (ChangeObserversIsOptimized(objectInfo.changeObservers)) {
+    var observer = objectInfo.changeObservers;
+    ObserverEnqueueIfActive(observer, objectInfo, changeRecord);
+    return;
+  }
+
+  for (var priority in objectInfo.changeObservers) {
+    var observer = objectInfo.changeObservers[priority];
+    if (IS_NULL(observer))
+      continue;
+    ObserverEnqueueIfActive(observer, objectInfo, changeRecord);
+  }
+}
+
+
+function BeginPerformSplice(array) {
+  var objectInfo = ObjectInfoGet(array);
+  if (!IS_UNDEFINED(objectInfo))
+    ObjectInfoAddPerformingType(objectInfo, 'splice');
+}
+
+
+function EndPerformSplice(array) {
+  var objectInfo = ObjectInfoGet(array);
+  if (!IS_UNDEFINED(objectInfo))
+    ObjectInfoRemovePerformingType(objectInfo, 'splice');
+}
+
+
+function EnqueueSpliceRecord(array, index, removed, addedCount) {
+  var objectInfo = ObjectInfoGet(array);
+  if (!ObjectInfoHasActiveObservers(objectInfo))
+    return;
+
+  var changeRecord = {
+    type: 'splice',
+    object: array,
+    index: index,
+    removed: removed,
+    addedCount: addedCount
+  };
+
+  %object_freeze(changeRecord);
+  %object_freeze(changeRecord.removed);
+  ObjectInfoEnqueueInternalChangeRecord(objectInfo, changeRecord);
+}
+
+
+function NotifyChange(type, object, name, oldValue) {
+  var objectInfo = ObjectInfoGet(object);
+  if (!ObjectInfoHasActiveObservers(objectInfo))
+    return;
+
+  var changeRecord;
+  if (arguments.length == 2) {
+    changeRecord = { type: type, object: object };
+  } else if (arguments.length == 3) {
+    changeRecord = { type: type, object: object, name: name };
+  } else {
+    changeRecord = {
+      type: type,
+      object: object,
+      name: name,
+      oldValue: oldValue
+    };
+  }
+
+  %object_freeze(changeRecord);
+  ObjectInfoEnqueueInternalChangeRecord(objectInfo, changeRecord);
+}
+
+
+function ObjectNotifierNotify(changeRecord) {
+  if (!IS_RECEIVER(this))
+    throw MakeTypeError(kCalledOnNonObject, "notify");
+
+  var objectInfo = ObjectInfoGetFromNotifier(this);
+  if (IS_UNDEFINED(objectInfo))
+    throw MakeTypeError(kObserveNotifyNonNotifier);
+  if (!IS_STRING(changeRecord.type))
+    throw MakeTypeError(kObserveTypeNonString);
+
+  ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord);
+}
+
+
+function ObjectNotifierPerformChange(changeType, changeFn) {
+  if (!IS_RECEIVER(this))
+    throw MakeTypeError(kCalledOnNonObject, "performChange");
+
+  var objectInfo = ObjectInfoGetFromNotifier(this);
+  if (IS_UNDEFINED(objectInfo))
+    throw MakeTypeError(kObserveNotifyNonNotifier);
+  if (!IS_STRING(changeType))
+    throw MakeTypeError(kObservePerformNonString);
+  if (!IS_CALLABLE(changeFn))
+    throw MakeTypeError(kObservePerformNonFunction);
+
+  var performChangeFn = %GetObjectContextNotifierPerformChange(objectInfo);
+  performChangeFn(objectInfo, changeType, changeFn);
+}
+
+
+function NativeObjectNotifierPerformChange(objectInfo, changeType, changeFn) {
+  ObjectInfoAddPerformingType(objectInfo, changeType);
+
+  var changeRecord;
+  try {
+    changeRecord = changeFn();
+  } finally {
+    ObjectInfoRemovePerformingType(objectInfo, changeType);
+  }
+
+  if (IS_RECEIVER(changeRecord))
+    ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord, changeType);
+}
+
+
+function ObjectGetNotifier(object) {
+  if (!IS_RECEIVER(object))
+    throw MakeTypeError(kObserveNonObject, "getNotifier", "getNotifier");
+  if (%IsJSGlobalProxy(object))
+    throw MakeTypeError(kObserveGlobalProxy, "getNotifier");
+  if (%IsAccessCheckNeeded(object))
+    throw MakeTypeError(kObserveAccessChecked, "getNotifier");
+
+  if (%object_is_frozen(object)) return null;
+
+  if (!%ObjectWasCreatedInCurrentOrigin(object)) return null;
+
+  var getNotifierFn = %GetObjectContextObjectGetNotifier(object);
+  return getNotifierFn(object);
+}
+
+
+function NativeObjectGetNotifier(object) {
+  var objectInfo = ObjectInfoGetOrCreate(object);
+  return ObjectInfoGetNotifier(objectInfo);
+}
+
+
+function CallbackDeliverPending(callback) {
+  var callbackInfo = CallbackInfoGet(callback);
+  if (IS_UNDEFINED(callbackInfo) || IS_NUMBER(callbackInfo))
+    return false;
+
+  // Clear the pending change records from callback and return it to its
+  // "optimized" state.
+  var priority = callbackInfo.priority;
+  CallbackInfoSet(callback, priority);
+
+  var pendingObservers = GetPendingObservers();
+  if (!IS_NULL(pendingObservers))
+    delete pendingObservers[priority];
+
+  // TODO: combine the following runtime calls for perf optimization.
+  var delivered = [];
+  %MoveArrayContents(callbackInfo, delivered);
+  %DeliverObservationChangeRecords(callback, delivered);
+
+  return true;
+}
+
+
+function ObjectDeliverChangeRecords(callback) {
+  if (!IS_CALLABLE(callback))
+    throw MakeTypeError(kObserveNonFunction, "deliverChangeRecords");
+
+  while (CallbackDeliverPending(callback)) {}
+}
+
+
+function ObserveMicrotaskRunner() {
+  var pendingObservers = GetPendingObservers();
+  if (!IS_NULL(pendingObservers)) {
+    SetPendingObservers(null);
+    for (var i in pendingObservers) {
+      CallbackDeliverPending(pendingObservers[i]);
+    }
+  }
+}
+
+// -------------------------------------------------------------------
+
+utils.InstallFunctions(notifierPrototype, DONT_ENUM, [
+  "notify", ObjectNotifierNotify,
+  "performChange", ObjectNotifierPerformChange
+]);
+
+var ObserveObjectMethods = [
+  "deliverChangeRecords", ObjectDeliverChangeRecords,
+  "getNotifier", ObjectGetNotifier,
+  "observe", ObjectObserve,
+  "unobserve", ObjectUnobserve
+];
+
+var ObserveArrayMethods = [
+  "observe", ArrayObserve,
+  "unobserve", ArrayUnobserve
+];
+
+// TODO(adamk): Figure out why this prototype removal has to
+// happen as part of initial snapshotting.
+var removePrototypeFn = function(f, i) {
+  if (i % 2 === 1) %FunctionRemovePrototype(f);
+};
+ObserveObjectMethods.forEach(removePrototypeFn);
+ObserveArrayMethods.forEach(removePrototypeFn);
+
+%InstallToContext([
+  "native_object_get_notifier", NativeObjectGetNotifier,
+  "native_object_notifier_perform_change", NativeObjectNotifierPerformChange,
+  "native_object_observe", NativeObjectObserve,
+  "observers_begin_perform_splice", BeginPerformSplice,
+  "observers_end_perform_splice", EndPerformSplice,
+  "observers_enqueue_splice", EnqueueSpliceRecord,
+  "observers_notify_change", NotifyChange,
+]);
+
+utils.Export(function(to) {
+  to.ObserveArrayMethods = ObserveArrayMethods;
+  to.ObserveBeginPerformSplice = BeginPerformSplice;
+  to.ObserveEndPerformSplice = EndPerformSplice;
+  to.ObserveEnqueueSpliceRecord = EnqueueSpliceRecord;
+  to.ObserveObjectMethods = ObserveObjectMethods;
+});
+
+})
diff --git a/src/js/prologue.js b/src/js/prologue.js
new file mode 100644
index 0000000..2779393
--- /dev/null
+++ b/src/js/prologue.js
@@ -0,0 +1,342 @@
+// Copyright 2015 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.
+
+(function(global, utils, extrasUtils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -----------------------------------------------------------------------
+// Utils
+
+var imports = UNDEFINED;
+var imports_from_experimental = UNDEFINED;
+var exports_container = %ExportFromRuntime({});
+var typed_array_setup = UNDEFINED;
+
+// Register context value to be initialized with a typed array in
+// Genesis::InitializeBuiltinTypedArrays.
+function SetupTypedArray(f) {
+  f.next = typed_array_setup;
+  typed_array_setup = f;
+}
+
+// Export to other scripts.
+// In normal natives, this exports functions to other normal natives.
+// In experimental natives, this exports to other experimental natives and
+// to normal natives that import using utils.ImportFromExperimental.
+function Export(f) {
+  f(exports_container);
+}
+
+
+// Import from other scripts. The actual importing happens in PostNatives and
+// PostExperimental so that we can import from scripts executed later. However,
+// that means that the import is not available until the very end. If the
+// import needs to be available immediate, use ImportNow.
+// In normal natives, this imports from other normal natives.
+// In experimental natives, this imports from other experimental natives and
+// whitelisted exports from normal natives.
+function Import(f) {
+  f.next = imports;
+  imports = f;
+}
+
+
+// Import immediately from exports of previous scripts. We need this for
+// functions called during bootstrapping. Hooking up imports in PostNatives
+// would be too late.
+function ImportNow(name) {
+  return exports_container[name];
+}
+
+
+// In normal natives, import from experimental natives.
+// Not callable from experimental natives.
+function ImportFromExperimental(f) {
+  f.next = imports_from_experimental;
+  imports_from_experimental = f;
+}
+
+
+function SetFunctionName(f, name, prefix) {
+  if (IS_SYMBOL(name)) {
+    name = "[" + %SymbolDescription(name) + "]";
+  }
+  if (IS_UNDEFINED(prefix)) {
+    %FunctionSetName(f, name);
+  } else {
+    %FunctionSetName(f, prefix + " " + name);
+  }
+}
+
+
+function InstallConstants(object, constants) {
+  %CheckIsBootstrapping();
+  %OptimizeObjectForAddingMultipleProperties(object, constants.length >> 1);
+  var attributes = DONT_ENUM | DONT_DELETE | READ_ONLY;
+  for (var i = 0; i < constants.length; i += 2) {
+    var name = constants[i];
+    var k = constants[i + 1];
+    %AddNamedProperty(object, name, k, attributes);
+  }
+  %ToFastProperties(object);
+}
+
+
+function InstallFunctions(object, attributes, functions) {
+  %CheckIsBootstrapping();
+  %OptimizeObjectForAddingMultipleProperties(object, functions.length >> 1);
+  for (var i = 0; i < functions.length; i += 2) {
+    var key = functions[i];
+    var f = functions[i + 1];
+    SetFunctionName(f, key);
+    %FunctionRemovePrototype(f);
+    %AddNamedProperty(object, key, f, attributes);
+    %SetNativeFlag(f);
+  }
+  %ToFastProperties(object);
+}
+
+
+// Helper function to install a getter-only accessor property.
+function InstallGetter(object, name, getter, attributes, prefix) {
+  %CheckIsBootstrapping();
+  if (IS_UNDEFINED(attributes)) attributes = DONT_ENUM;
+  SetFunctionName(getter, name, IS_UNDEFINED(prefix) ? "get" : prefix);
+  %FunctionRemovePrototype(getter);
+  %DefineGetterPropertyUnchecked(object, name, getter, attributes);
+  %SetNativeFlag(getter);
+}
+
+
+// Helper function to install a getter/setter accessor property.
+function InstallGetterSetter(object, name, getter, setter, attributes) {
+  %CheckIsBootstrapping();
+  if (IS_UNDEFINED(attributes)) attributes = DONT_ENUM;
+  SetFunctionName(getter, name, "get");
+  SetFunctionName(setter, name, "set");
+  %FunctionRemovePrototype(getter);
+  %FunctionRemovePrototype(setter);
+  %DefineAccessorPropertyUnchecked(object, name, getter, setter, DONT_ENUM);
+  %SetNativeFlag(getter);
+  %SetNativeFlag(setter);
+}
+
+
+// Prevents changes to the prototype of a built-in function.
+// The "prototype" property of the function object is made non-configurable,
+// and the prototype object is made non-extensible. The latter prevents
+// changing the __proto__ property.
+function SetUpLockedPrototype(
+    constructor, fields, methods) {
+  %CheckIsBootstrapping();
+  var prototype = constructor.prototype;
+  // Install functions first, because this function is used to initialize
+  // PropertyDescriptor itself.
+  var property_count = (methods.length >> 1) + (fields ? fields.length : 0);
+  if (property_count >= 4) {
+    %OptimizeObjectForAddingMultipleProperties(prototype, property_count);
+  }
+  if (fields) {
+    for (var i = 0; i < fields.length; i++) {
+      %AddNamedProperty(prototype, fields[i],
+                        UNDEFINED, DONT_ENUM | DONT_DELETE);
+    }
+  }
+  for (var i = 0; i < methods.length; i += 2) {
+    var key = methods[i];
+    var f = methods[i + 1];
+    %AddNamedProperty(prototype, key, f, DONT_ENUM | DONT_DELETE | READ_ONLY);
+    %SetNativeFlag(f);
+  }
+  %InternalSetPrototype(prototype, null);
+  %ToFastProperties(prototype);
+}
+
+
+// -----------------------------------------------------------------------
+// To be called by bootstrapper
+
+function PostNatives(utils) {
+  %CheckIsBootstrapping();
+
+  for ( ; !IS_UNDEFINED(imports); imports = imports.next) {
+    imports(exports_container);
+  }
+
+  // Whitelist of exports from normal natives to experimental natives and debug.
+  var expose_list = [
+    "ArrayToString",
+    "ErrorToString",
+    "GetIterator",
+    "GetMethod",
+    "IsNaN",
+    "MakeError",
+    "MakeTypeError",
+    "MapEntries",
+    "MapIterator",
+    "MapIteratorNext",
+    "MathMax",
+    "MathMin",
+    "MaxSimple",
+    "MinSimple",
+    "ObjectDefineProperty",
+    "ObserveArrayMethods",
+    "ObserveObjectMethods",
+    "PromiseChain",
+    "PromiseDeferred",
+    "PromiseResolved",
+    "SameValueZero",
+    "SetIterator",
+    "SetIteratorNext",
+    "SetValues",
+    "SymbolToString",
+    "ToPositiveInteger",
+    // From runtime:
+    "is_concat_spreadable_symbol",
+    "iterator_symbol",
+    "promise_status_symbol",
+    "promise_value_symbol",
+    "object_freeze",
+    "object_is_frozen",
+    "object_is_sealed",
+    "reflect_apply",
+    "reflect_construct",
+    "regexp_flags_symbol",
+    "to_string_tag_symbol",
+    "object_to_string",
+    "species_symbol",
+  ];
+
+  var filtered_exports = {};
+  %OptimizeObjectForAddingMultipleProperties(
+      filtered_exports, expose_list.length);
+  for (var key of expose_list) {
+    filtered_exports[key] = exports_container[key];
+  }
+  %ToFastProperties(filtered_exports);
+  exports_container = filtered_exports;
+
+  utils.PostNatives = UNDEFINED;
+  utils.ImportFromExperimental = UNDEFINED;
+}
+
+
+function PostExperimentals(utils) {
+  %CheckIsBootstrapping();
+  %ExportExperimentalFromRuntime(exports_container);
+  for ( ; !IS_UNDEFINED(imports); imports = imports.next) {
+    imports(exports_container);
+  }
+  for ( ; !IS_UNDEFINED(imports_from_experimental);
+          imports_from_experimental = imports_from_experimental.next) {
+    imports_from_experimental(exports_container);
+  }
+
+  utils.CreateDoubleResultArray();
+  utils.CreateDoubleResultArray = UNDEFINED;
+
+  utils.Export = UNDEFINED;
+  utils.PostDebug = UNDEFINED;
+  utils.PostExperimentals = UNDEFINED;
+  typed_array_setup = UNDEFINED;
+}
+
+
+function PostDebug(utils) {
+  for ( ; !IS_UNDEFINED(imports); imports = imports.next) {
+    imports(exports_container);
+  }
+
+  utils.CreateDoubleResultArray();
+  utils.CreateDoubleResultArray = UNDEFINED;
+
+  exports_container = UNDEFINED;
+
+  utils.Export = UNDEFINED;
+  utils.Import = UNDEFINED;
+  utils.ImportNow = UNDEFINED;
+  utils.PostDebug = UNDEFINED;
+  utils.PostExperimentals = UNDEFINED;
+  typed_array_setup = UNDEFINED;
+}
+
+
+function InitializeBuiltinTypedArrays(utils, rng_state, rempio2result) {
+  var setup_list =  typed_array_setup;
+
+  for ( ; !IS_UNDEFINED(setup_list); setup_list = setup_list.next) {
+    setup_list(rng_state, rempio2result);
+  }
+}
+
+
+// -----------------------------------------------------------------------
+
+%OptimizeObjectForAddingMultipleProperties(utils, 14);
+
+utils.Import = Import;
+utils.ImportNow = ImportNow;
+utils.Export = Export;
+utils.ImportFromExperimental = ImportFromExperimental;
+utils.SetFunctionName = SetFunctionName;
+utils.InstallConstants = InstallConstants;
+utils.InstallFunctions = InstallFunctions;
+utils.InstallGetter = InstallGetter;
+utils.InstallGetterSetter = InstallGetterSetter;
+utils.SetUpLockedPrototype = SetUpLockedPrototype;
+utils.PostNatives = PostNatives;
+utils.PostExperimentals = PostExperimentals;
+utils.PostDebug = PostDebug;
+
+%ToFastProperties(utils);
+
+// -----------------------------------------------------------------------
+
+%OptimizeObjectForAddingMultipleProperties(extrasUtils, 5);
+
+extrasUtils.logStackTrace = function logStackTrace() {
+  %DebugTrace();
+};
+
+extrasUtils.log = function log() {
+  let message = '';
+  for (const arg of arguments) {
+    message += arg;
+  }
+
+  %GlobalPrint(message);
+};
+
+// Extras need the ability to store private state on their objects without
+// exposing it to the outside world.
+
+extrasUtils.createPrivateSymbol = function createPrivateSymbol(name) {
+  return %CreatePrivateSymbol(name);
+};
+
+// These functions are key for safe meta-programming:
+// http://wiki.ecmascript.org/doku.php?id=conventions:safe_meta_programming
+//
+// Technically they could all be derived from combinations of
+// Function.prototype.{bind,call,apply} but that introduces lots of layers of
+// indirection and slowness given how un-optimized bind is.
+
+extrasUtils.simpleBind = function simpleBind(func, thisArg) {
+  return function() {
+    return %Apply(func, thisArg, arguments, 0, arguments.length);
+  };
+};
+
+extrasUtils.uncurryThis = function uncurryThis(func) {
+  return function(thisArg) {
+    return %Apply(func, thisArg, arguments, 1, arguments.length - 1);
+  };
+};
+
+%ToFastProperties(extrasUtils);
+
+})
diff --git a/src/js/promise-extra.js b/src/js/promise-extra.js
new file mode 100644
index 0000000..f6f7959
--- /dev/null
+++ b/src/js/promise-extra.js
@@ -0,0 +1,26 @@
+// Copyright 2015 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.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+var GlobalPromise = global.Promise;
+
+var PromiseChain = utils.ImportNow("PromiseChain");
+var PromiseDeferred = utils.ImportNow("PromiseDeferred");
+var PromiseResolved = utils.ImportNow("PromiseResolved");
+
+utils.InstallFunctions(GlobalPromise.prototype, DONT_ENUM, [
+  "chain", PromiseChain,
+]);
+
+utils.InstallFunctions(GlobalPromise, DONT_ENUM, [
+  "defer", PromiseDeferred,
+  "accept", PromiseResolved,
+]);
+
+})
diff --git a/src/js/promise.js b/src/js/promise.js
new file mode 100644
index 0000000..8cf6a36
--- /dev/null
+++ b/src/js/promise.js
@@ -0,0 +1,480 @@
+// Copyright 2012 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.
+
+(function(global, utils, extrasUtils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var InternalArray = utils.InternalArray;
+var MakeTypeError;
+var promiseCombinedDeferredSymbol =
+    utils.ImportNow("promise_combined_deferred_symbol");
+var promiseHasHandlerSymbol =
+    utils.ImportNow("promise_has_handler_symbol");
+var promiseOnRejectSymbol = utils.ImportNow("promise_on_reject_symbol");
+var promiseOnResolveSymbol =
+    utils.ImportNow("promise_on_resolve_symbol");
+var promiseRawSymbol = utils.ImportNow("promise_raw_symbol");
+var promiseStatusSymbol = utils.ImportNow("promise_status_symbol");
+var promiseValueSymbol = utils.ImportNow("promise_value_symbol");
+var SpeciesConstructor;
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+
+utils.Import(function(from) {
+  MakeTypeError = from.MakeTypeError;
+  SpeciesConstructor = from.SpeciesConstructor;
+});
+
+// -------------------------------------------------------------------
+
+// Status values: 0 = pending, +1 = resolved, -1 = rejected
+var lastMicrotaskId = 0;
+
+function CreateResolvingFunctions(promise) {
+  var alreadyResolved = false;
+
+  var resolve = value => {
+    if (alreadyResolved === true) return;
+    alreadyResolved = true;
+    PromiseResolve(promise, value);
+  };
+
+  var reject = reason => {
+    if (alreadyResolved === true) return;
+    alreadyResolved = true;
+    PromiseReject(promise, reason);
+  };
+
+  return {
+    __proto__: null,
+    resolve: resolve,
+    reject: reject
+  };
+}
+
+
+var GlobalPromise = function Promise(resolver) {
+  if (resolver === promiseRawSymbol) {
+    return %NewObject(GlobalPromise, new.target);
+  }
+  if (IS_UNDEFINED(new.target)) throw MakeTypeError(kNotAPromise, this);
+  if (!IS_CALLABLE(resolver))
+    throw MakeTypeError(kResolverNotAFunction, resolver);
+
+  var promise = PromiseInit(%NewObject(GlobalPromise, new.target));
+  var callbacks = CreateResolvingFunctions(promise);
+
+  try {
+    %DebugPushPromise(promise, Promise);
+    resolver(callbacks.resolve, callbacks.reject);
+  } catch (e) {
+    %_Call(callbacks.reject, UNDEFINED, e);
+  } finally {
+    %DebugPopPromise();
+  }
+
+  return promise;
+}
+
+// Core functionality.
+
+function PromiseSet(promise, status, value, onResolve, onReject) {
+  SET_PRIVATE(promise, promiseStatusSymbol, status);
+  SET_PRIVATE(promise, promiseValueSymbol, value);
+  SET_PRIVATE(promise, promiseOnResolveSymbol, onResolve);
+  SET_PRIVATE(promise, promiseOnRejectSymbol, onReject);
+  if (DEBUG_IS_ACTIVE) {
+    %DebugPromiseEvent({ promise: promise, status: status, value: value });
+  }
+  return promise;
+}
+
+function PromiseCreateAndSet(status, value) {
+  var promise = new GlobalPromise(promiseRawSymbol);
+  // If debug is active, notify about the newly created promise first.
+  if (DEBUG_IS_ACTIVE) PromiseSet(promise, 0, UNDEFINED);
+  return PromiseSet(promise, status, value);
+}
+
+function PromiseInit(promise) {
+  return PromiseSet(
+      promise, 0, UNDEFINED, new InternalArray, new InternalArray)
+}
+
+function PromiseDone(promise, status, value, promiseQueue) {
+  if (GET_PRIVATE(promise, promiseStatusSymbol) === 0) {
+    var tasks = GET_PRIVATE(promise, promiseQueue);
+    if (tasks.length) PromiseEnqueue(value, tasks, status);
+    PromiseSet(promise, status, value);
+  }
+}
+
+function PromiseHandle(value, handler, deferred) {
+  try {
+    %DebugPushPromise(deferred.promise, PromiseHandle);
+    var result = handler(value);
+    deferred.resolve(result);
+  } catch (exception) {
+    try { deferred.reject(exception); } catch (e) { }
+  } finally {
+    %DebugPopPromise();
+  }
+}
+
+function PromiseEnqueue(value, tasks, status) {
+  var id, name, instrumenting = DEBUG_IS_ACTIVE;
+  %EnqueueMicrotask(function() {
+    if (instrumenting) {
+      %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name });
+    }
+    for (var i = 0; i < tasks.length; i += 2) {
+      PromiseHandle(value, tasks[i], tasks[i + 1])
+    }
+    if (instrumenting) {
+      %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name });
+    }
+  });
+  if (instrumenting) {
+    id = ++lastMicrotaskId;
+    name = status > 0 ? "Promise.resolve" : "Promise.reject";
+    %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name });
+  }
+}
+
+function PromiseIdResolveHandler(x) { return x }
+function PromiseIdRejectHandler(r) { throw r }
+
+function PromiseNopResolver() {}
+
+// -------------------------------------------------------------------
+// Define exported functions.
+
+// For bootstrapper.
+
+function IsPromise(x) {
+  return IS_RECEIVER(x) && HAS_DEFINED_PRIVATE(x, promiseStatusSymbol);
+}
+
+function PromiseCreate() {
+  return new GlobalPromise(PromiseNopResolver)
+}
+
+function PromiseResolve(promise, x) {
+  if (x === promise) {
+    return PromiseReject(promise, MakeTypeError(kPromiseCyclic, x));
+  }
+  if (IS_RECEIVER(x)) {
+    // 25.4.1.3.2 steps 8-12
+    try {
+      var then = x.then;
+    } catch (e) {
+      return PromiseReject(promise, e);
+    }
+    if (IS_CALLABLE(then)) {
+      // PromiseResolveThenableJob
+      var id, name, instrumenting = DEBUG_IS_ACTIVE;
+      %EnqueueMicrotask(function() {
+        if (instrumenting) {
+          %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name });
+        }
+        var callbacks = CreateResolvingFunctions(promise);
+        try {
+          %_Call(then, x, callbacks.resolve, callbacks.reject);
+        } catch (e) {
+          %_Call(callbacks.reject, UNDEFINED, e);
+        }
+        if (instrumenting) {
+          %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name });
+        }
+      });
+      if (instrumenting) {
+        id = ++lastMicrotaskId;
+        name = "PromseResolveThenableJob";
+        %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name });
+      }
+      return;
+    }
+  }
+  PromiseDone(promise, +1, x, promiseOnResolveSymbol);
+}
+
+function PromiseReject(promise, r) {
+  // Check promise status to confirm that this reject has an effect.
+  // Call runtime for callbacks to the debugger or for unhandled reject.
+  if (GET_PRIVATE(promise, promiseStatusSymbol) == 0) {
+    var debug_is_active = DEBUG_IS_ACTIVE;
+    if (debug_is_active ||
+        !HAS_DEFINED_PRIVATE(promise, promiseHasHandlerSymbol)) {
+      %PromiseRejectEvent(promise, r, debug_is_active);
+    }
+  }
+  PromiseDone(promise, -1, r, promiseOnRejectSymbol)
+}
+
+// Convenience.
+
+function NewPromiseCapability(C) {
+  if (C === GlobalPromise) {
+    // Optimized case, avoid extra closure.
+    var promise = PromiseInit(new GlobalPromise(promiseRawSymbol));
+    var callbacks = CreateResolvingFunctions(promise);
+    return {
+      promise: promise,
+      resolve: callbacks.resolve,
+      reject: callbacks.reject
+    };
+  }
+
+  var result = {promise: UNDEFINED, resolve: UNDEFINED, reject: UNDEFINED };
+  result.promise = new C((resolve, reject) => {
+    if (!IS_UNDEFINED(result.resolve) || !IS_UNDEFINED(result.reject))
+        throw MakeTypeError(kPromiseExecutorAlreadyInvoked);
+    result.resolve = resolve;
+    result.reject = reject;
+  });
+
+  return result;
+}
+
+function PromiseDeferred() {
+  %IncrementUseCounter(kPromiseDefer);
+  return NewPromiseCapability(this);
+}
+
+function PromiseResolved(x) {
+  %IncrementUseCounter(kPromiseAccept);
+  return %_Call(PromiseCast, this, x);
+}
+
+function PromiseRejected(r) {
+  if (!IS_RECEIVER(this)) {
+    throw MakeTypeError(kCalledOnNonObject, PromiseRejected);
+  }
+  if (this === GlobalPromise) {
+    // Optimized case, avoid extra closure.
+    var promise = PromiseCreateAndSet(-1, r);
+    // The debug event for this would always be an uncaught promise reject,
+    // which is usually simply noise. Do not trigger that debug event.
+    %PromiseRejectEvent(promise, r, false);
+    return promise;
+  } else {
+    var promiseCapability = NewPromiseCapability(this);
+    %_Call(promiseCapability.reject, UNDEFINED, r);
+    return promiseCapability.promise;
+  }
+}
+
+// Multi-unwrapped chaining with thenable coercion.
+
+function PromiseThen(onResolve, onReject) {
+  var status = GET_PRIVATE(this, promiseStatusSymbol);
+  if (IS_UNDEFINED(status)) {
+    throw MakeTypeError(kNotAPromise, this);
+  }
+
+  var constructor = SpeciesConstructor(this, GlobalPromise);
+  onResolve = IS_CALLABLE(onResolve) ? onResolve : PromiseIdResolveHandler;
+  onReject = IS_CALLABLE(onReject) ? onReject : PromiseIdRejectHandler;
+  var deferred = NewPromiseCapability(constructor);
+  switch (status) {
+    case 0:  // Pending
+      GET_PRIVATE(this, promiseOnResolveSymbol).push(onResolve, deferred);
+      GET_PRIVATE(this, promiseOnRejectSymbol).push(onReject, deferred);
+      break;
+    case +1:  // Resolved
+      PromiseEnqueue(GET_PRIVATE(this, promiseValueSymbol),
+                     [onResolve, deferred],
+                     +1);
+      break;
+    case -1:  // Rejected
+      if (!HAS_DEFINED_PRIVATE(this, promiseHasHandlerSymbol)) {
+        // Promise has already been rejected, but had no handler.
+        // Revoke previously triggered reject event.
+        %PromiseRevokeReject(this);
+      }
+      PromiseEnqueue(GET_PRIVATE(this, promiseValueSymbol),
+                     [onReject, deferred],
+                     -1);
+      break;
+  }
+  // Mark this promise as having handler.
+  SET_PRIVATE(this, promiseHasHandlerSymbol, true);
+  if (DEBUG_IS_ACTIVE) {
+    %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this });
+  }
+  return deferred.promise;
+}
+
+// Chain is left around for now as an alias for then
+function PromiseChain(onResolve, onReject) {
+  %IncrementUseCounter(kPromiseChain);
+  return %_Call(PromiseThen, this, onResolve, onReject);
+}
+
+function PromiseCatch(onReject) {
+  return this.then(UNDEFINED, onReject);
+}
+
+// Combinators.
+
+function PromiseCast(x) {
+  if (!IS_RECEIVER(this)) {
+    throw MakeTypeError(kCalledOnNonObject, PromiseCast);
+  }
+  if (IsPromise(x) && x.constructor === this) return x;
+
+  var promiseCapability = NewPromiseCapability(this);
+  var resolveResult = %_Call(promiseCapability.resolve, UNDEFINED, x);
+  return promiseCapability.promise;
+}
+
+function PromiseAll(iterable) {
+  if (!IS_RECEIVER(this)) {
+    throw MakeTypeError(kCalledOnNonObject, "Promise.all");
+  }
+
+  var deferred = NewPromiseCapability(this);
+  var resolutions = new InternalArray();
+  var count;
+
+  function CreateResolveElementFunction(index, values, promiseCapability) {
+    var alreadyCalled = false;
+    return (x) => {
+      if (alreadyCalled === true) return;
+      alreadyCalled = true;
+      values[index] = x;
+      if (--count === 0) {
+        var valuesArray = [];
+        %MoveArrayContents(values, valuesArray);
+        %_Call(promiseCapability.resolve, UNDEFINED, valuesArray);
+      }
+    };
+  }
+
+  try {
+    var i = 0;
+    count = 1;
+    for (var value of iterable) {
+      var nextPromise = this.resolve(value);
+      ++count;
+      nextPromise.then(
+          CreateResolveElementFunction(i, resolutions, deferred),
+          deferred.reject);
+      SET_PRIVATE(deferred.reject, promiseCombinedDeferredSymbol, deferred);
+      ++i;
+    }
+
+    // 6.d
+    if (--count === 0) {
+      var valuesArray = [];
+      %MoveArrayContents(resolutions, valuesArray);
+      %_Call(deferred.resolve, UNDEFINED, valuesArray);
+    }
+
+  } catch (e) {
+    %_Call(deferred.reject, UNDEFINED, e);
+  }
+  return deferred.promise;
+}
+
+function PromiseRace(iterable) {
+  if (!IS_RECEIVER(this)) {
+    throw MakeTypeError(kCalledOnNonObject, PromiseRace);
+  }
+
+  var deferred = NewPromiseCapability(this);
+  try {
+    for (var value of iterable) {
+      this.resolve(value).then(deferred.resolve, deferred.reject);
+      SET_PRIVATE(deferred.reject, promiseCombinedDeferredSymbol, deferred);
+    }
+  } catch (e) {
+    deferred.reject(e)
+  }
+  return deferred.promise;
+}
+
+
+// Utility for debugger
+
+function PromiseHasUserDefinedRejectHandlerRecursive(promise) {
+  var queue = GET_PRIVATE(promise, promiseOnRejectSymbol);
+  if (IS_UNDEFINED(queue)) return false;
+  for (var i = 0; i < queue.length; i += 2) {
+    var handler = queue[i];
+    if (handler !== PromiseIdRejectHandler) {
+      var deferred = GET_PRIVATE(handler, promiseCombinedDeferredSymbol);
+      if (IS_UNDEFINED(deferred)) return true;
+      if (PromiseHasUserDefinedRejectHandlerRecursive(deferred.promise)) {
+        return true;
+      }
+    } else if (PromiseHasUserDefinedRejectHandlerRecursive(
+                   queue[i + 1].promise)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+// Return whether the promise will be handled by a user-defined reject
+// handler somewhere down the promise chain. For this, we do a depth-first
+// search for a reject handler that's not the default PromiseIdRejectHandler.
+function PromiseHasUserDefinedRejectHandler() {
+  return PromiseHasUserDefinedRejectHandlerRecursive(this);
+};
+
+// -------------------------------------------------------------------
+// Install exported functions.
+
+%AddNamedProperty(global, 'Promise', GlobalPromise, DONT_ENUM);
+%AddNamedProperty(GlobalPromise.prototype, toStringTagSymbol, "Promise",
+                  DONT_ENUM | READ_ONLY);
+
+utils.InstallFunctions(GlobalPromise, DONT_ENUM, [
+  "reject", PromiseRejected,
+  "all", PromiseAll,
+  "race", PromiseRace,
+  "resolve", PromiseCast
+]);
+
+utils.InstallFunctions(GlobalPromise.prototype, DONT_ENUM, [
+  "then", PromiseThen,
+  "catch", PromiseCatch
+]);
+
+%InstallToContext([
+  "promise_catch", PromiseCatch,
+  "promise_chain", PromiseChain,
+  "promise_create", PromiseCreate,
+  "promise_has_user_defined_reject_handler", PromiseHasUserDefinedRejectHandler,
+  "promise_reject", PromiseReject,
+  "promise_resolve", PromiseResolve,
+  "promise_then", PromiseThen,
+]);
+
+// This allows extras to create promises quickly without building extra
+// resolve/reject closures, and allows them to later resolve and reject any
+// promise without having to hold on to those closures forever.
+utils.InstallFunctions(extrasUtils, 0, [
+  "createPromise", PromiseCreate,
+  "resolvePromise", PromiseResolve,
+  "rejectPromise", PromiseReject
+]);
+
+// TODO(v8:4567): Allow experimental natives to remove function prototype
+[PromiseChain, PromiseDeferred, PromiseResolved].forEach(
+    fn => %FunctionRemovePrototype(fn));
+
+utils.Export(function(to) {
+  to.PromiseChain = PromiseChain;
+  to.PromiseDeferred = PromiseDeferred;
+  to.PromiseResolved = PromiseResolved;
+});
+
+})
diff --git a/src/js/proxy.js b/src/js/proxy.js
new file mode 100644
index 0000000..842bac0
--- /dev/null
+++ b/src/js/proxy.js
@@ -0,0 +1,69 @@
+// Copyright 2015 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.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// ----------------------------------------------------------------------------
+// Imports
+//
+var GlobalProxy = global.Proxy;
+var MakeTypeError;
+
+utils.Import(function(from) {
+  MakeTypeError = from.MakeTypeError;
+});
+
+//----------------------------------------------------------------------------
+
+function ProxyCreateRevocable(target, handler) {
+  var p = new GlobalProxy(target, handler);
+  return {proxy: p, revoke: () => %JSProxyRevoke(p)};
+}
+
+// -------------------------------------------------------------------
+// Proxy Builtins
+
+// Implements part of ES6 9.5.11 Proxy.[[Enumerate]]:
+// Call the trap, which should return an iterator, exhaust the iterator,
+// and return an array containing the values.
+function ProxyEnumerate(trap, handler, target) {
+  // 7. Let trapResult be ? Call(trap, handler, «target»).
+  var trap_result = %_Call(trap, handler, target);
+  // 8. If Type(trapResult) is not Object, throw a TypeError exception.
+  if (!IS_RECEIVER(trap_result)) {
+    throw MakeTypeError(kProxyEnumerateNonObject);
+  }
+  // 9. Return trapResult.
+  var result = [];
+  for (var it = trap_result.next(); !it.done; it = trap_result.next()) {
+    var key = it.value;
+    // Not yet spec'ed as of 2015-11-25, but will be spec'ed soon:
+    // If the iterator returns a non-string value, throw a TypeError.
+    if (!IS_STRING(key)) {
+      throw MakeTypeError(kProxyEnumerateNonString);
+    }
+    result.push(key);
+  }
+  return result;
+}
+
+//-------------------------------------------------------------------
+
+//Set up non-enumerable properties of the Proxy object.
+utils.InstallFunctions(GlobalProxy, DONT_ENUM, [
+  "revocable", ProxyCreateRevocable
+]);
+
+// -------------------------------------------------------------------
+// Exports
+
+%InstallToContext([
+  "proxy_enumerate", ProxyEnumerate,
+]);
+
+})
diff --git a/src/js/regexp.js b/src/js/regexp.js
new file mode 100644
index 0000000..eeacd6e
--- /dev/null
+++ b/src/js/regexp.js
@@ -0,0 +1,600 @@
+// Copyright 2012 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.
+
+(function(global, utils) {
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var FLAG_harmony_tolength;
+var GlobalObject = global.Object;
+var GlobalRegExp = global.RegExp;
+var GlobalRegExpPrototype;
+var InternalArray = utils.InternalArray;
+var InternalPackedArray = utils.InternalPackedArray;
+var MakeTypeError;
+var matchSymbol = utils.ImportNow("match_symbol");
+var searchSymbol = utils.ImportNow("search_symbol");
+var splitSymbol = utils.ImportNow("split_symbol");
+
+utils.ImportFromExperimental(function(from) {
+  FLAG_harmony_tolength = from.FLAG_harmony_tolength;
+});
+
+utils.Import(function(from) {
+  MakeTypeError = from.MakeTypeError;
+});
+
+// -------------------------------------------------------------------
+
+// Property of the builtins object for recording the result of the last
+// regexp match.  The property RegExpLastMatchInfo includes the matchIndices
+// array of the last successful regexp match (an array of start/end index
+// pairs for the match and all the captured substrings), the invariant is
+// that there are at least two capture indeces.  The array also contains
+// the subject string for the last successful match.
+var RegExpLastMatchInfo = new InternalPackedArray(
+ 2,                 // REGEXP_NUMBER_OF_CAPTURES
+ "",                // Last subject.
+ UNDEFINED,         // Last input - settable with RegExpSetInput.
+ 0,                 // REGEXP_FIRST_CAPTURE + 0
+ 0                  // REGEXP_FIRST_CAPTURE + 1
+);
+
+// -------------------------------------------------------------------
+
+function IsRegExp(o) {
+  if (!IS_RECEIVER(o)) return false;
+  var is_regexp = o[matchSymbol];
+  if (!IS_UNDEFINED(is_regexp)) return TO_BOOLEAN(is_regexp);
+  return IS_REGEXP(o);
+}
+
+
+// ES6 section 21.2.3.2.2
+function RegExpInitialize(object, pattern, flags) {
+  pattern = IS_UNDEFINED(pattern) ? '' : TO_STRING(pattern);
+  flags = IS_UNDEFINED(flags) ? '' : TO_STRING(flags);
+  %RegExpInitializeAndCompile(object, pattern, flags);
+  return object;
+}
+
+
+function PatternFlags(pattern) {
+  return (REGEXP_GLOBAL(pattern) ? 'g' : '') +
+         (REGEXP_IGNORE_CASE(pattern) ? 'i' : '') +
+         (REGEXP_MULTILINE(pattern) ? 'm' : '') +
+         (REGEXP_UNICODE(pattern) ? 'u' : '') +
+         (REGEXP_STICKY(pattern) ? 'y' : '');
+}
+
+
+function RegExpConstructor(pattern, flags) {
+  var newtarget = new.target;
+  var pattern_is_regexp = IsRegExp(pattern);
+
+  if (IS_UNDEFINED(newtarget)) {
+    newtarget = GlobalRegExp;
+
+    // ES6 section 21.2.3.1 step 3.b
+    if (pattern_is_regexp && IS_UNDEFINED(flags) &&
+        pattern.constructor === newtarget) {
+      return pattern;
+    }
+  }
+
+  if (IS_REGEXP(pattern)) {
+    if (IS_UNDEFINED(flags)) flags = PatternFlags(pattern);
+    pattern = REGEXP_SOURCE(pattern);
+
+  } else if (pattern_is_regexp) {
+    var input_pattern = pattern;
+    pattern = pattern.source;
+    if (IS_UNDEFINED(flags)) flags = input_pattern.flags;
+  }
+
+  var object = %NewObject(GlobalRegExp, newtarget);
+  return RegExpInitialize(object, pattern, flags);
+}
+
+
+function RegExpCompileJS(pattern, flags) {
+  if (!IS_REGEXP(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        "RegExp.prototype.compile", this);
+  }
+
+  if (IS_REGEXP(pattern)) {
+    if (!IS_UNDEFINED(flags)) throw MakeTypeError(kRegExpFlags);
+
+    flags = PatternFlags(pattern);
+    pattern = REGEXP_SOURCE(pattern);
+  }
+
+  RegExpInitialize(this, pattern, flags);
+
+  // Return undefined for compatibility with JSC.
+  // See http://crbug.com/585775 for web compat details.
+}
+
+
+function DoRegExpExec(regexp, string, index) {
+  return %_RegExpExec(regexp, string, index, RegExpLastMatchInfo);
+}
+
+
+// This is kind of performance sensitive, so we want to avoid unnecessary
+// type checks on inputs. But we also don't want to inline it several times
+// manually, so we use a macro :-)
+macro RETURN_NEW_RESULT_FROM_MATCH_INFO(MATCHINFO, STRING)
+  var numResults = NUMBER_OF_CAPTURES(MATCHINFO) >> 1;
+  var start = MATCHINFO[CAPTURE0];
+  var end = MATCHINFO[CAPTURE1];
+  // Calculate the substring of the first match before creating the result array
+  // to avoid an unnecessary write barrier storing the first result.
+  var first = %_SubString(STRING, start, end);
+  var result = %_RegExpConstructResult(numResults, start, STRING);
+  result[0] = first;
+  if (numResults == 1) return result;
+  var j = REGEXP_FIRST_CAPTURE + 2;
+  for (var i = 1; i < numResults; i++) {
+    start = MATCHINFO[j++];
+    if (start != -1) {
+      end = MATCHINFO[j];
+      result[i] = %_SubString(STRING, start, end);
+    }
+    j++;
+  }
+  return result;
+endmacro
+
+
+function RegExpExecNoTests(regexp, string, start) {
+  // Must be called with RegExp, string and positive integer as arguments.
+  var matchInfo = %_RegExpExec(regexp, string, start, RegExpLastMatchInfo);
+  if (matchInfo !== null) {
+    // ES6 21.2.5.2.2 step 18.
+    if (REGEXP_STICKY(regexp)) regexp.lastIndex = matchInfo[CAPTURE1];
+    RETURN_NEW_RESULT_FROM_MATCH_INFO(matchInfo, string);
+  }
+  regexp.lastIndex = 0;
+  return null;
+}
+
+
+function RegExpExecJS(string) {
+  if (!IS_REGEXP(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'RegExp.prototype.exec', this);
+  }
+
+  string = TO_STRING(string);
+  var lastIndex = this.lastIndex;
+
+  // Conversion is required by the ES2015 specification (RegExpBuiltinExec
+  // algorithm, step 4) even if the value is discarded for non-global RegExps.
+  var i = TO_LENGTH_OR_INTEGER(lastIndex);
+
+  var updateLastIndex = REGEXP_GLOBAL(this) || REGEXP_STICKY(this);
+  if (updateLastIndex) {
+    if (i < 0 || i > string.length) {
+      this.lastIndex = 0;
+      return null;
+    }
+  } else {
+    i = 0;
+  }
+
+  // matchIndices is either null or the RegExpLastMatchInfo array.
+  var matchIndices = %_RegExpExec(this, string, i, RegExpLastMatchInfo);
+
+  if (IS_NULL(matchIndices)) {
+    this.lastIndex = 0;
+    return null;
+  }
+
+  // Successful match.
+  if (updateLastIndex) {
+    this.lastIndex = RegExpLastMatchInfo[CAPTURE1];
+  }
+  RETURN_NEW_RESULT_FROM_MATCH_INFO(matchIndices, string);
+}
+
+
+// One-element cache for the simplified test regexp.
+var regexp_key;
+var regexp_val;
+
+// Section 15.10.6.3 doesn't actually make sense, but the intention seems to be
+// that test is defined in terms of String.prototype.exec. However, it probably
+// means the original value of String.prototype.exec, which is what everybody
+// else implements.
+function RegExpTest(string) {
+  if (!IS_REGEXP(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'RegExp.prototype.test', this);
+  }
+  string = TO_STRING(string);
+
+  var lastIndex = this.lastIndex;
+
+  // Conversion is required by the ES2015 specification (RegExpBuiltinExec
+  // algorithm, step 4) even if the value is discarded for non-global RegExps.
+  var i = TO_LENGTH_OR_INTEGER(lastIndex);
+
+  if (REGEXP_GLOBAL(this) || REGEXP_STICKY(this)) {
+    if (i < 0 || i > string.length) {
+      this.lastIndex = 0;
+      return false;
+    }
+    // matchIndices is either null or the RegExpLastMatchInfo array.
+    var matchIndices = %_RegExpExec(this, string, i, RegExpLastMatchInfo);
+    if (IS_NULL(matchIndices)) {
+      this.lastIndex = 0;
+      return false;
+    }
+    this.lastIndex = RegExpLastMatchInfo[CAPTURE1];
+    return true;
+  } else {
+    // Non-global, non-sticky regexp.
+    // Remove irrelevant preceeding '.*' in a test regexp.  The expression
+    // checks whether this.source starts with '.*' and that the third char is
+    // not a '?'.  But see https://code.google.com/p/v8/issues/detail?id=3560
+    var regexp = this;
+    var source = REGEXP_SOURCE(regexp);
+    if (regexp.length >= 3 &&
+        %_StringCharCodeAt(regexp, 0) == 46 &&  // '.'
+        %_StringCharCodeAt(regexp, 1) == 42 &&  // '*'
+        %_StringCharCodeAt(regexp, 2) != 63) {  // '?'
+      regexp = TrimRegExp(regexp);
+    }
+    // matchIndices is either null or the RegExpLastMatchInfo array.
+    var matchIndices = %_RegExpExec(regexp, string, 0, RegExpLastMatchInfo);
+    if (IS_NULL(matchIndices)) {
+      this.lastIndex = 0;
+      return false;
+    }
+    return true;
+  }
+}
+
+function TrimRegExp(regexp) {
+  if (!%_ObjectEquals(regexp_key, regexp)) {
+    regexp_key = regexp;
+    regexp_val =
+      new GlobalRegExp(
+          %_SubString(REGEXP_SOURCE(regexp), 2, REGEXP_SOURCE(regexp).length),
+          (REGEXP_IGNORE_CASE(regexp) ? REGEXP_MULTILINE(regexp) ? "im" : "i"
+                                      : REGEXP_MULTILINE(regexp) ? "m" : ""));
+  }
+  return regexp_val;
+}
+
+
+function RegExpToString() {
+  if (!IS_REGEXP(this)) {
+    // RegExp.prototype.toString() returns '/(?:)/' as a compatibility fix;
+    // a UseCounter is incremented to track it.
+    // TODO(littledan): Remove this workaround or standardize it
+    if (this === GlobalRegExpPrototype) {
+      %IncrementUseCounter(kRegExpPrototypeToString);
+      return '/(?:)/';
+    }
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'RegExp.prototype.toString', this);
+  }
+  var result = '/' + REGEXP_SOURCE(this) + '/';
+  if (REGEXP_GLOBAL(this)) result += 'g';
+  if (REGEXP_IGNORE_CASE(this)) result += 'i';
+  if (REGEXP_MULTILINE(this)) result += 'm';
+  if (REGEXP_UNICODE(this)) result += 'u';
+  if (REGEXP_STICKY(this)) result += 'y';
+  return result;
+}
+
+
+// ES6 21.2.5.11.
+function RegExpSplit(string, limit) {
+  // TODO(yangguo): allow non-regexp receivers.
+  if (!IS_REGEXP(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        "RegExp.prototype.@@split", this);
+  }
+  var separator = this;
+  var subject = TO_STRING(string);
+
+  limit = (IS_UNDEFINED(limit)) ? kMaxUint32 : TO_UINT32(limit);
+  var length = subject.length;
+
+  if (limit === 0) return [];
+
+  if (length === 0) {
+    if (DoRegExpExec(separator, subject, 0, 0) !== null) return [];
+    return [subject];
+  }
+
+  var currentIndex = 0;
+  var startIndex = 0;
+  var startMatch = 0;
+  var result = new InternalArray();
+
+  outer_loop:
+  while (true) {
+    if (startIndex === length) {
+      result[result.length] = %_SubString(subject, currentIndex, length);
+      break;
+    }
+
+    var matchInfo = DoRegExpExec(separator, subject, startIndex);
+    if (matchInfo === null || length === (startMatch = matchInfo[CAPTURE0])) {
+      result[result.length] = %_SubString(subject, currentIndex, length);
+      break;
+    }
+    var endIndex = matchInfo[CAPTURE1];
+
+    // We ignore a zero-length match at the currentIndex.
+    if (startIndex === endIndex && endIndex === currentIndex) {
+      startIndex++;
+      continue;
+    }
+
+    result[result.length] = %_SubString(subject, currentIndex, startMatch);
+
+    if (result.length === limit) break;
+
+    var matchinfo_len = NUMBER_OF_CAPTURES(matchInfo) + REGEXP_FIRST_CAPTURE;
+    for (var i = REGEXP_FIRST_CAPTURE + 2; i < matchinfo_len; ) {
+      var start = matchInfo[i++];
+      var end = matchInfo[i++];
+      if (end != -1) {
+        result[result.length] = %_SubString(subject, start, end);
+      } else {
+        result[result.length] = UNDEFINED;
+      }
+      if (result.length === limit) break outer_loop;
+    }
+
+    startIndex = currentIndex = endIndex;
+  }
+
+  var array_result = [];
+  %MoveArrayContents(result, array_result);
+  return array_result;
+}
+
+
+// ES6 21.2.5.6.
+function RegExpMatch(string) {
+  // TODO(yangguo): allow non-regexp receivers.
+  if (!IS_REGEXP(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        "RegExp.prototype.@@match", this);
+  }
+  var subject = TO_STRING(string);
+
+  if (!REGEXP_GLOBAL(this)) return RegExpExecNoTests(this, subject, 0);
+  this.lastIndex = 0;
+  var result = %StringMatch(subject, this, RegExpLastMatchInfo);
+  return result;
+}
+
+
+// ES6 21.2.5.9.
+function RegExpSearch(string) {
+  // TODO(yangguo): allow non-regexp receivers.
+  if (!IS_REGEXP(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        "RegExp.prototype.@@search", this);
+  }
+  var match = DoRegExpExec(this, TO_STRING(string), 0);
+  if (match) return match[CAPTURE0];
+  return -1;
+}
+
+
+// Getters for the static properties lastMatch, lastParen, leftContext, and
+// rightContext of the RegExp constructor.  The properties are computed based
+// on the captures array of the last successful match and the subject string
+// of the last successful match.
+function RegExpGetLastMatch() {
+  var regExpSubject = LAST_SUBJECT(RegExpLastMatchInfo);
+  return %_SubString(regExpSubject,
+                     RegExpLastMatchInfo[CAPTURE0],
+                     RegExpLastMatchInfo[CAPTURE1]);
+}
+
+
+function RegExpGetLastParen() {
+  var length = NUMBER_OF_CAPTURES(RegExpLastMatchInfo);
+  if (length <= 2) return '';  // There were no captures.
+  // We match the SpiderMonkey behavior: return the substring defined by the
+  // last pair (after the first pair) of elements of the capture array even if
+  // it is empty.
+  var regExpSubject = LAST_SUBJECT(RegExpLastMatchInfo);
+  var start = RegExpLastMatchInfo[CAPTURE(length - 2)];
+  var end = RegExpLastMatchInfo[CAPTURE(length - 1)];
+  if (start != -1 && end != -1) {
+    return %_SubString(regExpSubject, start, end);
+  }
+  return "";
+}
+
+
+function RegExpGetLeftContext() {
+  var start_index;
+  var subject;
+  start_index = RegExpLastMatchInfo[CAPTURE0];
+  subject = LAST_SUBJECT(RegExpLastMatchInfo);
+  return %_SubString(subject, 0, start_index);
+}
+
+
+function RegExpGetRightContext() {
+  var start_index;
+  var subject;
+  start_index = RegExpLastMatchInfo[CAPTURE1];
+  subject = LAST_SUBJECT(RegExpLastMatchInfo);
+  return %_SubString(subject, start_index, subject.length);
+}
+
+
+// The properties $1..$9 are the first nine capturing substrings of the last
+// successful match, or ''.  The function RegExpMakeCaptureGetter will be
+// called with indices from 1 to 9.
+function RegExpMakeCaptureGetter(n) {
+  return function foo() {
+    var index = n * 2;
+    if (index >= NUMBER_OF_CAPTURES(RegExpLastMatchInfo)) return '';
+    var matchStart = RegExpLastMatchInfo[CAPTURE(index)];
+    var matchEnd = RegExpLastMatchInfo[CAPTURE(index + 1)];
+    if (matchStart == -1 || matchEnd == -1) return '';
+    return %_SubString(LAST_SUBJECT(RegExpLastMatchInfo), matchStart, matchEnd);
+  };
+}
+
+
+// ES6 21.2.5.4.
+function RegExpGetGlobal() {
+  if (!IS_REGEXP(this)) {
+    // TODO(littledan): Remove this RegExp compat workaround
+    if (this === GlobalRegExpPrototype) {
+      return UNDEFINED;
+    }
+    throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.global");
+  }
+  return !!REGEXP_GLOBAL(this);
+}
+%FunctionSetName(RegExpGetGlobal, "RegExp.prototype.global");
+%SetNativeFlag(RegExpGetGlobal);
+
+
+// ES6 21.2.5.5.
+function RegExpGetIgnoreCase() {
+  if (!IS_REGEXP(this)) {
+    // TODO(littledan): Remove this RegExp compat workaround
+    if (this === GlobalRegExpPrototype) {
+      return UNDEFINED;
+    }
+    throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.ignoreCase");
+  }
+  return !!REGEXP_IGNORE_CASE(this);
+}
+%FunctionSetName(RegExpGetIgnoreCase, "RegExp.prototype.ignoreCase");
+%SetNativeFlag(RegExpGetIgnoreCase);
+
+
+// ES6 21.2.5.7.
+function RegExpGetMultiline() {
+  if (!IS_REGEXP(this)) {
+    // TODO(littledan): Remove this RegExp compat workaround
+    if (this === GlobalRegExpPrototype) {
+      return UNDEFINED;
+    }
+    throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.multiline");
+  }
+  return !!REGEXP_MULTILINE(this);
+}
+%FunctionSetName(RegExpGetMultiline, "RegExp.prototype.multiline");
+%SetNativeFlag(RegExpGetMultiline);
+
+
+// ES6 21.2.5.10.
+function RegExpGetSource() {
+  if (!IS_REGEXP(this)) {
+    // TODO(littledan): Remove this RegExp compat workaround
+    if (this === GlobalRegExpPrototype) {
+      return UNDEFINED;
+    }
+    throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.source");
+  }
+  return REGEXP_SOURCE(this);
+}
+%FunctionSetName(RegExpGetSource, "RegExp.prototype.source");
+%SetNativeFlag(RegExpGetSource);
+
+// -------------------------------------------------------------------
+
+%FunctionSetInstanceClassName(GlobalRegExp, 'RegExp');
+GlobalRegExpPrototype = new GlobalObject();
+%FunctionSetPrototype(GlobalRegExp, GlobalRegExpPrototype);
+%AddNamedProperty(
+    GlobalRegExp.prototype, 'constructor', GlobalRegExp, DONT_ENUM);
+%SetCode(GlobalRegExp, RegExpConstructor);
+
+utils.InstallFunctions(GlobalRegExp.prototype, DONT_ENUM, [
+  "exec", RegExpExecJS,
+  "test", RegExpTest,
+  "toString", RegExpToString,
+  "compile", RegExpCompileJS,
+  matchSymbol, RegExpMatch,
+  searchSymbol, RegExpSearch,
+  splitSymbol, RegExpSplit,
+]);
+
+utils.InstallGetter(GlobalRegExp.prototype, 'global', RegExpGetGlobal);
+utils.InstallGetter(GlobalRegExp.prototype, 'ignoreCase', RegExpGetIgnoreCase);
+utils.InstallGetter(GlobalRegExp.prototype, 'multiline', RegExpGetMultiline);
+utils.InstallGetter(GlobalRegExp.prototype, 'source', RegExpGetSource);
+
+// The length of compile is 1 in SpiderMonkey.
+%FunctionSetLength(GlobalRegExp.prototype.compile, 1);
+
+// The properties `input` and `$_` are aliases for each other.  When this
+// value is set the value it is set to is coerced to a string.
+// Getter and setter for the input.
+var RegExpGetInput = function() {
+  var regExpInput = LAST_INPUT(RegExpLastMatchInfo);
+  return IS_UNDEFINED(regExpInput) ? "" : regExpInput;
+};
+var RegExpSetInput = function(string) {
+  LAST_INPUT(RegExpLastMatchInfo) = TO_STRING(string);
+};
+
+%OptimizeObjectForAddingMultipleProperties(GlobalRegExp, 22);
+utils.InstallGetterSetter(GlobalRegExp, 'input', RegExpGetInput, RegExpSetInput,
+                          DONT_DELETE);
+utils.InstallGetterSetter(GlobalRegExp, '$_', RegExpGetInput, RegExpSetInput,
+                          DONT_ENUM | DONT_DELETE);
+
+
+var NoOpSetter = function(ignored) {};
+
+
+// Static properties set by a successful match.
+utils.InstallGetterSetter(GlobalRegExp, 'lastMatch', RegExpGetLastMatch,
+                          NoOpSetter, DONT_DELETE);
+utils.InstallGetterSetter(GlobalRegExp, '$&', RegExpGetLastMatch, NoOpSetter,
+                          DONT_ENUM | DONT_DELETE);
+utils.InstallGetterSetter(GlobalRegExp, 'lastParen', RegExpGetLastParen,
+                          NoOpSetter, DONT_DELETE);
+utils.InstallGetterSetter(GlobalRegExp, '$+', RegExpGetLastParen, NoOpSetter,
+                          DONT_ENUM | DONT_DELETE);
+utils.InstallGetterSetter(GlobalRegExp, 'leftContext', RegExpGetLeftContext,
+                          NoOpSetter, DONT_DELETE);
+utils.InstallGetterSetter(GlobalRegExp, '$`', RegExpGetLeftContext, NoOpSetter,
+                          DONT_ENUM | DONT_DELETE);
+utils.InstallGetterSetter(GlobalRegExp, 'rightContext', RegExpGetRightContext,
+                          NoOpSetter, DONT_DELETE);
+utils.InstallGetterSetter(GlobalRegExp, "$'", RegExpGetRightContext, NoOpSetter,
+                          DONT_ENUM | DONT_DELETE);
+
+for (var i = 1; i < 10; ++i) {
+  utils.InstallGetterSetter(GlobalRegExp, '$' + i, RegExpMakeCaptureGetter(i),
+                            NoOpSetter, DONT_DELETE);
+}
+%ToFastProperties(GlobalRegExp);
+
+// -------------------------------------------------------------------
+// Exports
+
+utils.Export(function(to) {
+  to.RegExpExec = DoRegExpExec;
+  to.RegExpExecNoTests = RegExpExecNoTests;
+  to.RegExpLastMatchInfo = RegExpLastMatchInfo;
+  to.RegExpTest = RegExpTest;
+});
+
+})
diff --git a/src/js/runtime.js b/src/js/runtime.js
new file mode 100644
index 0000000..301d75a
--- /dev/null
+++ b/src/js/runtime.js
@@ -0,0 +1,191 @@
+// Copyright 2006-2008 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.
+
+// This files contains runtime support implemented in JavaScript.
+
+// CAUTION: Some of the functions specified in this file are called
+// directly from compiled code. These are the functions with names in
+// ALL CAPS. The compiled code passes the first argument in 'this'.
+
+
+// The following declarations are shared with other native JS files.
+// They are all declared at this one spot to avoid redeclaration errors.
+
+(function(global, utils) {
+
+%CheckIsBootstrapping();
+
+var FLAG_harmony_species;
+var GlobalArray = global.Array;
+var GlobalBoolean = global.Boolean;
+var GlobalString = global.String;
+var MakeRangeError;
+var MakeTypeError;
+var speciesSymbol;
+
+utils.Import(function(from) {
+  MakeRangeError = from.MakeRangeError;
+  MakeTypeError = from.MakeTypeError;
+  speciesSymbol = from.species_symbol;
+});
+
+utils.ImportFromExperimental(function(from) {
+  FLAG_harmony_species = from.FLAG_harmony_species;
+});
+
+// ----------------------------------------------------------------------------
+
+/* -----------------------------
+   - - -   H e l p e r s   - - -
+   -----------------------------
+*/
+
+function CONCAT_ITERABLE_TO_ARRAY(iterable) {
+  return %concat_iterable_to_array(this, iterable);
+};
+
+
+/* -------------------------------------
+   - - -   C o n v e r s i o n s   - - -
+   -------------------------------------
+*/
+
+// ES5, section 9.12
+function SameValue(x, y) {
+  if (typeof x != typeof y) return false;
+  if (IS_NUMBER(x)) {
+    if (NUMBER_IS_NAN(x) && NUMBER_IS_NAN(y)) return true;
+    // x is +0 and y is -0 or vice versa.
+    if (x === 0 && y === 0 && %_IsMinusZero(x) != %_IsMinusZero(y)) {
+      return false;
+    }
+  }
+  if (IS_SIMD_VALUE(x)) return %SimdSameValue(x, y);
+  return x === y;
+}
+
+
+// ES6, section 7.2.4
+function SameValueZero(x, y) {
+  if (typeof x != typeof y) return false;
+  if (IS_NUMBER(x)) {
+    if (NUMBER_IS_NAN(x) && NUMBER_IS_NAN(y)) return true;
+  }
+  if (IS_SIMD_VALUE(x)) return %SimdSameValueZero(x, y);
+  return x === y;
+}
+
+
+function ConcatIterableToArray(target, iterable) {
+   var index = target.length;
+   for (var element of iterable) {
+     AddIndexedProperty(target, index++, element);
+   }
+   return target;
+}
+
+
+/* ---------------------------------
+   - - -   U t i l i t i e s   - - -
+   ---------------------------------
+*/
+
+
+// This function should be called rather than %AddElement in contexts where the
+// argument might not be less than 2**32-1. ES2015 ToLength semantics mean that
+// this is a concern at basically all callsites.
+function AddIndexedProperty(obj, index, value) {
+  if (index === TO_UINT32(index) && index !== kMaxUint32) {
+    %AddElement(obj, index, value);
+  } else {
+    %AddNamedProperty(obj, TO_STRING(index), value, NONE);
+  }
+}
+%SetForceInlineFlag(AddIndexedProperty);
+
+
+function ToPositiveInteger(x, rangeErrorIndex) {
+  var i = TO_INTEGER_MAP_MINUS_ZERO(x);
+  if (i < 0) throw MakeRangeError(rangeErrorIndex);
+  return i;
+}
+
+
+function MaxSimple(a, b) {
+  return a > b ? a : b;
+}
+
+
+function MinSimple(a, b) {
+  return a > b ? b : a;
+}
+
+
+%SetForceInlineFlag(MaxSimple);
+%SetForceInlineFlag(MinSimple);
+
+
+// ES2015 7.3.20
+// For the fallback with --harmony-species off, there are two possible choices:
+//  - "conservative": return defaultConstructor
+//  - "not conservative": return object.constructor
+// This fallback path is only needed in the transition to ES2015, and the
+// choice is made simply to preserve the previous behavior so that we don't
+// have a three-step upgrade: old behavior, unspecified intermediate behavior,
+// and ES2015.
+// In some cases, we were "conservative" (e.g., ArrayBuffer, RegExp), and in
+// other cases we were "not conservative (e.g., TypedArray, Promise).
+function SpeciesConstructor(object, defaultConstructor, conservative) {
+  if (FLAG_harmony_species) {
+    var constructor = object.constructor;
+    if (IS_UNDEFINED(constructor)) {
+      return defaultConstructor;
+    }
+    if (!IS_RECEIVER(constructor)) {
+      throw MakeTypeError(kConstructorNotReceiver);
+    }
+    var species = constructor[speciesSymbol];
+    if (IS_NULL_OR_UNDEFINED(species)) {
+      return defaultConstructor;
+    }
+    if (%IsConstructor(species)) {
+      return species;
+    }
+    throw MakeTypeError(kSpeciesNotConstructor);
+  } else {
+    return conservative ? defaultConstructor : object.constructor;
+  }
+}
+
+//----------------------------------------------------------------------------
+
+// NOTE: Setting the prototype for Array must take place as early as
+// possible due to code generation for array literals.  When
+// generating code for a array literal a boilerplate array is created
+// that is cloned when running the code.  It is essential that the
+// boilerplate gets the right prototype.
+%FunctionSetPrototype(GlobalArray, new GlobalArray(0));
+
+// ----------------------------------------------------------------------------
+// Exports
+
+utils.Export(function(to) {
+  to.AddIndexedProperty = AddIndexedProperty;
+  to.MaxSimple = MaxSimple;
+  to.MinSimple = MinSimple;
+  to.SameValue = SameValue;
+  to.SameValueZero = SameValueZero;
+  to.ToPositiveInteger = ToPositiveInteger;
+  to.SpeciesConstructor = SpeciesConstructor;
+});
+
+%InstallToContext([
+  "concat_iterable_to_array_builtin", CONCAT_ITERABLE_TO_ARRAY,
+]);
+
+%InstallToContext([
+  "concat_iterable_to_array", ConcatIterableToArray,
+]);
+
+})
diff --git a/src/js/spread.js b/src/js/spread.js
new file mode 100644
index 0000000..235c91a
--- /dev/null
+++ b/src/js/spread.js
@@ -0,0 +1,56 @@
+// Copyright 2015 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.
+
+(function(global, utils) {
+
+'use strict';
+
+// -------------------------------------------------------------------
+// Imports
+var InternalArray = utils.InternalArray;
+var MakeTypeError;
+
+utils.Import(function(from) {
+  MakeTypeError = from.MakeTypeError;
+});
+
+// -------------------------------------------------------------------
+
+function SpreadArguments() {
+  var count = %_ArgumentsLength();
+  var args = new InternalArray();
+
+  for (var i = 0; i < count; ++i) {
+    var array = %_Arguments(i);
+    var length = array.length;
+    for (var j = 0; j < length; ++j) {
+      args.push(array[j]);
+    }
+  }
+
+  return args;
+}
+
+
+function SpreadIterable(collection) {
+  if (IS_NULL_OR_UNDEFINED(collection)) {
+    throw MakeTypeError(kNotIterable, collection);
+  }
+
+  var args = new InternalArray();
+  for (var value of collection) {
+    args.push(value);
+  }
+  return args;
+}
+
+// ----------------------------------------------------------------------------
+// Exports
+
+%InstallToContext([
+  "spread_arguments", SpreadArguments,
+  "spread_iterable", SpreadIterable,
+]);
+
+})
diff --git a/src/js/string-iterator.js b/src/js/string-iterator.js
new file mode 100644
index 0000000..3c331dd
--- /dev/null
+++ b/src/js/string-iterator.js
@@ -0,0 +1,102 @@
+// 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.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalString = global.String;
+var IteratorPrototype = utils.ImportNow("IteratorPrototype");
+var iteratorSymbol = utils.ImportNow("iterator_symbol");
+var MakeTypeError;
+var stringIteratorIteratedStringSymbol =
+    utils.ImportNow("string_iterator_iterated_string_symbol");
+var stringIteratorNextIndexSymbol =
+    utils.ImportNow("string_iterator_next_index_symbol");
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+
+utils.Import(function(from) {
+  MakeTypeError = from.MakeTypeError;
+});
+
+// -------------------------------------------------------------------
+
+function StringIterator() {}
+
+
+// 21.1.5.1 CreateStringIterator Abstract Operation
+function CreateStringIterator(string) {
+  var s = TO_STRING(string);
+  var iterator = new StringIterator;
+  SET_PRIVATE(iterator, stringIteratorIteratedStringSymbol, s);
+  SET_PRIVATE(iterator, stringIteratorNextIndexSymbol, 0);
+  return iterator;
+}
+
+
+// ES6 section 21.1.5.2.1 %StringIteratorPrototype%.next ( )
+function StringIteratorNext() {
+  var iterator = this;
+  var value = UNDEFINED;
+  var done = true;
+
+  if (!IS_RECEIVER(iterator) ||
+      !HAS_DEFINED_PRIVATE(iterator, stringIteratorNextIndexSymbol)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'String Iterator.prototype.next');
+  }
+
+  var s = GET_PRIVATE(iterator, stringIteratorIteratedStringSymbol);
+  if (!IS_UNDEFINED(s)) {
+    var position = GET_PRIVATE(iterator, stringIteratorNextIndexSymbol);
+    var length = TO_UINT32(s.length);
+    if (position >= length) {
+      SET_PRIVATE(iterator, stringIteratorIteratedStringSymbol, UNDEFINED);
+    } else {
+      var first = %_StringCharCodeAt(s, position);
+      value = %_StringCharFromCode(first);
+      done = false;
+      position++;
+
+      if (first >= 0xD800 && first <= 0xDBFF && position < length) {
+        var second = %_StringCharCodeAt(s, position);
+        if (second >= 0xDC00 && second <= 0xDFFF) {
+          value += %_StringCharFromCode(second);
+          position++;
+        }
+      }
+
+      SET_PRIVATE(iterator, stringIteratorNextIndexSymbol, position);
+    }
+  }
+  return %_CreateIterResultObject(value, done);
+}
+
+
+// 21.1.3.27 String.prototype [ @@iterator ]( )
+function StringPrototypeIterator() {
+  return CreateStringIterator(this);
+}
+
+//-------------------------------------------------------------------
+
+%FunctionSetPrototype(StringIterator, {__proto__: IteratorPrototype});
+%FunctionSetInstanceClassName(StringIterator, 'String Iterator');
+
+utils.InstallFunctions(StringIterator.prototype, DONT_ENUM, [
+  'next', StringIteratorNext
+]);
+%AddNamedProperty(StringIterator.prototype, toStringTagSymbol,
+                  "String Iterator", READ_ONLY | DONT_ENUM);
+
+utils.SetFunctionName(StringPrototypeIterator, iteratorSymbol);
+%AddNamedProperty(GlobalString.prototype, iteratorSymbol,
+                  StringPrototypeIterator, DONT_ENUM);
+
+})
diff --git a/src/js/string.js b/src/js/string.js
new file mode 100644
index 0000000..b220038
--- /dev/null
+++ b/src/js/string.js
@@ -0,0 +1,1112 @@
+// Copyright 2012 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.
+
+(function(global, utils) {
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var ArrayIndexOf;
+var ArrayJoin;
+var GlobalRegExp = global.RegExp;
+var GlobalString = global.String;
+var InternalArray = utils.InternalArray;
+var InternalPackedArray = utils.InternalPackedArray;
+var MakeRangeError;
+var MakeTypeError;
+var MathMax;
+var MathMin;
+var matchSymbol = utils.ImportNow("match_symbol");
+var RegExpExec;
+var RegExpExecNoTests;
+var RegExpLastMatchInfo;
+var searchSymbol = utils.ImportNow("search_symbol");
+var splitSymbol = utils.ImportNow("split_symbol");
+
+utils.Import(function(from) {
+  ArrayIndexOf = from.ArrayIndexOf;
+  ArrayJoin = from.ArrayJoin;
+  MakeRangeError = from.MakeRangeError;
+  MakeTypeError = from.MakeTypeError;
+  MathMax = from.MathMax;
+  MathMin = from.MathMin;
+  RegExpExec = from.RegExpExec;
+  RegExpExecNoTests = from.RegExpExecNoTests;
+  RegExpLastMatchInfo = from.RegExpLastMatchInfo;
+});
+
+//-------------------------------------------------------------------
+
+// ECMA-262 section 15.5.4.2
+function StringToString() {
+  if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) {
+    throw MakeTypeError(kNotGeneric, 'String.prototype.toString');
+  }
+  return %_ValueOf(this);
+}
+
+
+// ECMA-262 section 15.5.4.3
+function StringValueOf() {
+  if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) {
+    throw MakeTypeError(kNotGeneric, 'String.prototype.valueOf');
+  }
+  return %_ValueOf(this);
+}
+
+
+// ECMA-262, section 15.5.4.4
+function StringCharAtJS(pos) {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.charAt");
+
+  var result = %_StringCharAt(this, pos);
+  if (%_IsSmi(result)) {
+    result = %_StringCharAt(TO_STRING(this), TO_INTEGER(pos));
+  }
+  return result;
+}
+
+
+// ECMA-262 section 15.5.4.5
+function StringCharCodeAtJS(pos) {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.charCodeAt");
+
+  var result = %_StringCharCodeAt(this, pos);
+  if (!%_IsSmi(result)) {
+    result = %_StringCharCodeAt(TO_STRING(this), TO_INTEGER(pos));
+  }
+  return result;
+}
+
+
+// ECMA-262, section 15.5.4.6
+function StringConcat(other /* and more */) {  // length == 1
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.concat");
+  var len = %_ArgumentsLength();
+  var this_as_string = TO_STRING(this);
+  if (len === 1) {
+    return this_as_string + TO_STRING(other);
+  }
+  var parts = new InternalArray(len + 1);
+  parts[0] = this_as_string;
+  for (var i = 0; i < len; i++) {
+    var part = %_Arguments(i);
+    parts[i + 1] = TO_STRING(part);
+  }
+  return %StringBuilderConcat(parts, len + 1, "");
+}
+
+
+// ECMA-262 section 15.5.4.7
+function StringIndexOfJS(pattern /* position */) {  // length == 1
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.indexOf");
+
+  var subject = TO_STRING(this);
+  pattern = TO_STRING(pattern);
+  var index = 0;
+  if (%_ArgumentsLength() > 1) {
+    index = %_Arguments(1);  // position
+    index = TO_INTEGER(index);
+    if (index < 0) index = 0;
+    if (index > subject.length) index = subject.length;
+  }
+  return %StringIndexOf(subject, pattern, index);
+}
+
+
+// ECMA-262 section 15.5.4.8
+function StringLastIndexOfJS(pat /* position */) {  // length == 1
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.lastIndexOf");
+
+  var sub = TO_STRING(this);
+  var subLength = sub.length;
+  var pat = TO_STRING(pat);
+  var patLength = pat.length;
+  var index = subLength - patLength;
+  if (%_ArgumentsLength() > 1) {
+    var position = TO_NUMBER(%_Arguments(1));
+    if (!NUMBER_IS_NAN(position)) {
+      position = TO_INTEGER(position);
+      if (position < 0) {
+        position = 0;
+      }
+      if (position + patLength < subLength) {
+        index = position;
+      }
+    }
+  }
+  if (index < 0) {
+    return -1;
+  }
+  return %StringLastIndexOf(sub, pat, index);
+}
+
+
+// ECMA-262 section 15.5.4.9
+//
+// This function is implementation specific.  For now, we do not
+// do anything locale specific.
+function StringLocaleCompareJS(other) {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.localeCompare");
+
+  return %StringLocaleCompare(TO_STRING(this), TO_STRING(other));
+}
+
+
+// ES6 21.1.3.11.
+function StringMatchJS(pattern) {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.match");
+
+  if (!IS_NULL_OR_UNDEFINED(pattern)) {
+    var matcher = pattern[matchSymbol];
+    if (!IS_UNDEFINED(matcher)) {
+      return %_Call(matcher, pattern, this);
+    }
+  }
+
+  var subject = TO_STRING(this);
+
+  // Non-regexp argument.
+  var regexp = new GlobalRegExp(pattern);
+  return RegExpExecNoTests(regexp, subject, 0);
+}
+
+
+// ECMA-262 v6, section 21.1.3.12
+//
+// For now we do nothing, as proper normalization requires big tables.
+// If Intl is enabled, then i18n.js will override it and provide the the
+// proper functionality.
+function StringNormalizeJS() {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.normalize");
+  var s = TO_STRING(this);
+
+  var formArg = %_Arguments(0);
+  var form = IS_UNDEFINED(formArg) ? 'NFC' : TO_STRING(formArg);
+
+  var NORMALIZATION_FORMS = ['NFC', 'NFD', 'NFKC', 'NFKD'];
+  var normalizationForm = %_Call(ArrayIndexOf, NORMALIZATION_FORMS, form);
+  if (normalizationForm === -1) {
+    throw MakeRangeError(kNormalizationForm,
+                         %_Call(ArrayJoin, NORMALIZATION_FORMS, ', '));
+  }
+
+  return s;
+}
+
+
+// This has the same size as the RegExpLastMatchInfo array, and can be used
+// for functions that expect that structure to be returned.  It is used when
+// the needle is a string rather than a regexp.  In this case we can't update
+// lastMatchArray without erroneously affecting the properties on the global
+// RegExp object.
+var reusableMatchInfo = [2, "", "", -1, -1];
+
+
+// ECMA-262, section 15.5.4.11
+function StringReplace(search, replace) {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.replace");
+
+  var subject = TO_STRING(this);
+
+  // Decision tree for dispatch
+  // .. regexp search
+  // .... string replace
+  // ...... non-global search
+  // ........ empty string replace
+  // ........ non-empty string replace (with $-expansion)
+  // ...... global search
+  // ........ no need to circumvent last match info override
+  // ........ need to circument last match info override
+  // .... function replace
+  // ...... global search
+  // ...... non-global search
+  // .. string search
+  // .... special case that replaces with one single character
+  // ...... function replace
+  // ...... string replace (with $-expansion)
+
+  if (IS_REGEXP(search)) {
+    if (!IS_CALLABLE(replace)) {
+      replace = TO_STRING(replace);
+
+      if (!REGEXP_GLOBAL(search)) {
+        // Non-global regexp search, string replace.
+        var match = RegExpExec(search, subject, 0);
+        if (match == null) {
+          search.lastIndex = 0
+          return subject;
+        }
+        if (replace.length == 0) {
+          return %_SubString(subject, 0, match[CAPTURE0]) +
+                 %_SubString(subject, match[CAPTURE1], subject.length)
+        }
+        return ExpandReplacement(replace, subject, RegExpLastMatchInfo,
+                                 %_SubString(subject, 0, match[CAPTURE0])) +
+               %_SubString(subject, match[CAPTURE1], subject.length);
+      }
+
+      // Global regexp search, string replace.
+      search.lastIndex = 0;
+      return %StringReplaceGlobalRegExpWithString(
+          subject, search, replace, RegExpLastMatchInfo);
+    }
+
+    if (REGEXP_GLOBAL(search)) {
+      // Global regexp search, function replace.
+      return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
+    }
+    // Non-global regexp search, function replace.
+    return StringReplaceNonGlobalRegExpWithFunction(subject, search, replace);
+  }
+
+  search = TO_STRING(search);
+
+  if (search.length == 1 &&
+      subject.length > 0xFF &&
+      IS_STRING(replace) &&
+      %StringIndexOf(replace, '$', 0) < 0) {
+    // Searching by traversing a cons string tree and replace with cons of
+    // slices works only when the replaced string is a single character, being
+    // replaced by a simple string and only pays off for long strings.
+    return %StringReplaceOneCharWithString(subject, search, replace);
+  }
+  var start = %StringIndexOf(subject, search, 0);
+  if (start < 0) return subject;
+  var end = start + search.length;
+
+  var result = %_SubString(subject, 0, start);
+
+  // Compute the string to replace with.
+  if (IS_CALLABLE(replace)) {
+    result += replace(search, start, subject);
+  } else {
+    reusableMatchInfo[CAPTURE0] = start;
+    reusableMatchInfo[CAPTURE1] = end;
+    result = ExpandReplacement(TO_STRING(replace),
+                               subject,
+                               reusableMatchInfo,
+                               result);
+  }
+
+  return result + %_SubString(subject, end, subject.length);
+}
+
+
+// Expand the $-expressions in the string and return a new string with
+// the result.
+function ExpandReplacement(string, subject, matchInfo, result) {
+  var length = string.length;
+  var next = %StringIndexOf(string, '$', 0);
+  if (next < 0) {
+    if (length > 0) result += string;
+    return result;
+  }
+
+  if (next > 0) result += %_SubString(string, 0, next);
+
+  while (true) {
+    var expansion = '$';
+    var position = next + 1;
+    if (position < length) {
+      var peek = %_StringCharCodeAt(string, position);
+      if (peek == 36) {         // $$
+        ++position;
+        result += '$';
+      } else if (peek == 38) {  // $& - match
+        ++position;
+        result +=
+          %_SubString(subject, matchInfo[CAPTURE0], matchInfo[CAPTURE1]);
+      } else if (peek == 96) {  // $` - prefix
+        ++position;
+        result += %_SubString(subject, 0, matchInfo[CAPTURE0]);
+      } else if (peek == 39) {  // $' - suffix
+        ++position;
+        result += %_SubString(subject, matchInfo[CAPTURE1], subject.length);
+      } else if (peek >= 48 && peek <= 57) {
+        // Valid indices are $1 .. $9, $01 .. $09 and $10 .. $99
+        var scaled_index = (peek - 48) << 1;
+        var advance = 1;
+        var number_of_captures = NUMBER_OF_CAPTURES(matchInfo);
+        if (position + 1 < string.length) {
+          var next = %_StringCharCodeAt(string, position + 1);
+          if (next >= 48 && next <= 57) {
+            var new_scaled_index = scaled_index * 10 + ((next - 48) << 1);
+            if (new_scaled_index < number_of_captures) {
+              scaled_index = new_scaled_index;
+              advance = 2;
+            }
+          }
+        }
+        if (scaled_index != 0 && scaled_index < number_of_captures) {
+          var start = matchInfo[CAPTURE(scaled_index)];
+          if (start >= 0) {
+            result +=
+              %_SubString(subject, start, matchInfo[CAPTURE(scaled_index + 1)]);
+          }
+          position += advance;
+        } else {
+          result += '$';
+        }
+      } else {
+        result += '$';
+      }
+    } else {
+      result += '$';
+    }
+
+    // Go the the next $ in the string.
+    next = %StringIndexOf(string, '$', position);
+
+    // Return if there are no more $ characters in the string. If we
+    // haven't reached the end, we need to append the suffix.
+    if (next < 0) {
+      if (position < length) {
+        result += %_SubString(string, position, length);
+      }
+      return result;
+    }
+
+    // Append substring between the previous and the next $ character.
+    if (next > position) {
+      result += %_SubString(string, position, next);
+    }
+  }
+  return result;
+}
+
+
+// Compute the string of a given regular expression capture.
+function CaptureString(string, lastCaptureInfo, index) {
+  // Scale the index.
+  var scaled = index << 1;
+  // Compute start and end.
+  var start = lastCaptureInfo[CAPTURE(scaled)];
+  // If start isn't valid, return undefined.
+  if (start < 0) return;
+  var end = lastCaptureInfo[CAPTURE(scaled + 1)];
+  return %_SubString(string, start, end);
+}
+
+
+// TODO(lrn): This array will survive indefinitely if replace is never
+// called again. However, it will be empty, since the contents are cleared
+// in the finally block.
+var reusableReplaceArray = new InternalArray(4);
+
+// Helper function for replacing regular expressions with the result of a
+// function application in String.prototype.replace.
+function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) {
+  var resultArray = reusableReplaceArray;
+  if (resultArray) {
+    reusableReplaceArray = null;
+  } else {
+    // Inside a nested replace (replace called from the replacement function
+    // of another replace) or we have failed to set the reusable array
+    // back due to an exception in a replacement function. Create a new
+    // array to use in the future, or until the original is written back.
+    resultArray = new InternalArray(16);
+  }
+  var res = %RegExpExecMultiple(regexp,
+                                subject,
+                                RegExpLastMatchInfo,
+                                resultArray);
+  regexp.lastIndex = 0;
+  if (IS_NULL(res)) {
+    // No matches at all.
+    reusableReplaceArray = resultArray;
+    return subject;
+  }
+  var len = res.length;
+  if (NUMBER_OF_CAPTURES(RegExpLastMatchInfo) == 2) {
+    // If the number of captures is two then there are no explicit captures in
+    // the regexp, just the implicit capture that captures the whole match.  In
+    // this case we can simplify quite a bit and end up with something faster.
+    // The builder will consist of some integers that indicate slices of the
+    // input string and some replacements that were returned from the replace
+    // function.
+    var match_start = 0;
+    for (var i = 0; i < len; i++) {
+      var elem = res[i];
+      if (%_IsSmi(elem)) {
+        // Integers represent slices of the original string.
+        if (elem > 0) {
+          match_start = (elem >> 11) + (elem & 0x7ff);
+        } else {
+          match_start = res[++i] - elem;
+        }
+      } else {
+        var func_result = replace(elem, match_start, subject);
+        // Overwrite the i'th element in the results with the string we got
+        // back from the callback function.
+        res[i] = TO_STRING(func_result);
+        match_start += elem.length;
+      }
+    }
+  } else {
+    for (var i = 0; i < len; i++) {
+      var elem = res[i];
+      if (!%_IsSmi(elem)) {
+        // elem must be an Array.
+        // Use the apply argument as backing for global RegExp properties.
+        var func_result = %Apply(replace, UNDEFINED, elem, 0, elem.length);
+        // Overwrite the i'th element in the results with the string we got
+        // back from the callback function.
+        res[i] = TO_STRING(func_result);
+      }
+    }
+  }
+  var result = %StringBuilderConcat(res, len, subject);
+  resultArray.length = 0;
+  reusableReplaceArray = resultArray;
+  return result;
+}
+
+
+function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) {
+  var matchInfo = RegExpExec(regexp, subject, 0);
+  if (IS_NULL(matchInfo)) {
+    regexp.lastIndex = 0;
+    return subject;
+  }
+  var index = matchInfo[CAPTURE0];
+  var result = %_SubString(subject, 0, index);
+  var endOfMatch = matchInfo[CAPTURE1];
+  // Compute the parameter list consisting of the match, captures, index,
+  // and subject for the replace function invocation.
+  // The number of captures plus one for the match.
+  var m = NUMBER_OF_CAPTURES(matchInfo) >> 1;
+  var replacement;
+  if (m == 1) {
+    // No captures, only the match, which is always valid.
+    var s = %_SubString(subject, index, endOfMatch);
+    // Don't call directly to avoid exposing the built-in global object.
+    replacement = replace(s, index, subject);
+  } else {
+    var parameters = new InternalArray(m + 2);
+    for (var j = 0; j < m; j++) {
+      parameters[j] = CaptureString(subject, matchInfo, j);
+    }
+    parameters[j] = index;
+    parameters[j + 1] = subject;
+
+    replacement = %Apply(replace, UNDEFINED, parameters, 0, j + 2);
+  }
+
+  result += replacement;  // The add method converts to string if necessary.
+  // Can't use matchInfo any more from here, since the function could
+  // overwrite it.
+  return result + %_SubString(subject, endOfMatch, subject.length);
+}
+
+
+// ES6 21.1.3.15.
+function StringSearch(pattern) {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.search");
+
+  if (!IS_NULL_OR_UNDEFINED(pattern)) {
+    var searcher = pattern[searchSymbol];
+    if (!IS_UNDEFINED(searcher)) {
+      return %_Call(searcher, pattern, this);
+    }
+  }
+
+  var subject = TO_STRING(this);
+  var regexp = new GlobalRegExp(pattern);
+  return %_Call(regexp[searchSymbol], regexp, subject);
+}
+
+
+// ECMA-262 section 15.5.4.13
+function StringSlice(start, end) {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.slice");
+
+  var s = TO_STRING(this);
+  var s_len = s.length;
+  var start_i = TO_INTEGER(start);
+  var end_i = s_len;
+  if (!IS_UNDEFINED(end)) {
+    end_i = TO_INTEGER(end);
+  }
+
+  if (start_i < 0) {
+    start_i += s_len;
+    if (start_i < 0) {
+      start_i = 0;
+    }
+  } else {
+    if (start_i > s_len) {
+      return '';
+    }
+  }
+
+  if (end_i < 0) {
+    end_i += s_len;
+    if (end_i < 0) {
+      return '';
+    }
+  } else {
+    if (end_i > s_len) {
+      end_i = s_len;
+    }
+  }
+
+  if (end_i <= start_i) {
+    return '';
+  }
+
+  return %_SubString(s, start_i, end_i);
+}
+
+
+// ES6 21.1.3.17.
+function StringSplitJS(separator, limit) {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.split");
+
+  if (!IS_NULL_OR_UNDEFINED(separator)) {
+    var splitter = separator[splitSymbol];
+    if (!IS_UNDEFINED(splitter)) {
+      return %_Call(splitter, separator, this, limit);
+    }
+  }
+
+  var subject = TO_STRING(this);
+  limit = (IS_UNDEFINED(limit)) ? kMaxUint32 : TO_UINT32(limit);
+
+  var length = subject.length;
+  var separator_string = TO_STRING(separator);
+
+  if (limit === 0) return [];
+
+  // ECMA-262 says that if separator is undefined, the result should
+  // be an array of size 1 containing the entire string.
+  if (IS_UNDEFINED(separator)) return [subject];
+
+  var separator_length = separator_string.length;
+
+  // If the separator string is empty then return the elements in the subject.
+  if (separator_length === 0) return %StringToArray(subject, limit);
+
+  return %StringSplit(subject, separator_string, limit);
+}
+
+
+// ECMA-262 section 15.5.4.15
+function StringSubstring(start, end) {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.subString");
+
+  var s = TO_STRING(this);
+  var s_len = s.length;
+
+  var start_i = TO_INTEGER(start);
+  if (start_i < 0) {
+    start_i = 0;
+  } else if (start_i > s_len) {
+    start_i = s_len;
+  }
+
+  var end_i = s_len;
+  if (!IS_UNDEFINED(end)) {
+    end_i = TO_INTEGER(end);
+    if (end_i > s_len) {
+      end_i = s_len;
+    } else {
+      if (end_i < 0) end_i = 0;
+      if (start_i > end_i) {
+        var tmp = end_i;
+        end_i = start_i;
+        start_i = tmp;
+      }
+    }
+  }
+
+  return %_SubString(s, start_i, end_i);
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.1
+function StringSubstr(start, n) {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.substr");
+
+  var s = TO_STRING(this);
+  var len;
+
+  // Correct n: If not given, set to string length; if explicitly
+  // set to undefined, zero, or negative, returns empty string.
+  if (IS_UNDEFINED(n)) {
+    len = s.length;
+  } else {
+    len = TO_INTEGER(n);
+    if (len <= 0) return '';
+  }
+
+  // Correct start: If not given (or undefined), set to zero; otherwise
+  // convert to integer and handle negative case.
+  if (IS_UNDEFINED(start)) {
+    start = 0;
+  } else {
+    start = TO_INTEGER(start);
+    // If positive, and greater than or equal to the string length,
+    // return empty string.
+    if (start >= s.length) return '';
+    // If negative and absolute value is larger than the string length,
+    // use zero.
+    if (start < 0) {
+      start += s.length;
+      if (start < 0) start = 0;
+    }
+  }
+
+  var end = start + len;
+  if (end > s.length) end = s.length;
+
+  return %_SubString(s, start, end);
+}
+
+
+// ECMA-262, 15.5.4.16
+function StringToLowerCaseJS() {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLowerCase");
+
+  return %StringToLowerCase(TO_STRING(this));
+}
+
+
+// ECMA-262, 15.5.4.17
+function StringToLocaleLowerCase() {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleLowerCase");
+
+  return %StringToLowerCase(TO_STRING(this));
+}
+
+
+// ECMA-262, 15.5.4.18
+function StringToUpperCaseJS() {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.toUpperCase");
+
+  return %StringToUpperCase(TO_STRING(this));
+}
+
+
+// ECMA-262, 15.5.4.19
+function StringToLocaleUpperCase() {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleUpperCase");
+
+  return %StringToUpperCase(TO_STRING(this));
+}
+
+// ES5, 15.5.4.20
+function StringTrimJS() {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.trim");
+
+  return %StringTrim(TO_STRING(this), true, true);
+}
+
+function StringTrimLeft() {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.trimLeft");
+
+  return %StringTrim(TO_STRING(this), true, false);
+}
+
+function StringTrimRight() {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.trimRight");
+
+  return %StringTrim(TO_STRING(this), false, true);
+}
+
+
+// ECMA-262, section 15.5.3.2
+function StringFromCharCode(code) {
+  var n = %_ArgumentsLength();
+  if (n == 1) return %_StringCharFromCode(code & 0xffff);
+
+  var one_byte = %NewString(n, NEW_ONE_BYTE_STRING);
+  var i;
+  for (i = 0; i < n; i++) {
+    code = %_Arguments(i) & 0xffff;
+    if (code > 0xff) break;
+    %_OneByteSeqStringSetChar(i, code, one_byte);
+  }
+  if (i == n) return one_byte;
+  one_byte = %TruncateString(one_byte, i);
+
+  var two_byte = %NewString(n - i, NEW_TWO_BYTE_STRING);
+  %_TwoByteSeqStringSetChar(0, code, two_byte);
+  i++;
+  for (var j = 1; i < n; i++, j++) {
+    code = %_Arguments(i) & 0xffff;
+    %_TwoByteSeqStringSetChar(j, code, two_byte);
+  }
+  return one_byte + two_byte;
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.2.1
+function HtmlEscape(str) {
+  return %_Call(StringReplace, TO_STRING(str), /"/g, "&quot;");
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.2
+function StringAnchor(name) {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.anchor");
+  return "<a name=\"" + HtmlEscape(name) + "\">" + TO_STRING(this) +
+         "</a>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.3
+function StringBig() {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.big");
+  return "<big>" + TO_STRING(this) + "</big>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.4
+function StringBlink() {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.blink");
+  return "<blink>" + TO_STRING(this) + "</blink>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.5
+function StringBold() {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.bold");
+  return "<b>" + TO_STRING(this) + "</b>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.6
+function StringFixed() {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.fixed");
+  return "<tt>" + TO_STRING(this) + "</tt>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.7
+function StringFontcolor(color) {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.fontcolor");
+  return "<font color=\"" + HtmlEscape(color) + "\">" + TO_STRING(this) +
+         "</font>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.8
+function StringFontsize(size) {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.fontsize");
+  return "<font size=\"" + HtmlEscape(size) + "\">" + TO_STRING(this) +
+         "</font>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.9
+function StringItalics() {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.italics");
+  return "<i>" + TO_STRING(this) + "</i>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.10
+function StringLink(s) {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.link");
+  return "<a href=\"" + HtmlEscape(s) + "\">" + TO_STRING(this) + "</a>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.11
+function StringSmall() {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.small");
+  return "<small>" + TO_STRING(this) + "</small>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.12
+function StringStrike() {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.strike");
+  return "<strike>" + TO_STRING(this) + "</strike>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.13
+function StringSub() {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.sub");
+  return "<sub>" + TO_STRING(this) + "</sub>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.14
+function StringSup() {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.sup");
+  return "<sup>" + TO_STRING(this) + "</sup>";
+}
+
+// ES6, section 21.1.3.13
+function StringRepeat(count) {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.repeat");
+
+  var s = TO_STRING(this);
+  var n = TO_INTEGER(count);
+
+  if (n < 0 || n === INFINITY) throw MakeRangeError(kInvalidCountValue);
+
+  // Early return to allow an arbitrarily-large repeat of the empty string.
+  if (s.length === 0) return "";
+
+  // The maximum string length is stored in a smi, so a longer repeat
+  // must result in a range error.
+  if (n > %_MaxSmi()) throw MakeRangeError(kInvalidCountValue);
+
+  var r = "";
+  while (true) {
+    if (n & 1) r += s;
+    n >>= 1;
+    if (n === 0) return r;
+    s += s;
+  }
+}
+
+
+// ES6 draft 04-05-14, section 21.1.3.18
+function StringStartsWith(searchString /* position */) {  // length == 1
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.startsWith");
+
+  var s = TO_STRING(this);
+
+  if (IS_REGEXP(searchString)) {
+    throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.startsWith");
+  }
+
+  var ss = TO_STRING(searchString);
+  var pos = 0;
+  if (%_ArgumentsLength() > 1) {
+    var arg = %_Arguments(1);  // position
+    if (!IS_UNDEFINED(arg)) {
+      pos = TO_INTEGER(arg);
+    }
+  }
+
+  var s_len = s.length;
+  var start = MathMin(MathMax(pos, 0), s_len);
+  var ss_len = ss.length;
+  if (ss_len + start > s_len) {
+    return false;
+  }
+
+  return %_SubString(s, start, start + ss_len) === ss;
+}
+
+
+// ES6 draft 04-05-14, section 21.1.3.7
+function StringEndsWith(searchString /* position */) {  // length == 1
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.endsWith");
+
+  var s = TO_STRING(this);
+
+  if (IS_REGEXP(searchString)) {
+    throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.endsWith");
+  }
+
+  var ss = TO_STRING(searchString);
+  var s_len = s.length;
+  var pos = s_len;
+  if (%_ArgumentsLength() > 1) {
+    var arg = %_Arguments(1);  // position
+    if (!IS_UNDEFINED(arg)) {
+      pos = TO_INTEGER(arg);
+    }
+  }
+
+  var end = MathMin(MathMax(pos, 0), s_len);
+  var ss_len = ss.length;
+  var start = end - ss_len;
+  if (start < 0) {
+    return false;
+  }
+
+  return %_SubString(s, start, start + ss_len) === ss;
+}
+
+
+// ES6 draft 04-05-14, section 21.1.3.6
+function StringIncludes(searchString /* position */) {  // length == 1
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.includes");
+
+  var string = TO_STRING(this);
+
+  if (IS_REGEXP(searchString)) {
+    throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.includes");
+  }
+
+  searchString = TO_STRING(searchString);
+  var pos = 0;
+  if (%_ArgumentsLength() > 1) {
+    pos = %_Arguments(1);  // position
+    pos = TO_INTEGER(pos);
+  }
+
+  var stringLength = string.length;
+  if (pos < 0) pos = 0;
+  if (pos > stringLength) pos = stringLength;
+  var searchStringLength = searchString.length;
+
+  if (searchStringLength + pos > stringLength) {
+    return false;
+  }
+
+  return %StringIndexOf(string, searchString, pos) !== -1;
+}
+
+
+// ES6 Draft 05-22-2014, section 21.1.3.3
+function StringCodePointAt(pos) {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.codePointAt");
+
+  var string = TO_STRING(this);
+  var size = string.length;
+  pos = TO_INTEGER(pos);
+  if (pos < 0 || pos >= size) {
+    return UNDEFINED;
+  }
+  var first = %_StringCharCodeAt(string, pos);
+  if (first < 0xD800 || first > 0xDBFF || pos + 1 == size) {
+    return first;
+  }
+  var second = %_StringCharCodeAt(string, pos + 1);
+  if (second < 0xDC00 || second > 0xDFFF) {
+    return first;
+  }
+  return (first - 0xD800) * 0x400 + second + 0x2400;
+}
+
+
+// ES6 Draft 05-22-2014, section 21.1.2.2
+function StringFromCodePoint(_) {  // length = 1
+  var code;
+  var length = %_ArgumentsLength();
+  var index;
+  var result = "";
+  for (index = 0; index < length; index++) {
+    code = %_Arguments(index);
+    if (!%_IsSmi(code)) {
+      code = TO_NUMBER(code);
+    }
+    if (code < 0 || code > 0x10FFFF || code !== TO_INTEGER(code)) {
+      throw MakeRangeError(kInvalidCodePoint, code);
+    }
+    if (code <= 0xFFFF) {
+      result += %_StringCharFromCode(code);
+    } else {
+      code -= 0x10000;
+      result += %_StringCharFromCode((code >>> 10) & 0x3FF | 0xD800);
+      result += %_StringCharFromCode(code & 0x3FF | 0xDC00);
+    }
+  }
+  return result;
+}
+
+
+// -------------------------------------------------------------------
+// String methods related to templates
+
+// ES6 Draft 03-17-2015, section 21.1.2.4
+function StringRaw(callSite) {
+  // TODO(caitp): Use rest parameters when implemented
+  var numberOfSubstitutions = %_ArgumentsLength();
+  var cooked = TO_OBJECT(callSite);
+  var raw = TO_OBJECT(cooked.raw);
+  var literalSegments = TO_LENGTH(raw.length);
+  if (literalSegments <= 0) return "";
+
+  var result = TO_STRING(raw[0]);
+
+  for (var i = 1; i < literalSegments; ++i) {
+    if (i < numberOfSubstitutions) {
+      result += TO_STRING(%_Arguments(i));
+    }
+    result += TO_STRING(raw[i]);
+  }
+
+  return result;
+}
+
+// -------------------------------------------------------------------
+
+// Set the String function and constructor.
+%FunctionSetPrototype(GlobalString, new GlobalString());
+
+// Set up the constructor property on the String prototype object.
+%AddNamedProperty(
+    GlobalString.prototype, "constructor", GlobalString, DONT_ENUM);
+
+// Set up the non-enumerable functions on the String object.
+utils.InstallFunctions(GlobalString, DONT_ENUM, [
+  "fromCharCode", StringFromCharCode,
+  "fromCodePoint", StringFromCodePoint,
+  "raw", StringRaw
+]);
+
+// Set up the non-enumerable functions on the String prototype object.
+utils.InstallFunctions(GlobalString.prototype, DONT_ENUM, [
+  "valueOf", StringValueOf,
+  "toString", StringToString,
+  "charAt", StringCharAtJS,
+  "charCodeAt", StringCharCodeAtJS,
+  "codePointAt", StringCodePointAt,
+  "concat", StringConcat,
+  "endsWith", StringEndsWith,
+  "includes", StringIncludes,
+  "indexOf", StringIndexOfJS,
+  "lastIndexOf", StringLastIndexOfJS,
+  "localeCompare", StringLocaleCompareJS,
+  "match", StringMatchJS,
+  "normalize", StringNormalizeJS,
+  "repeat", StringRepeat,
+  "replace", StringReplace,
+  "search", StringSearch,
+  "slice", StringSlice,
+  "split", StringSplitJS,
+  "substring", StringSubstring,
+  "substr", StringSubstr,
+  "startsWith", StringStartsWith,
+  "toLowerCase", StringToLowerCaseJS,
+  "toLocaleLowerCase", StringToLocaleLowerCase,
+  "toUpperCase", StringToUpperCaseJS,
+  "toLocaleUpperCase", StringToLocaleUpperCase,
+  "trim", StringTrimJS,
+  "trimLeft", StringTrimLeft,
+  "trimRight", StringTrimRight,
+
+  "link", StringLink,
+  "anchor", StringAnchor,
+  "fontcolor", StringFontcolor,
+  "fontsize", StringFontsize,
+  "big", StringBig,
+  "blink", StringBlink,
+  "bold", StringBold,
+  "fixed", StringFixed,
+  "italics", StringItalics,
+  "small", StringSmall,
+  "strike", StringStrike,
+  "sub", StringSub,
+  "sup", StringSup
+]);
+
+// -------------------------------------------------------------------
+// Exports
+
+utils.Export(function(to) {
+  to.StringCharAt = StringCharAtJS;
+  to.StringIndexOf = StringIndexOfJS;
+  to.StringLastIndexOf = StringLastIndexOfJS;
+  to.StringMatch = StringMatchJS;
+  to.StringReplace = StringReplace;
+  to.StringSlice = StringSlice;
+  to.StringSplit = StringSplitJS;
+  to.StringSubstr = StringSubstr;
+  to.StringSubstring = StringSubstring;
+});
+
+})
diff --git a/src/js/symbol.js b/src/js/symbol.js
new file mode 100644
index 0000000..5be6e01
--- /dev/null
+++ b/src/js/symbol.js
@@ -0,0 +1,135 @@
+// Copyright 2013 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.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalObject = global.Object;
+var GlobalSymbol = global.Symbol;
+var hasInstanceSymbol = utils.ImportNow("has_instance_symbol");
+var isConcatSpreadableSymbol =
+    utils.ImportNow("is_concat_spreadable_symbol");
+var iteratorSymbol = utils.ImportNow("iterator_symbol");
+var MakeTypeError;
+var toPrimitiveSymbol = utils.ImportNow("to_primitive_symbol");
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+var unscopablesSymbol = utils.ImportNow("unscopables_symbol");
+
+utils.Import(function(from) {
+  MakeTypeError = from.MakeTypeError;
+});
+
+// -------------------------------------------------------------------
+
+// 19.4.3.4 Symbol.prototype [ @@toPrimitive ] ( hint )
+function SymbolToPrimitive(hint) {
+  if (!(IS_SYMBOL(this) || IS_SYMBOL_WRAPPER(this))) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        "Symbol.prototype [ @@toPrimitive ]", this);
+  }
+  return %_ValueOf(this);
+}
+
+
+function SymbolToString() {
+  if (!(IS_SYMBOL(this) || IS_SYMBOL_WRAPPER(this))) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        "Symbol.prototype.toString", this);
+  }
+  return %SymbolDescriptiveString(%_ValueOf(this));
+}
+
+
+function SymbolValueOf() {
+  if (!(IS_SYMBOL(this) || IS_SYMBOL_WRAPPER(this))) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        "Symbol.prototype.valueOf", this);
+  }
+  return %_ValueOf(this);
+}
+
+
+function SymbolFor(key) {
+  key = TO_STRING(key);
+  var registry = %SymbolRegistry();
+  if (IS_UNDEFINED(registry.for[key])) {
+    var symbol = %CreateSymbol(key);
+    registry.for[key] = symbol;
+    registry.keyFor[symbol] = key;
+  }
+  return registry.for[key];
+}
+
+
+function SymbolKeyFor(symbol) {
+  if (!IS_SYMBOL(symbol)) throw MakeTypeError(kSymbolKeyFor, symbol);
+  return %SymbolRegistry().keyFor[symbol];
+}
+
+
+// ES6 19.1.2.8
+function ObjectGetOwnPropertySymbols(obj) {
+  obj = TO_OBJECT(obj);
+
+  return %GetOwnPropertyKeys(obj, PROPERTY_FILTER_SKIP_STRINGS);
+}
+
+// -------------------------------------------------------------------
+
+%FunctionSetPrototype(GlobalSymbol, new GlobalObject());
+
+utils.InstallConstants(GlobalSymbol, [
+  // TODO(rossberg): expose when implemented.
+  // "hasInstance", hasInstanceSymbol,
+  // "isConcatSpreadable", isConcatSpreadableSymbol,
+  "iterator", iteratorSymbol,
+  // TODO(yangguo): expose when implemented.
+  // "match", matchSymbol,
+  // "replace", replaceSymbol,
+  // "search", searchSymbol,
+  // "split, splitSymbol,
+  "toPrimitive", toPrimitiveSymbol,
+  // TODO(dslomov, caitp): Currently defined in harmony-tostring.js ---
+  // Move here when shipping
+  // "toStringTag", toStringTagSymbol,
+  "unscopables", unscopablesSymbol,
+]);
+
+utils.InstallFunctions(GlobalSymbol, DONT_ENUM, [
+  "for", SymbolFor,
+  "keyFor", SymbolKeyFor
+]);
+
+%AddNamedProperty(
+    GlobalSymbol.prototype, "constructor", GlobalSymbol, DONT_ENUM);
+%AddNamedProperty(
+    GlobalSymbol.prototype, toStringTagSymbol, "Symbol", DONT_ENUM | READ_ONLY);
+
+utils.InstallFunctions(GlobalSymbol.prototype, DONT_ENUM | READ_ONLY, [
+  toPrimitiveSymbol, SymbolToPrimitive
+]);
+
+utils.InstallFunctions(GlobalSymbol.prototype, DONT_ENUM, [
+  "toString", SymbolToString,
+  "valueOf", SymbolValueOf
+]);
+
+utils.InstallFunctions(GlobalObject, DONT_ENUM, [
+  "getOwnPropertySymbols", ObjectGetOwnPropertySymbols
+]);
+
+// -------------------------------------------------------------------
+// Exports
+
+utils.Export(function(to) {
+  to.SymbolToString = SymbolToString;
+})
+
+})
diff --git a/src/js/templates.js b/src/js/templates.js
new file mode 100644
index 0000000..7236d5c
--- /dev/null
+++ b/src/js/templates.js
@@ -0,0 +1,84 @@
+// 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.
+
+// Called from a desugaring in the parser.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalMap = global.Map;
+var InternalArray = utils.InternalArray;
+
+// -------------------------------------------------------------------
+
+var callSiteCache = new GlobalMap;
+var mapGetFn = GlobalMap.prototype.get;
+var mapSetFn = GlobalMap.prototype.set;
+
+
+function SameCallSiteElements(rawStrings, other) {
+  var length = rawStrings.length;
+  var other = other.raw;
+
+  if (length !== other.length) return false;
+
+  for (var i = 0; i < length; ++i) {
+    if (rawStrings[i] !== other[i]) return false;
+  }
+
+  return true;
+}
+
+
+function GetCachedCallSite(siteObj, hash) {
+  var obj = %_Call(mapGetFn, callSiteCache, hash);
+
+  if (IS_UNDEFINED(obj)) return;
+
+  var length = obj.length;
+  for (var i = 0; i < length; ++i) {
+    if (SameCallSiteElements(siteObj, obj[i])) return obj[i];
+  }
+}
+
+
+function SetCachedCallSite(siteObj, hash) {
+  var obj = %_Call(mapGetFn, callSiteCache, hash);
+  var array;
+
+  if (IS_UNDEFINED(obj)) {
+    array = new InternalArray(1);
+    array[0] = siteObj;
+    %_Call(mapSetFn, callSiteCache, hash, array);
+  } else {
+    obj.push(siteObj);
+  }
+
+  return siteObj;
+}
+
+
+function GetTemplateCallSite(siteObj, rawStrings, hash) {
+  var cached = GetCachedCallSite(rawStrings, hash);
+
+  if (!IS_UNDEFINED(cached)) return cached;
+
+  %AddNamedProperty(siteObj, "raw", %object_freeze(rawStrings),
+      READ_ONLY | DONT_ENUM | DONT_DELETE);
+
+  return SetCachedCallSite(%object_freeze(siteObj), hash);
+}
+
+// ----------------------------------------------------------------------------
+// Exports
+
+%InstallToContext(["get_template_call_site", GetTemplateCallSite]);
+
+})
diff --git a/src/js/typedarray.js b/src/js/typedarray.js
new file mode 100644
index 0000000..fd668a5
--- /dev/null
+++ b/src/js/typedarray.js
@@ -0,0 +1,980 @@
+// Copyright 2013 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.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var ArrayFrom;
+var ArrayToString;
+var ArrayValues;
+var GlobalArray = global.Array;
+var GlobalArrayBuffer = global.ArrayBuffer;
+var GlobalDataView = global.DataView;
+var GlobalObject = global.Object;
+var InternalArray = utils.InternalArray;
+var InnerArrayCopyWithin;
+var InnerArrayEvery;
+var InnerArrayFill;
+var InnerArrayFilter;
+var InnerArrayFind;
+var InnerArrayFindIndex;
+var InnerArrayForEach;
+var InnerArrayIncludes;
+var InnerArrayIndexOf;
+var InnerArrayJoin;
+var InnerArrayLastIndexOf;
+var InnerArrayReduce;
+var InnerArrayReduceRight;
+var InnerArraySome;
+var InnerArraySort;
+var InnerArrayToLocaleString;
+var InternalArray = utils.InternalArray;
+var IsNaN;
+var MakeRangeError;
+var MakeTypeError;
+var MaxSimple;
+var MinSimple;
+var PackedArrayReverse;
+var SpeciesConstructor;
+var ToPositiveInteger;
+var iteratorSymbol = utils.ImportNow("iterator_symbol");
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+
+macro TYPED_ARRAYS(FUNCTION)
+// arrayIds below should be synchronized with Runtime_TypedArrayInitialize.
+FUNCTION(1, Uint8Array, 1)
+FUNCTION(2, Int8Array, 1)
+FUNCTION(3, Uint16Array, 2)
+FUNCTION(4, Int16Array, 2)
+FUNCTION(5, Uint32Array, 4)
+FUNCTION(6, Int32Array, 4)
+FUNCTION(7, Float32Array, 4)
+FUNCTION(8, Float64Array, 8)
+FUNCTION(9, Uint8ClampedArray, 1)
+endmacro
+
+macro DECLARE_GLOBALS(INDEX, NAME, SIZE)
+var GlobalNAME = global.NAME;
+endmacro
+
+TYPED_ARRAYS(DECLARE_GLOBALS)
+
+utils.Import(function(from) {
+  ArrayFrom = from.ArrayFrom;
+  ArrayToString = from.ArrayToString;
+  ArrayValues = from.ArrayValues;
+  InnerArrayCopyWithin = from.InnerArrayCopyWithin;
+  InnerArrayEvery = from.InnerArrayEvery;
+  InnerArrayFill = from.InnerArrayFill;
+  InnerArrayFilter = from.InnerArrayFilter;
+  InnerArrayFind = from.InnerArrayFind;
+  InnerArrayFindIndex = from.InnerArrayFindIndex;
+  InnerArrayForEach = from.InnerArrayForEach;
+  InnerArrayIncludes = from.InnerArrayIncludes;
+  InnerArrayIndexOf = from.InnerArrayIndexOf;
+  InnerArrayJoin = from.InnerArrayJoin;
+  InnerArrayLastIndexOf = from.InnerArrayLastIndexOf;
+  InnerArrayReduce = from.InnerArrayReduce;
+  InnerArrayReduceRight = from.InnerArrayReduceRight;
+  InnerArraySome = from.InnerArraySome;
+  InnerArraySort = from.InnerArraySort;
+  InnerArrayToLocaleString = from.InnerArrayToLocaleString;
+  IsNaN = from.IsNaN;
+  MakeRangeError = from.MakeRangeError;
+  MakeTypeError = from.MakeTypeError;
+  MaxSimple = from.MaxSimple;
+  MinSimple = from.MinSimple;
+  PackedArrayReverse = from.PackedArrayReverse;
+  SpeciesConstructor = from.SpeciesConstructor;
+  ToPositiveInteger = from.ToPositiveInteger;
+});
+
+// --------------- Typed Arrays ---------------------
+
+function TypedArrayDefaultConstructor(typedArray) {
+  switch (%_ClassOf(typedArray)) {
+macro TYPED_ARRAY_CONSTRUCTOR_CASE(ARRAY_ID, NAME, ELEMENT_SIZE)
+    case "NAME":
+      return GlobalNAME;
+endmacro
+TYPED_ARRAYS(TYPED_ARRAY_CONSTRUCTOR_CASE)
+  }
+  // The TypeError should not be generated since all callers should
+  // have already called ValidateTypedArray.
+  throw MakeTypeError(kIncompatibleMethodReceiver,
+                      "TypedArrayDefaultConstructor", this);
+}
+
+function TypedArrayCreate(constructor, arg0, arg1, arg2) {
+  if (IS_UNDEFINED(arg1)) {
+    var newTypedArray = new constructor(arg0);
+  } else {
+    var newTypedArray = new constructor(arg0, arg1, arg2);
+  }
+  if (!%_IsTypedArray(newTypedArray)) throw MakeTypeError(kNotTypedArray);
+  // TODO(littledan): Check for being detached, here and elsewhere
+  // All callers where the first argument is a Number have no additional
+  // arguments.
+  if (IS_NUMBER(arg0) && %_TypedArrayGetLength(newTypedArray) < arg0) {
+    throw MakeTypeError(kTypedArrayTooShort);
+  }
+  return newTypedArray;
+}
+
+function TypedArraySpeciesCreate(exemplar, arg0, arg1, arg2, conservative) {
+  var defaultConstructor = TypedArrayDefaultConstructor(exemplar);
+  var constructor = SpeciesConstructor(exemplar, defaultConstructor,
+                                       conservative);
+  return TypedArrayCreate(constructor, arg0, arg1, arg2);
+}
+
+macro TYPED_ARRAY_CONSTRUCTOR(ARRAY_ID, NAME, ELEMENT_SIZE)
+function NAMEConstructByArrayBuffer(obj, buffer, byteOffset, length) {
+  if (!IS_UNDEFINED(byteOffset)) {
+    byteOffset = ToPositiveInteger(byteOffset, kInvalidTypedArrayLength);
+  }
+  if (!IS_UNDEFINED(length)) {
+    length = ToPositiveInteger(length, kInvalidTypedArrayLength);
+  }
+
+  var bufferByteLength = %_ArrayBufferGetByteLength(buffer);
+  var offset;
+  if (IS_UNDEFINED(byteOffset)) {
+    offset = 0;
+  } else {
+    offset = byteOffset;
+
+    if (offset % ELEMENT_SIZE !== 0) {
+      throw MakeRangeError(kInvalidTypedArrayAlignment,
+                           "start offset", "NAME", ELEMENT_SIZE);
+    }
+    if (offset > bufferByteLength) {
+      throw MakeRangeError(kInvalidTypedArrayOffset);
+    }
+  }
+
+  var newByteLength;
+  var newLength;
+  if (IS_UNDEFINED(length)) {
+    if (bufferByteLength % ELEMENT_SIZE !== 0) {
+      throw MakeRangeError(kInvalidTypedArrayAlignment,
+                           "byte length", "NAME", ELEMENT_SIZE);
+    }
+    newByteLength = bufferByteLength - offset;
+    newLength = newByteLength / ELEMENT_SIZE;
+  } else {
+    var newLength = length;
+    newByteLength = newLength * ELEMENT_SIZE;
+  }
+  if ((offset + newByteLength > bufferByteLength)
+      || (newLength > %_MaxSmi())) {
+    throw MakeRangeError(kInvalidTypedArrayLength);
+  }
+  %_TypedArrayInitialize(obj, ARRAY_ID, buffer, offset, newByteLength, true);
+}
+
+function NAMEConstructByLength(obj, length) {
+  var l = IS_UNDEFINED(length) ?
+    0 : ToPositiveInteger(length, kInvalidTypedArrayLength);
+  if (l > %_MaxSmi()) {
+    throw MakeRangeError(kInvalidTypedArrayLength);
+  }
+  var byteLength = l * ELEMENT_SIZE;
+  if (byteLength > %_TypedArrayMaxSizeInHeap()) {
+    var buffer = new GlobalArrayBuffer(byteLength);
+    %_TypedArrayInitialize(obj, ARRAY_ID, buffer, 0, byteLength, true);
+  } else {
+    %_TypedArrayInitialize(obj, ARRAY_ID, null, 0, byteLength, true);
+  }
+}
+
+function NAMEConstructByArrayLike(obj, arrayLike) {
+  var length = arrayLike.length;
+  var l = ToPositiveInteger(length, kInvalidTypedArrayLength);
+
+  if (l > %_MaxSmi()) {
+    throw MakeRangeError(kInvalidTypedArrayLength);
+  }
+  var initialized = false;
+  var byteLength = l * ELEMENT_SIZE;
+  if (byteLength <= %_TypedArrayMaxSizeInHeap()) {
+    %_TypedArrayInitialize(obj, ARRAY_ID, null, 0, byteLength, false);
+  } else {
+    initialized =
+        %TypedArrayInitializeFromArrayLike(obj, ARRAY_ID, arrayLike, l);
+  }
+  if (!initialized) {
+    for (var i = 0; i < l; i++) {
+      // It is crucial that we let any execptions from arrayLike[i]
+      // propagate outside the function.
+      obj[i] = arrayLike[i];
+    }
+  }
+}
+
+function NAMEConstructByIterable(obj, iterable, iteratorFn) {
+  var list = new InternalArray();
+  // Reading the Symbol.iterator property of iterable twice would be
+  // observable with getters, so instead, we call the function which
+  // was already looked up, and wrap it in another iterable. The
+  // __proto__ of the new iterable is set to null to avoid any chance
+  // of modifications to Object.prototype being observable here.
+  var iterator = %_Call(iteratorFn, iterable);
+  var newIterable = {
+    __proto__: null
+  };
+  // TODO(littledan): Computed properties don't work yet in nosnap.
+  // Rephrase when they do.
+  newIterable[iteratorSymbol] = function() { return iterator; }
+  for (var value of newIterable) {
+    list.push(value);
+  }
+  NAMEConstructByArrayLike(obj, list);
+}
+
+function NAMEConstructor(arg1, arg2, arg3) {
+  if (!IS_UNDEFINED(new.target)) {
+    if (IS_ARRAYBUFFER(arg1) || IS_SHAREDARRAYBUFFER(arg1)) {
+      NAMEConstructByArrayBuffer(this, arg1, arg2, arg3);
+    } else if (IS_NUMBER(arg1) || IS_STRING(arg1) ||
+               IS_BOOLEAN(arg1) || IS_UNDEFINED(arg1)) {
+      NAMEConstructByLength(this, arg1);
+    } else {
+      // TODO(littledan): If arg1 is a TypedArray, follow the constructor
+      // path in ES2015 22.2.4.3, and call SpeciesConstructor, in a
+      // path that seems to be an optimized version of what's below, but
+      // in an observably different way.
+      var iteratorFn = arg1[iteratorSymbol];
+      if (IS_UNDEFINED(iteratorFn) || iteratorFn === ArrayValues) {
+        NAMEConstructByArrayLike(this, arg1);
+      } else {
+        NAMEConstructByIterable(this, arg1, iteratorFn);
+      }
+    }
+  } else {
+    throw MakeTypeError(kConstructorNotFunction, "NAME")
+  }
+}
+
+// TODO(littledan): Remove this performance workaround BUG(chromium:579905)
+function NAME_GetLength() {
+  if (!(%_ClassOf(this) === 'NAME')) {
+    throw MakeTypeError(kIncompatibleMethodReceiver, "NAME.length", this);
+  }
+  return %_TypedArrayGetLength(this);
+}
+
+function NAMESubArray(begin, end) {
+  var beginInt = TO_INTEGER(begin);
+  if (!IS_UNDEFINED(end)) {
+    var endInt = TO_INTEGER(end);
+    var srcLength = %_TypedArrayGetLength(this);
+  } else {
+    var srcLength = %_TypedArrayGetLength(this);
+    var endInt = srcLength;
+  }
+
+  if (beginInt < 0) {
+    beginInt = MaxSimple(0, srcLength + beginInt);
+  } else {
+    beginInt = MinSimple(beginInt, srcLength);
+  }
+
+  if (endInt < 0) {
+    endInt = MaxSimple(0, srcLength + endInt);
+  } else {
+    endInt = MinSimple(endInt, srcLength);
+  }
+
+  if (endInt < beginInt) {
+    endInt = beginInt;
+  }
+
+  var newLength = endInt - beginInt;
+  var beginByteOffset =
+      %_ArrayBufferViewGetByteOffset(this) + beginInt * ELEMENT_SIZE;
+  return TypedArraySpeciesCreate(this, %TypedArrayGetBuffer(this),
+                                 beginByteOffset, newLength, true);
+}
+endmacro
+
+TYPED_ARRAYS(TYPED_ARRAY_CONSTRUCTOR)
+
+function TypedArraySubArray(begin, end) {
+  switch (%_ClassOf(this)) {
+macro TYPED_ARRAY_SUBARRAY_CASE(ARRAY_ID, NAME, ELEMENT_SIZE)
+    case "NAME":
+      return %_Call(NAMESubArray, this, begin, end);
+endmacro
+TYPED_ARRAYS(TYPED_ARRAY_SUBARRAY_CASE)
+  }
+  throw MakeTypeError(kIncompatibleMethodReceiver,
+                      "get TypedArray.prototype.subarray", this);
+}
+%SetForceInlineFlag(TypedArraySubArray);
+
+function TypedArrayGetBuffer() {
+  if (!%_IsTypedArray(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        "get TypedArray.prototype.buffer", this);
+  }
+  return %TypedArrayGetBuffer(this);
+}
+%SetForceInlineFlag(TypedArrayGetBuffer);
+
+function TypedArrayGetByteLength() {
+  if (!%_IsTypedArray(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        "get TypedArray.prototype.byteLength", this);
+  }
+  return %_ArrayBufferViewGetByteLength(this);
+}
+%SetForceInlineFlag(TypedArrayGetByteLength);
+
+function TypedArrayGetByteOffset() {
+  if (!%_IsTypedArray(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        "get TypedArray.prototype.byteOffset", this);
+  }
+  return %_ArrayBufferViewGetByteOffset(this);
+}
+%SetForceInlineFlag(TypedArrayGetByteOffset);
+
+function TypedArrayGetLength() {
+  if (!%_IsTypedArray(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        "get TypedArray.prototype.length", this);
+  }
+  return %_TypedArrayGetLength(this);
+}
+%SetForceInlineFlag(TypedArrayGetLength);
+
+
+
+function TypedArraySetFromArrayLike(target, source, sourceLength, offset) {
+  if (offset > 0) {
+    for (var i = 0; i < sourceLength; i++) {
+      target[offset + i] = source[i];
+    }
+  }
+  else {
+    for (var i = 0; i < sourceLength; i++) {
+      target[i] = source[i];
+    }
+  }
+}
+
+function TypedArraySetFromOverlappingTypedArray(target, source, offset) {
+  var sourceElementSize = source.BYTES_PER_ELEMENT;
+  var targetElementSize = target.BYTES_PER_ELEMENT;
+  var sourceLength = source.length;
+
+  // Copy left part.
+  function CopyLeftPart() {
+    // First un-mutated byte after the next write
+    var targetPtr = target.byteOffset + (offset + 1) * targetElementSize;
+    // Next read at sourcePtr. We do not care for memory changing before
+    // sourcePtr - we have already copied it.
+    var sourcePtr = source.byteOffset;
+    for (var leftIndex = 0;
+         leftIndex < sourceLength && targetPtr <= sourcePtr;
+         leftIndex++) {
+      target[offset + leftIndex] = source[leftIndex];
+      targetPtr += targetElementSize;
+      sourcePtr += sourceElementSize;
+    }
+    return leftIndex;
+  }
+  var leftIndex = CopyLeftPart();
+
+  // Copy rigth part;
+  function CopyRightPart() {
+    // First unmutated byte before the next write
+    var targetPtr =
+      target.byteOffset + (offset + sourceLength - 1) * targetElementSize;
+    // Next read before sourcePtr. We do not care for memory changing after
+    // sourcePtr - we have already copied it.
+    var sourcePtr =
+      source.byteOffset + sourceLength * sourceElementSize;
+    for(var rightIndex = sourceLength - 1;
+        rightIndex >= leftIndex && targetPtr >= sourcePtr;
+        rightIndex--) {
+      target[offset + rightIndex] = source[rightIndex];
+      targetPtr -= targetElementSize;
+      sourcePtr -= sourceElementSize;
+    }
+    return rightIndex;
+  }
+  var rightIndex = CopyRightPart();
+
+  var temp = new GlobalArray(rightIndex + 1 - leftIndex);
+  for (var i = leftIndex; i <= rightIndex; i++) {
+    temp[i - leftIndex] = source[i];
+  }
+  for (i = leftIndex; i <= rightIndex; i++) {
+    target[offset + i] = temp[i - leftIndex];
+  }
+}
+
+function TypedArraySet(obj, offset) {
+  var intOffset = IS_UNDEFINED(offset) ? 0 : TO_INTEGER(offset);
+  if (intOffset < 0) throw MakeTypeError(kTypedArraySetNegativeOffset);
+
+  if (intOffset > %_MaxSmi()) {
+    throw MakeRangeError(kTypedArraySetSourceTooLarge);
+  }
+  switch (%TypedArraySetFastCases(this, obj, intOffset)) {
+    // These numbers should be synchronized with runtime.cc.
+    case 0: // TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE
+      return;
+    case 1: // TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING
+      TypedArraySetFromOverlappingTypedArray(this, obj, intOffset);
+      return;
+    case 2: // TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING
+      TypedArraySetFromArrayLike(this, obj, obj.length, intOffset);
+      return;
+    case 3: // TYPED_ARRAY_SET_NON_TYPED_ARRAY
+      var l = obj.length;
+      if (IS_UNDEFINED(l)) {
+        if (IS_NUMBER(obj)) {
+            // For number as a first argument, throw TypeError
+            // instead of silently ignoring the call, so that
+            // the user knows (s)he did something wrong.
+            // (Consistent with Firefox and Blink/WebKit)
+            throw MakeTypeError(kInvalidArgument);
+        }
+        return;
+      }
+      l = TO_LENGTH(l);
+      if (intOffset + l > this.length) {
+        throw MakeRangeError(kTypedArraySetSourceTooLarge);
+      }
+      TypedArraySetFromArrayLike(this, obj, l, intOffset);
+      return;
+  }
+}
+
+function TypedArrayGetToStringTag() {
+  if (!%_IsTypedArray(this)) return;
+  var name = %_ClassOf(this);
+  if (IS_UNDEFINED(name)) return;
+  return name;
+}
+
+
+function TypedArrayCopyWithin(target, start, end) {
+  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+  var length = %_TypedArrayGetLength(this);
+
+  // TODO(littledan): Replace with a memcpy for better performance
+  return InnerArrayCopyWithin(target, start, end, this, length);
+}
+%FunctionSetLength(TypedArrayCopyWithin, 2);
+
+
+// ES6 draft 05-05-15, section 22.2.3.7
+function TypedArrayEvery(f, receiver) {
+  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+  var length = %_TypedArrayGetLength(this);
+
+  return InnerArrayEvery(f, receiver, this, length);
+}
+%FunctionSetLength(TypedArrayEvery, 1);
+
+
+// ES6 draft 08-24-14, section 22.2.3.12
+function TypedArrayForEach(f, receiver) {
+  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+  var length = %_TypedArrayGetLength(this);
+
+  InnerArrayForEach(f, receiver, this, length);
+}
+%FunctionSetLength(TypedArrayForEach, 1);
+
+
+// ES6 draft 04-05-14 section 22.2.3.8
+function TypedArrayFill(value, start, end) {
+  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+  var length = %_TypedArrayGetLength(this);
+
+  return InnerArrayFill(value, start, end, this, length);
+}
+%FunctionSetLength(TypedArrayFill, 1);
+
+
+// ES6 draft 07-15-13, section 22.2.3.9
+function TypedArrayFilter(f, thisArg) {
+  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+  var length = %_TypedArrayGetLength(this);
+  if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
+  var result = new InternalArray();
+  InnerArrayFilter(f, thisArg, this, length, result);
+  var captured = result.length;
+  var output = TypedArraySpeciesCreate(this, captured);
+  for (var i = 0; i < captured; i++) {
+    output[i] = result[i];
+  }
+  return output;
+}
+%FunctionSetLength(TypedArrayFilter, 1);
+
+
+// ES6 draft 07-15-13, section 22.2.3.10
+function TypedArrayFind(predicate, thisArg) {
+  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+  var length = %_TypedArrayGetLength(this);
+
+  return InnerArrayFind(predicate, thisArg, this, length);
+}
+%FunctionSetLength(TypedArrayFind, 1);
+
+
+// ES6 draft 07-15-13, section 22.2.3.11
+function TypedArrayFindIndex(predicate, thisArg) {
+  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+  var length = %_TypedArrayGetLength(this);
+
+  return InnerArrayFindIndex(predicate, thisArg, this, length);
+}
+%FunctionSetLength(TypedArrayFindIndex, 1);
+
+
+// ES6 draft 05-18-15, section 22.2.3.21
+function TypedArrayReverse() {
+  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+  var length = %_TypedArrayGetLength(this);
+
+  return PackedArrayReverse(this, length);
+}
+
+
+function TypedArrayComparefn(x, y) {
+  if (IsNaN(x) && IsNaN(y)) {
+    return IsNaN(y) ? 0 : 1;
+  }
+  if (IsNaN(x)) {
+    return 1;
+  }
+  if (x === 0 && x === y) {
+    if (%_IsMinusZero(x)) {
+      if (!%_IsMinusZero(y)) {
+        return -1;
+      }
+    } else if (%_IsMinusZero(y)) {
+      return 1;
+    }
+  }
+  return x - y;
+}
+
+
+// ES6 draft 05-18-15, section 22.2.3.25
+function TypedArraySort(comparefn) {
+  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+  var length = %_TypedArrayGetLength(this);
+
+  if (IS_UNDEFINED(comparefn)) {
+    comparefn = TypedArrayComparefn;
+  }
+
+  return InnerArraySort(this, length, comparefn);
+}
+
+
+// ES6 section 22.2.3.13
+function TypedArrayIndexOf(element, index) {
+  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+  var length = %_TypedArrayGetLength(this);
+  return InnerArrayIndexOf(this, element, index, length);
+}
+%FunctionSetLength(TypedArrayIndexOf, 1);
+
+
+// ES6 section 22.2.3.16
+function TypedArrayLastIndexOf(element, index) {
+  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+  var length = %_TypedArrayGetLength(this);
+
+  return InnerArrayLastIndexOf(this, element, index, length,
+                        %_ArgumentsLength());
+}
+%FunctionSetLength(TypedArrayLastIndexOf, 1);
+
+
+// ES6 draft 07-15-13, section 22.2.3.18
+function TypedArrayMap(f, thisArg) {
+  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+  var length = %_TypedArrayGetLength(this);
+  var result = TypedArraySpeciesCreate(this, length);
+  if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
+  for (var i = 0; i < length; i++) {
+    var element = this[i];
+    result[i] = %_Call(f, thisArg, element, i, this);
+  }
+  return result;
+}
+%FunctionSetLength(TypedArrayMap, 1);
+
+
+// ES6 draft 05-05-15, section 22.2.3.24
+function TypedArraySome(f, receiver) {
+  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+  var length = %_TypedArrayGetLength(this);
+
+  return InnerArraySome(f, receiver, this, length);
+}
+%FunctionSetLength(TypedArraySome, 1);
+
+
+// ES6 section 22.2.3.27
+function TypedArrayToLocaleString() {
+  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+  var length = %_TypedArrayGetLength(this);
+
+  return InnerArrayToLocaleString(this, length);
+}
+
+
+// ES6 section 22.2.3.28
+function TypedArrayToString() {
+  return %_Call(ArrayToString, this);
+}
+
+
+// ES6 section 22.2.3.14
+function TypedArrayJoin(separator) {
+  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+  var length = %_TypedArrayGetLength(this);
+
+  return InnerArrayJoin(separator, this, length);
+}
+
+
+// ES6 draft 07-15-13, section 22.2.3.19
+function TypedArrayReduce(callback, current) {
+  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+  var length = %_TypedArrayGetLength(this);
+  return InnerArrayReduce(callback, current, this, length,
+                          %_ArgumentsLength());
+}
+%FunctionSetLength(TypedArrayReduce, 1);
+
+
+// ES6 draft 07-15-13, section 22.2.3.19
+function TypedArrayReduceRight(callback, current) {
+  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+  var length = %_TypedArrayGetLength(this);
+  return InnerArrayReduceRight(callback, current, this, length,
+                               %_ArgumentsLength());
+}
+%FunctionSetLength(TypedArrayReduceRight, 1);
+
+
+function TypedArraySlice(start, end) {
+  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+  var len = %_TypedArrayGetLength(this);
+
+  var relativeStart = TO_INTEGER(start);
+
+  var k;
+  if (relativeStart < 0) {
+    k = MaxSimple(len + relativeStart, 0);
+  } else {
+    k = MinSimple(relativeStart, len);
+  }
+
+  var relativeEnd;
+  if (IS_UNDEFINED(end)) {
+    relativeEnd = len;
+  } else {
+    relativeEnd = TO_INTEGER(end);
+  }
+
+  var final;
+  if (relativeEnd < 0) {
+    final = MaxSimple(len + relativeEnd, 0);
+  } else {
+    final = MinSimple(relativeEnd, len);
+  }
+
+  var count = MaxSimple(final - k, 0);
+  var array = TypedArraySpeciesCreate(this, count);
+  // The code below is the 'then' branch; the 'else' branch species
+  // a memcpy. Because V8 doesn't canonicalize NaN, the difference is
+  // unobservable.
+  var n = 0;
+  while (k < final) {
+    var kValue = this[k];
+    array[n] = kValue;
+    k++;
+    n++;
+  }
+  return array;
+}
+
+
+// ES2016 draft, section 22.2.3.14
+function TypedArrayIncludes(searchElement, fromIndex) {
+  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+  var length = %_TypedArrayGetLength(this);
+
+  return InnerArrayIncludes(searchElement, fromIndex, this, length);
+}
+%FunctionSetLength(TypedArrayIncludes, 1);
+
+
+// ES6 draft 08-24-14, section 22.2.2.2
+function TypedArrayOf() {
+  var length = %_ArgumentsLength();
+  var array = TypedArrayCreate(this, length);
+  for (var i = 0; i < length; i++) {
+    array[i] = %_Arguments(i);
+  }
+  return array;
+}
+
+
+function TypedArrayFrom(source, mapfn, thisArg) {
+  // TODO(littledan): Investigate if there is a receiver which could be
+  // faster to accumulate on than Array, e.g., a TypedVector.
+  // TODO(littledan): Rewrite this code to ensure that things happen
+  // in the right order, e.g., the constructor needs to be called before
+  // the mapping function on array-likes.
+  var array = %_Call(ArrayFrom, GlobalArray, source, mapfn, thisArg);
+  return TypedArrayCreate(this, array);
+}
+%FunctionSetLength(TypedArrayFrom, 1);
+
+function TypedArray() {
+  if (IS_UNDEFINED(new.target)) {
+    throw MakeTypeError(kConstructorNonCallable, "TypedArray");
+  }
+  if (new.target === TypedArray) {
+    throw MakeTypeError(kConstructAbstractClass, "TypedArray");
+  }
+}
+
+// -------------------------------------------------------------------
+
+%FunctionSetPrototype(TypedArray, new GlobalObject());
+%AddNamedProperty(TypedArray.prototype,
+                  "constructor", TypedArray, DONT_ENUM);
+utils.InstallFunctions(TypedArray, DONT_ENUM | DONT_DELETE | READ_ONLY, [
+  "from", TypedArrayFrom,
+  "of", TypedArrayOf
+]);
+utils.InstallGetter(TypedArray.prototype, "buffer", TypedArrayGetBuffer);
+utils.InstallGetter(TypedArray.prototype, "byteOffset", TypedArrayGetByteOffset,
+                    DONT_ENUM | DONT_DELETE);
+utils.InstallGetter(TypedArray.prototype, "byteLength",
+                    TypedArrayGetByteLength, DONT_ENUM | DONT_DELETE);
+utils.InstallGetter(TypedArray.prototype, "length", TypedArrayGetLength,
+                    DONT_ENUM | DONT_DELETE);
+utils.InstallGetter(TypedArray.prototype, toStringTagSymbol,
+                    TypedArrayGetToStringTag);
+utils.InstallFunctions(TypedArray.prototype, DONT_ENUM, [
+  "subarray", TypedArraySubArray,
+  "set", TypedArraySet,
+  "copyWithin", TypedArrayCopyWithin,
+  "every", TypedArrayEvery,
+  "fill", TypedArrayFill,
+  "filter", TypedArrayFilter,
+  "find", TypedArrayFind,
+  "findIndex", TypedArrayFindIndex,
+  "includes", TypedArrayIncludes,
+  "indexOf", TypedArrayIndexOf,
+  "join", TypedArrayJoin,
+  "lastIndexOf", TypedArrayLastIndexOf,
+  "forEach", TypedArrayForEach,
+  "map", TypedArrayMap,
+  "reduce", TypedArrayReduce,
+  "reduceRight", TypedArrayReduceRight,
+  "reverse", TypedArrayReverse,
+  "slice", TypedArraySlice,
+  "some", TypedArraySome,
+  "sort", TypedArraySort,
+  "toString", TypedArrayToString,
+  "toLocaleString", TypedArrayToLocaleString
+]);
+
+
+macro SETUP_TYPED_ARRAY(ARRAY_ID, NAME, ELEMENT_SIZE)
+  %SetCode(GlobalNAME, NAMEConstructor);
+  %FunctionSetPrototype(GlobalNAME, new GlobalObject());
+  %InternalSetPrototype(GlobalNAME, TypedArray);
+  %InternalSetPrototype(GlobalNAME.prototype, TypedArray.prototype);
+
+  %AddNamedProperty(GlobalNAME, "BYTES_PER_ELEMENT", ELEMENT_SIZE,
+                    READ_ONLY | DONT_ENUM | DONT_DELETE);
+
+  %AddNamedProperty(GlobalNAME.prototype,
+                    "constructor", global.NAME, DONT_ENUM);
+  %AddNamedProperty(GlobalNAME.prototype,
+                    "BYTES_PER_ELEMENT", ELEMENT_SIZE,
+                    READ_ONLY | DONT_ENUM | DONT_DELETE);
+  // TODO(littledan): Remove this performance workaround BUG(chromium:579905)
+  utils.InstallGetter(GlobalNAME.prototype, "length", NAME_GetLength,
+                      DONT_ENUM | DONT_DELETE);
+endmacro
+
+TYPED_ARRAYS(SETUP_TYPED_ARRAY)
+
+// --------------------------- DataView -----------------------------
+
+function DataViewConstructor(buffer, byteOffset, byteLength) { // length = 3
+  if (IS_UNDEFINED(new.target)) {
+    throw MakeTypeError(kConstructorNotFunction, "DataView");
+  }
+
+  // TODO(binji): support SharedArrayBuffers?
+  if (!IS_ARRAYBUFFER(buffer)) throw MakeTypeError(kDataViewNotArrayBuffer);
+  if (!IS_UNDEFINED(byteOffset)) {
+    byteOffset = ToPositiveInteger(byteOffset, kInvalidDataViewOffset);
+  }
+  if (!IS_UNDEFINED(byteLength)) {
+    byteLength = TO_INTEGER(byteLength);
+  }
+
+  var bufferByteLength = %_ArrayBufferGetByteLength(buffer);
+
+  var offset = IS_UNDEFINED(byteOffset) ?  0 : byteOffset;
+  if (offset > bufferByteLength) throw MakeRangeError(kInvalidDataViewOffset);
+
+  var length = IS_UNDEFINED(byteLength)
+      ? bufferByteLength - offset
+      : byteLength;
+  if (length < 0 || offset + length > bufferByteLength) {
+    throw new MakeRangeError(kInvalidDataViewLength);
+  }
+  var result = %NewObject(GlobalDataView, new.target);
+  %_DataViewInitialize(result, buffer, offset, length);
+  return result;
+}
+
+function DataViewGetBufferJS() {
+  if (!IS_DATAVIEW(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver, 'DataView.buffer', this);
+  }
+  return %DataViewGetBuffer(this);
+}
+
+function DataViewGetByteOffset() {
+  if (!IS_DATAVIEW(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'DataView.byteOffset', this);
+  }
+  return %_ArrayBufferViewGetByteOffset(this);
+}
+
+function DataViewGetByteLength() {
+  if (!IS_DATAVIEW(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'DataView.byteLength', this);
+  }
+  return %_ArrayBufferViewGetByteLength(this);
+}
+
+macro DATA_VIEW_TYPES(FUNCTION)
+  FUNCTION(Int8)
+  FUNCTION(Uint8)
+  FUNCTION(Int16)
+  FUNCTION(Uint16)
+  FUNCTION(Int32)
+  FUNCTION(Uint32)
+  FUNCTION(Float32)
+  FUNCTION(Float64)
+endmacro
+
+
+macro DATA_VIEW_GETTER_SETTER(TYPENAME)
+function DataViewGetTYPENAMEJS(offset, little_endian) {
+  if (!IS_DATAVIEW(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'DataView.getTYPENAME', this);
+  }
+  if (%_ArgumentsLength() < 1) throw MakeTypeError(kInvalidArgument);
+  offset = ToPositiveInteger(offset, kInvalidDataViewAccessorOffset);
+  return %DataViewGetTYPENAME(this, offset, !!little_endian);
+}
+
+function DataViewSetTYPENAMEJS(offset, value, little_endian) {
+  if (!IS_DATAVIEW(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'DataView.setTYPENAME', this);
+  }
+  if (%_ArgumentsLength() < 2) throw MakeTypeError(kInvalidArgument);
+  offset = ToPositiveInteger(offset, kInvalidDataViewAccessorOffset);
+  %DataViewSetTYPENAME(this, offset, TO_NUMBER(value), !!little_endian);
+}
+endmacro
+
+DATA_VIEW_TYPES(DATA_VIEW_GETTER_SETTER)
+
+// Setup the DataView constructor.
+%SetCode(GlobalDataView, DataViewConstructor);
+%FunctionSetPrototype(GlobalDataView, new GlobalObject);
+
+// Set up constructor property on the DataView prototype.
+%AddNamedProperty(GlobalDataView.prototype, "constructor", GlobalDataView,
+                  DONT_ENUM);
+%AddNamedProperty(GlobalDataView.prototype, toStringTagSymbol, "DataView",
+                  READ_ONLY|DONT_ENUM);
+
+utils.InstallGetter(GlobalDataView.prototype, "buffer", DataViewGetBufferJS);
+utils.InstallGetter(GlobalDataView.prototype, "byteOffset",
+                    DataViewGetByteOffset);
+utils.InstallGetter(GlobalDataView.prototype, "byteLength",
+                    DataViewGetByteLength);
+
+utils.InstallFunctions(GlobalDataView.prototype, DONT_ENUM, [
+  "getInt8", DataViewGetInt8JS,
+  "setInt8", DataViewSetInt8JS,
+
+  "getUint8", DataViewGetUint8JS,
+  "setUint8", DataViewSetUint8JS,
+
+  "getInt16", DataViewGetInt16JS,
+  "setInt16", DataViewSetInt16JS,
+
+  "getUint16", DataViewGetUint16JS,
+  "setUint16", DataViewSetUint16JS,
+
+  "getInt32", DataViewGetInt32JS,
+  "setInt32", DataViewSetInt32JS,
+
+  "getUint32", DataViewGetUint32JS,
+  "setUint32", DataViewSetUint32JS,
+
+  "getFloat32", DataViewGetFloat32JS,
+  "setFloat32", DataViewSetFloat32JS,
+
+  "getFloat64", DataViewGetFloat64JS,
+  "setFloat64", DataViewSetFloat64JS
+]);
+
+})
diff --git a/src/js/uri.js b/src/js/uri.js
new file mode 100644
index 0000000..712d7e6
--- /dev/null
+++ b/src/js/uri.js
@@ -0,0 +1,380 @@
+// Copyright 2006-2008 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.
+
+// This file contains support for URI manipulations written in
+// JavaScript.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+//- ------------------------------------------------------------------
+// Imports
+
+var GlobalObject = global.Object;
+var GlobalArray = global.Array;
+var InternalArray = utils.InternalArray;
+var MakeURIError;
+
+utils.Import(function(from) {
+  MakeURIError = from.MakeURIError;
+});
+
+
+// -------------------------------------------------------------------
+// Define internal helper functions.
+
+function HexValueOf(code) {
+  // 0-9
+  if (code >= 48 && code <= 57) return code - 48;
+  // A-F
+  if (code >= 65 && code <= 70) return code - 55;
+  // a-f
+  if (code >= 97 && code <= 102) return code - 87;
+
+  return -1;
+}
+
+// Does the char code correspond to an alpha-numeric char.
+function isAlphaNumeric(cc) {
+  // a - z
+  if (97 <= cc && cc <= 122) return true;
+  // A - Z
+  if (65 <= cc && cc <= 90) return true;
+  // 0 - 9
+  if (48 <= cc && cc <= 57) return true;
+
+  return false;
+}
+
+// Lazily initialized.
+var hexCharCodeArray = 0;
+
+function URIAddEncodedOctetToBuffer(octet, result, index) {
+  result[index++] = 37; // Char code of '%'.
+  result[index++] = hexCharCodeArray[octet >> 4];
+  result[index++] = hexCharCodeArray[octet & 0x0F];
+  return index;
+}
+
+function URIEncodeOctets(octets, result, index) {
+  if (hexCharCodeArray === 0) {
+    hexCharCodeArray = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+                        65, 66, 67, 68, 69, 70];
+  }
+  index = URIAddEncodedOctetToBuffer(octets[0], result, index);
+  if (octets[1]) index = URIAddEncodedOctetToBuffer(octets[1], result, index);
+  if (octets[2]) index = URIAddEncodedOctetToBuffer(octets[2], result, index);
+  if (octets[3]) index = URIAddEncodedOctetToBuffer(octets[3], result, index);
+  return index;
+}
+
+function URIEncodeSingle(cc, result, index) {
+  var x = (cc >> 12) & 0xF;
+  var y = (cc >> 6) & 63;
+  var z = cc & 63;
+  var octets = new GlobalArray(3);
+  if (cc <= 0x007F) {
+    octets[0] = cc;
+  } else if (cc <= 0x07FF) {
+    octets[0] = y + 192;
+    octets[1] = z + 128;
+  } else {
+    octets[0] = x + 224;
+    octets[1] = y + 128;
+    octets[2] = z + 128;
+  }
+  return URIEncodeOctets(octets, result, index);
+}
+
+function URIEncodePair(cc1 , cc2, result, index) {
+  var u = ((cc1 >> 6) & 0xF) + 1;
+  var w = (cc1 >> 2) & 0xF;
+  var x = cc1 & 3;
+  var y = (cc2 >> 6) & 0xF;
+  var z = cc2 & 63;
+  var octets = new GlobalArray(4);
+  octets[0] = (u >> 2) + 240;
+  octets[1] = (((u & 3) << 4) | w) + 128;
+  octets[2] = ((x << 4) | y) + 128;
+  octets[3] = z + 128;
+  return URIEncodeOctets(octets, result, index);
+}
+
+function URIHexCharsToCharCode(highChar, lowChar) {
+  var highCode = HexValueOf(highChar);
+  var lowCode = HexValueOf(lowChar);
+  if (highCode == -1 || lowCode == -1) throw MakeURIError();
+  return (highCode << 4) | lowCode;
+}
+
+// Callers must ensure that |result| is a sufficiently long sequential
+// two-byte string!
+function URIDecodeOctets(octets, result, index) {
+  var value;
+  var o0 = octets[0];
+  if (o0 < 0x80) {
+    value = o0;
+  } else if (o0 < 0xc2) {
+    throw MakeURIError();
+  } else {
+    var o1 = octets[1];
+    if (o0 < 0xe0) {
+      var a = o0 & 0x1f;
+      if ((o1 < 0x80) || (o1 > 0xbf)) throw MakeURIError();
+      var b = o1 & 0x3f;
+      value = (a << 6) + b;
+      if (value < 0x80 || value > 0x7ff) throw MakeURIError();
+    } else {
+      var o2 = octets[2];
+      if (o0 < 0xf0) {
+        var a = o0 & 0x0f;
+        if ((o1 < 0x80) || (o1 > 0xbf)) throw MakeURIError();
+        var b = o1 & 0x3f;
+        if ((o2 < 0x80) || (o2 > 0xbf)) throw MakeURIError();
+        var c = o2 & 0x3f;
+        value = (a << 12) + (b << 6) + c;
+        if ((value < 0x800) || (value > 0xffff)) throw MakeURIError();
+      } else {
+        var o3 = octets[3];
+        if (o0 < 0xf8) {
+          var a = (o0 & 0x07);
+          if ((o1 < 0x80) || (o1 > 0xbf)) throw MakeURIError();
+          var b = (o1 & 0x3f);
+          if ((o2 < 0x80) || (o2 > 0xbf)) {
+            throw MakeURIError();
+          }
+          var c = (o2 & 0x3f);
+          if ((o3 < 0x80) || (o3 > 0xbf)) throw MakeURIError();
+          var d = (o3 & 0x3f);
+          value = (a << 18) + (b << 12) + (c << 6) + d;
+          if ((value < 0x10000) || (value > 0x10ffff)) throw MakeURIError();
+        } else {
+          throw MakeURIError();
+        }
+      }
+    }
+  }
+  if (0xD800 <= value && value <= 0xDFFF) throw MakeURIError();
+  if (value < 0x10000) {
+    %_TwoByteSeqStringSetChar(index++, value, result);
+  } else {
+    %_TwoByteSeqStringSetChar(index++, (value >> 10) + 0xd7c0, result);
+    %_TwoByteSeqStringSetChar(index++, (value & 0x3ff) + 0xdc00, result);
+  }
+  return index;
+}
+
+// ECMA-262, section 15.1.3
+function Encode(uri, unescape) {
+  uri = TO_STRING(uri);
+  var uriLength = uri.length;
+  var array = new InternalArray(uriLength);
+  var index = 0;
+  for (var k = 0; k < uriLength; k++) {
+    var cc1 = %_StringCharCodeAt(uri, k);
+    if (unescape(cc1)) {
+      array[index++] = cc1;
+    } else {
+      if (cc1 >= 0xDC00 && cc1 <= 0xDFFF) throw MakeURIError();
+      if (cc1 < 0xD800 || cc1 > 0xDBFF) {
+        index = URIEncodeSingle(cc1, array, index);
+      } else {
+        k++;
+        if (k == uriLength) throw MakeURIError();
+        var cc2 = %_StringCharCodeAt(uri, k);
+        if (cc2 < 0xDC00 || cc2 > 0xDFFF) throw MakeURIError();
+        index = URIEncodePair(cc1, cc2, array, index);
+      }
+    }
+  }
+
+  var result = %NewString(array.length, NEW_ONE_BYTE_STRING);
+  for (var i = 0; i < array.length; i++) {
+    %_OneByteSeqStringSetChar(i, array[i], result);
+  }
+  return result;
+}
+
+// ECMA-262, section 15.1.3
+function Decode(uri, reserved) {
+  uri = TO_STRING(uri);
+  var uriLength = uri.length;
+  var one_byte = %NewString(uriLength, NEW_ONE_BYTE_STRING);
+  var index = 0;
+  var k = 0;
+
+  // Optimistically assume one-byte string.
+  for ( ; k < uriLength; k++) {
+    var code = %_StringCharCodeAt(uri, k);
+    if (code == 37) {  // '%'
+      if (k + 2 >= uriLength) throw MakeURIError();
+      var cc = URIHexCharsToCharCode(%_StringCharCodeAt(uri, k+1),
+                                     %_StringCharCodeAt(uri, k+2));
+      if (cc >> 7) break;  // Assumption wrong, two-byte string.
+      if (reserved(cc)) {
+        %_OneByteSeqStringSetChar(index++, 37, one_byte);  // '%'.
+        %_OneByteSeqStringSetChar(index++, %_StringCharCodeAt(uri, k+1),
+                                  one_byte);
+        %_OneByteSeqStringSetChar(index++, %_StringCharCodeAt(uri, k+2),
+                                  one_byte);
+      } else {
+        %_OneByteSeqStringSetChar(index++, cc, one_byte);
+      }
+      k += 2;
+    } else {
+      if (code > 0x7f) break;  // Assumption wrong, two-byte string.
+      %_OneByteSeqStringSetChar(index++, code, one_byte);
+    }
+  }
+
+  one_byte = %TruncateString(one_byte, index);
+  if (k == uriLength) return one_byte;
+
+  // Write into two byte string.
+  var two_byte = %NewString(uriLength - k, NEW_TWO_BYTE_STRING);
+  index = 0;
+
+  for ( ; k < uriLength; k++) {
+    var code = %_StringCharCodeAt(uri, k);
+    if (code == 37) {  // '%'
+      if (k + 2 >= uriLength) throw MakeURIError();
+      var cc = URIHexCharsToCharCode(%_StringCharCodeAt(uri, ++k),
+                                     %_StringCharCodeAt(uri, ++k));
+      if (cc >> 7) {
+        var n = 0;
+        while (((cc << ++n) & 0x80) != 0) { }
+        if (n == 1 || n > 4) throw MakeURIError();
+        var octets = new GlobalArray(n);
+        octets[0] = cc;
+        if (k + 3 * (n - 1) >= uriLength) throw MakeURIError();
+        for (var i = 1; i < n; i++) {
+          if (uri[++k] != '%') throw MakeURIError();
+          octets[i] = URIHexCharsToCharCode(%_StringCharCodeAt(uri, ++k),
+                                            %_StringCharCodeAt(uri, ++k));
+        }
+        index = URIDecodeOctets(octets, two_byte, index);
+      } else  if (reserved(cc)) {
+        %_TwoByteSeqStringSetChar(index++, 37, two_byte);  // '%'.
+        %_TwoByteSeqStringSetChar(index++, %_StringCharCodeAt(uri, k - 1),
+                                  two_byte);
+        %_TwoByteSeqStringSetChar(index++, %_StringCharCodeAt(uri, k),
+                                  two_byte);
+      } else {
+        %_TwoByteSeqStringSetChar(index++, cc, two_byte);
+      }
+    } else {
+      %_TwoByteSeqStringSetChar(index++, code, two_byte);
+    }
+  }
+
+  two_byte = %TruncateString(two_byte, index);
+  return one_byte + two_byte;
+}
+
+// -------------------------------------------------------------------
+// Define exported functions.
+
+// ECMA-262 - B.2.1.
+function URIEscapeJS(s) {
+  return %URIEscape(s);
+}
+
+// ECMA-262 - B.2.2.
+function URIUnescapeJS(s) {
+  return %URIUnescape(s);
+}
+
+// ECMA-262 - 15.1.3.1.
+function URIDecode(uri) {
+  var reservedPredicate = function(cc) {
+    // #$
+    if (35 <= cc && cc <= 36) return true;
+    // &
+    if (cc == 38) return true;
+    // +,
+    if (43 <= cc && cc <= 44) return true;
+    // /
+    if (cc == 47) return true;
+    // :;
+    if (58 <= cc && cc <= 59) return true;
+    // =
+    if (cc == 61) return true;
+    // ?@
+    if (63 <= cc && cc <= 64) return true;
+
+    return false;
+  };
+  return Decode(uri, reservedPredicate);
+}
+
+// ECMA-262 - 15.1.3.2.
+function URIDecodeComponent(component) {
+  var reservedPredicate = function(cc) { return false; };
+  return Decode(component, reservedPredicate);
+}
+
+// ECMA-262 - 15.1.3.3.
+function URIEncode(uri) {
+  var unescapePredicate = function(cc) {
+    if (isAlphaNumeric(cc)) return true;
+    // !
+    if (cc == 33) return true;
+    // #$
+    if (35 <= cc && cc <= 36) return true;
+    // &'()*+,-./
+    if (38 <= cc && cc <= 47) return true;
+    // :;
+    if (58 <= cc && cc <= 59) return true;
+    // =
+    if (cc == 61) return true;
+    // ?@
+    if (63 <= cc && cc <= 64) return true;
+    // _
+    if (cc == 95) return true;
+    // ~
+    if (cc == 126) return true;
+
+    return false;
+  };
+  return Encode(uri, unescapePredicate);
+}
+
+// ECMA-262 - 15.1.3.4
+function URIEncodeComponent(component) {
+  var unescapePredicate = function(cc) {
+    if (isAlphaNumeric(cc)) return true;
+    // !
+    if (cc == 33) return true;
+    // '()*
+    if (39 <= cc && cc <= 42) return true;
+    // -.
+    if (45 <= cc && cc <= 46) return true;
+    // _
+    if (cc == 95) return true;
+    // ~
+    if (cc == 126) return true;
+
+    return false;
+  };
+  return Encode(component, unescapePredicate);
+}
+
+// -------------------------------------------------------------------
+// Install exported functions.
+
+// Set up non-enumerable URI functions on the global object and set
+// their names.
+utils.InstallFunctions(global, DONT_ENUM, [
+  "escape", URIEscapeJS,
+  "unescape", URIUnescapeJS,
+  "decodeURI", URIDecode,
+  "decodeURIComponent", URIDecodeComponent,
+  "encodeURI", URIEncode,
+  "encodeURIComponent", URIEncodeComponent
+]);
+
+})
diff --git a/src/js/v8natives.js b/src/js/v8natives.js
new file mode 100644
index 0000000..26447da
--- /dev/null
+++ b/src/js/v8natives.js
@@ -0,0 +1,1190 @@
+// Copyright 2012 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.
+
+(function(global, utils) {
+
+%CheckIsBootstrapping();
+
+// ----------------------------------------------------------------------------
+// Imports
+
+var GlobalArray = global.Array;
+var GlobalBoolean = global.Boolean;
+var GlobalNumber = global.Number;
+var GlobalObject = global.Object;
+var InternalArray = utils.InternalArray;
+var iteratorSymbol = utils.ImportNow("iterator_symbol");
+var MakeRangeError;
+var MakeSyntaxError;
+var MakeTypeError;
+var MathAbs;
+var NaN = %GetRootNaN();
+var ObjectToString = utils.ImportNow("object_to_string");
+var ObserveBeginPerformSplice;
+var ObserveEndPerformSplice;
+var ObserveEnqueueSpliceRecord;
+var SameValue = utils.ImportNow("SameValue");
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+
+utils.Import(function(from) {
+  MakeRangeError = from.MakeRangeError;
+  MakeSyntaxError = from.MakeSyntaxError;
+  MakeTypeError = from.MakeTypeError;
+  MathAbs = from.MathAbs;
+  ObserveBeginPerformSplice = from.ObserveBeginPerformSplice;
+  ObserveEndPerformSplice = from.ObserveEndPerformSplice;
+  ObserveEnqueueSpliceRecord = from.ObserveEnqueueSpliceRecord;
+});
+
+// ----------------------------------------------------------------------------
+
+
+// ES6 18.2.3 isNaN(number)
+function GlobalIsNaN(number) {
+  number = TO_NUMBER(number);
+  return NUMBER_IS_NAN(number);
+}
+
+
+// ES6 18.2.2 isFinite(number)
+function GlobalIsFinite(number) {
+  number = TO_NUMBER(number);
+  return NUMBER_IS_FINITE(number);
+}
+
+
+// ES6 18.2.5 parseInt(string, radix)
+function GlobalParseInt(string, radix) {
+  if (IS_UNDEFINED(radix) || radix === 10 || radix === 0) {
+    // Some people use parseInt instead of Math.floor.  This
+    // optimization makes parseInt on a Smi 12 times faster (60ns
+    // vs 800ns).  The following optimization makes parseInt on a
+    // non-Smi number 9 times faster (230ns vs 2070ns).  Together
+    // they make parseInt on a string 1.4% slower (274ns vs 270ns).
+    if (%_IsSmi(string)) return string;
+    if (IS_NUMBER(string) &&
+        ((0.01 < string && string < 1e9) ||
+            (-1e9 < string && string < -0.01))) {
+      // Truncate number.
+      return string | 0;
+    }
+    string = TO_STRING(string);
+    radix = radix | 0;
+  } else {
+    // The spec says ToString should be evaluated before ToInt32.
+    string = TO_STRING(string);
+    radix = TO_INT32(radix);
+    if (!(radix == 0 || (2 <= radix && radix <= 36))) {
+      return NaN;
+    }
+  }
+
+  if (%_HasCachedArrayIndex(string) &&
+      (radix == 0 || radix == 10)) {
+    return %_GetCachedArrayIndex(string);
+  }
+  return %StringParseInt(string, radix);
+}
+
+
+// ES6 18.2.4 parseFloat(string)
+function GlobalParseFloat(string) {
+  // 1. Let inputString be ? ToString(string).
+  string = TO_STRING(string);
+  if (%_HasCachedArrayIndex(string)) return %_GetCachedArrayIndex(string);
+  return %StringParseFloat(string);
+}
+
+
+// ----------------------------------------------------------------------------
+
+// Set up global object.
+var attributes = DONT_ENUM | DONT_DELETE | READ_ONLY;
+
+utils.InstallConstants(global, [
+  // ES6 18.1.1
+  "Infinity", INFINITY,
+  // ES6 18.1.2
+  "NaN", NaN,
+  // ES6 18.1.3
+  "undefined", UNDEFINED,
+]);
+
+// Set up non-enumerable function on the global object.
+utils.InstallFunctions(global, DONT_ENUM, [
+  "isNaN", GlobalIsNaN,
+  "isFinite", GlobalIsFinite,
+  "parseInt", GlobalParseInt,
+  "parseFloat", GlobalParseFloat,
+]);
+
+
+// ----------------------------------------------------------------------------
+// Object
+
+// ES6 19.1.3.5 Object.prototype.toLocaleString([reserved1 [,reserved2]])
+function ObjectToLocaleString() {
+  CHECK_OBJECT_COERCIBLE(this, "Object.prototype.toLocaleString");
+  return this.toString();
+}
+
+
+// ES6 19.1.3.7 Object.prototype.valueOf()
+function ObjectValueOf() {
+  return TO_OBJECT(this);
+}
+
+
+// ES6 7.3.11
+function ObjectHasOwnProperty(value) {
+  var name = TO_NAME(value);
+  var object = TO_OBJECT(this);
+  return %HasOwnProperty(object, name);
+}
+
+
+// ES6 19.1.3.3 Object.prototype.isPrototypeOf(V)
+function ObjectIsPrototypeOf(V) {
+  if (!IS_RECEIVER(V)) return false;
+  var O = TO_OBJECT(this);
+  return %HasInPrototypeChain(V, O);
+}
+
+
+// ES6 19.1.3.4
+function ObjectPropertyIsEnumerable(V) {
+  var P = TO_NAME(V);
+  return %PropertyIsEnumerable(TO_OBJECT(this), P);
+}
+
+
+// Extensions for providing property getters and setters.
+function ObjectDefineGetter(name, fun) {
+  var receiver = this;
+  if (IS_NULL(receiver) || IS_UNDEFINED(receiver)) {
+    receiver = %GlobalProxy(ObjectDefineGetter);
+  }
+  if (!IS_CALLABLE(fun)) {
+    throw MakeTypeError(kObjectGetterExpectingFunction);
+  }
+  var desc = new PropertyDescriptor();
+  desc.setGet(fun);
+  desc.setEnumerable(true);
+  desc.setConfigurable(true);
+  DefineOwnProperty(TO_OBJECT(receiver), TO_NAME(name), desc, false);
+}
+
+
+function ObjectLookupGetter(name) {
+  var receiver = this;
+  if (IS_NULL(receiver) || IS_UNDEFINED(receiver)) {
+    receiver = %GlobalProxy(ObjectLookupGetter);
+  }
+  return %LookupAccessor(TO_OBJECT(receiver), TO_NAME(name), GETTER);
+}
+
+
+function ObjectDefineSetter(name, fun) {
+  var receiver = this;
+  if (IS_NULL(receiver) || IS_UNDEFINED(receiver)) {
+    receiver = %GlobalProxy(ObjectDefineSetter);
+  }
+  if (!IS_CALLABLE(fun)) {
+    throw MakeTypeError(kObjectSetterExpectingFunction);
+  }
+  var desc = new PropertyDescriptor();
+  desc.setSet(fun);
+  desc.setEnumerable(true);
+  desc.setConfigurable(true);
+  DefineOwnProperty(TO_OBJECT(receiver), TO_NAME(name), desc, false);
+}
+
+
+function ObjectLookupSetter(name) {
+  var receiver = this;
+  if (IS_NULL(receiver) || IS_UNDEFINED(receiver)) {
+    receiver = %GlobalProxy(ObjectLookupSetter);
+  }
+  return %LookupAccessor(TO_OBJECT(receiver), TO_NAME(name), SETTER);
+}
+
+
+// ES6 6.2.4.1
+function IsAccessorDescriptor(desc) {
+  if (IS_UNDEFINED(desc)) return false;
+  return desc.hasGetter() || desc.hasSetter();
+}
+
+
+// ES6 6.2.4.2
+function IsDataDescriptor(desc) {
+  if (IS_UNDEFINED(desc)) return false;
+  return desc.hasValue() || desc.hasWritable();
+}
+
+
+// ES6 6.2.4.3
+function IsGenericDescriptor(desc) {
+  if (IS_UNDEFINED(desc)) return false;
+  return !(IsAccessorDescriptor(desc) || IsDataDescriptor(desc));
+}
+
+
+function IsInconsistentDescriptor(desc) {
+  return IsAccessorDescriptor(desc) && IsDataDescriptor(desc);
+}
+
+
+// Harmony Proxies
+function FromGenericPropertyDescriptor(desc) {
+  if (IS_UNDEFINED(desc)) return desc;
+  var obj = new GlobalObject();
+
+  if (desc.hasValue()) {
+    %AddNamedProperty(obj, "value", desc.getValue(), NONE);
+  }
+  if (desc.hasWritable()) {
+    %AddNamedProperty(obj, "writable", desc.isWritable(), NONE);
+  }
+  if (desc.hasGetter()) {
+    %AddNamedProperty(obj, "get", desc.getGet(), NONE);
+  }
+  if (desc.hasSetter()) {
+    %AddNamedProperty(obj, "set", desc.getSet(), NONE);
+  }
+  if (desc.hasEnumerable()) {
+    %AddNamedProperty(obj, "enumerable", desc.isEnumerable(), NONE);
+  }
+  if (desc.hasConfigurable()) {
+    %AddNamedProperty(obj, "configurable", desc.isConfigurable(), NONE);
+  }
+  return obj;
+}
+
+
+// ES6 6.2.4.5
+function ToPropertyDescriptor(obj) {
+  if (!IS_RECEIVER(obj)) throw MakeTypeError(kPropertyDescObject, obj);
+
+  var desc = new PropertyDescriptor();
+
+  if ("enumerable" in obj) {
+    desc.setEnumerable(TO_BOOLEAN(obj.enumerable));
+  }
+
+  if ("configurable" in obj) {
+    desc.setConfigurable(TO_BOOLEAN(obj.configurable));
+  }
+
+  if ("value" in obj) {
+    desc.setValue(obj.value);
+  }
+
+  if ("writable" in obj) {
+    desc.setWritable(TO_BOOLEAN(obj.writable));
+  }
+
+  if ("get" in obj) {
+    var get = obj.get;
+    if (!IS_UNDEFINED(get) && !IS_CALLABLE(get)) {
+      throw MakeTypeError(kObjectGetterCallable, get);
+    }
+    desc.setGet(get);
+  }
+
+  if ("set" in obj) {
+    var set = obj.set;
+    if (!IS_UNDEFINED(set) && !IS_CALLABLE(set)) {
+      throw MakeTypeError(kObjectSetterCallable, set);
+    }
+    desc.setSet(set);
+  }
+
+  if (IsInconsistentDescriptor(desc)) {
+    throw MakeTypeError(kValueAndAccessor, obj);
+  }
+  return desc;
+}
+
+// TODO(cbruni): remove once callers have been removed
+function ToCompletePropertyDescriptor(obj) {
+  var desc = ToPropertyDescriptor(obj);
+  if (IsGenericDescriptor(desc) || IsDataDescriptor(desc)) {
+    if (!desc.hasValue()) desc.setValue(UNDEFINED);
+    if (!desc.hasWritable()) desc.setWritable(false);
+  } else {
+    // Is accessor descriptor.
+    if (!desc.hasGetter()) desc.setGet(UNDEFINED);
+    if (!desc.hasSetter()) desc.setSet(UNDEFINED);
+  }
+  if (!desc.hasEnumerable()) desc.setEnumerable(false);
+  if (!desc.hasConfigurable()) desc.setConfigurable(false);
+  return desc;
+}
+
+
+function PropertyDescriptor() {
+  // Initialize here so they are all in-object and have the same map.
+  // Default values from ES5 8.6.1.
+  this.value_ = UNDEFINED;
+  this.hasValue_ = false;
+  this.writable_ = false;
+  this.hasWritable_ = false;
+  this.enumerable_ = false;
+  this.hasEnumerable_ = false;
+  this.configurable_ = false;
+  this.hasConfigurable_ = false;
+  this.get_ = UNDEFINED;
+  this.hasGetter_ = false;
+  this.set_ = UNDEFINED;
+  this.hasSetter_ = false;
+}
+
+utils.SetUpLockedPrototype(PropertyDescriptor, [
+  "value_",
+  "hasValue_",
+  "writable_",
+  "hasWritable_",
+  "enumerable_",
+  "hasEnumerable_",
+  "configurable_",
+  "hasConfigurable_",
+  "get_",
+  "hasGetter_",
+  "set_",
+  "hasSetter_"
+], [
+  "toString", function PropertyDescriptor_ToString() {
+    return "[object PropertyDescriptor]";
+  },
+  "setValue", function PropertyDescriptor_SetValue(value) {
+    this.value_ = value;
+    this.hasValue_ = true;
+  },
+  "getValue", function PropertyDescriptor_GetValue() {
+    return this.value_;
+  },
+  "hasValue", function PropertyDescriptor_HasValue() {
+    return this.hasValue_;
+  },
+  "setEnumerable", function PropertyDescriptor_SetEnumerable(enumerable) {
+    this.enumerable_ = enumerable;
+      this.hasEnumerable_ = true;
+  },
+  "isEnumerable", function PropertyDescriptor_IsEnumerable() {
+    return this.enumerable_;
+  },
+  "hasEnumerable", function PropertyDescriptor_HasEnumerable() {
+    return this.hasEnumerable_;
+  },
+  "setWritable", function PropertyDescriptor_SetWritable(writable) {
+    this.writable_ = writable;
+    this.hasWritable_ = true;
+  },
+  "isWritable", function PropertyDescriptor_IsWritable() {
+    return this.writable_;
+  },
+  "hasWritable", function PropertyDescriptor_HasWritable() {
+    return this.hasWritable_;
+  },
+  "setConfigurable",
+  function PropertyDescriptor_SetConfigurable(configurable) {
+    this.configurable_ = configurable;
+    this.hasConfigurable_ = true;
+  },
+  "hasConfigurable", function PropertyDescriptor_HasConfigurable() {
+    return this.hasConfigurable_;
+  },
+  "isConfigurable", function PropertyDescriptor_IsConfigurable() {
+    return this.configurable_;
+  },
+  "setGet", function PropertyDescriptor_SetGetter(get) {
+    this.get_ = get;
+    this.hasGetter_ = true;
+  },
+  "getGet", function PropertyDescriptor_GetGetter() {
+    return this.get_;
+  },
+  "hasGetter", function PropertyDescriptor_HasGetter() {
+    return this.hasGetter_;
+  },
+  "setSet", function PropertyDescriptor_SetSetter(set) {
+    this.set_ = set;
+    this.hasSetter_ = true;
+  },
+  "getSet", function PropertyDescriptor_GetSetter() {
+    return this.set_;
+  },
+  "hasSetter", function PropertyDescriptor_HasSetter() {
+    return this.hasSetter_;
+  }
+]);
+
+
+// Converts an array returned from Runtime_GetOwnProperty to an actual
+// property descriptor. For a description of the array layout please
+// see the runtime.cc file.
+function ConvertDescriptorArrayToDescriptor(desc_array) {
+  if (IS_UNDEFINED(desc_array)) {
+    return UNDEFINED;
+  }
+
+  var desc = new PropertyDescriptor();
+  // This is an accessor.
+  if (desc_array[IS_ACCESSOR_INDEX]) {
+    desc.setGet(desc_array[GETTER_INDEX]);
+    desc.setSet(desc_array[SETTER_INDEX]);
+  } else {
+    desc.setValue(desc_array[VALUE_INDEX]);
+    desc.setWritable(desc_array[WRITABLE_INDEX]);
+  }
+  desc.setEnumerable(desc_array[ENUMERABLE_INDEX]);
+  desc.setConfigurable(desc_array[CONFIGURABLE_INDEX]);
+
+  return desc;
+}
+
+
+// For Harmony proxies.
+function GetTrap(handler, name, defaultTrap) {
+  var trap = handler[name];
+  if (IS_UNDEFINED(trap)) {
+    if (IS_UNDEFINED(defaultTrap)) {
+      throw MakeTypeError(kIllegalInvocation);
+    }
+    trap = defaultTrap;
+  } else if (!IS_CALLABLE(trap)) {
+    throw MakeTypeError(kIllegalInvocation);
+  }
+  return trap;
+}
+
+
+function CallTrap1(handler, name, defaultTrap, x) {
+  return %_Call(GetTrap(handler, name, defaultTrap), handler, x);
+}
+
+
+function CallTrap2(handler, name, defaultTrap, x, y) {
+  return %_Call(GetTrap(handler, name, defaultTrap), handler, x, y);
+}
+
+
+// ES5 section 8.12.1.
+// TODO(jkummerow): Deprecated. Migrate all callers to
+// ObjectGetOwnPropertyDescriptor and delete this.
+function GetOwnPropertyJS(obj, v) {
+  var p = TO_NAME(v);
+  if (IS_PROXY(obj)) {
+    // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+    if (IS_SYMBOL(v)) return UNDEFINED;
+
+    var handler = %JSProxyGetHandler(obj);
+    var descriptor = CallTrap1(
+                         handler, "getOwnPropertyDescriptor", UNDEFINED, p);
+    if (IS_UNDEFINED(descriptor)) return descriptor;
+    var desc = ToCompletePropertyDescriptor(descriptor);
+    if (!desc.isConfigurable()) {
+      throw MakeTypeError(kIllegalInvocation);
+    }
+    return desc;
+  }
+
+  // GetOwnProperty returns an array indexed by the constants
+  // defined in macros.py.
+  // If p is not a property on obj undefined is returned.
+  var props = %GetOwnProperty_Legacy(TO_OBJECT(obj), p);
+
+  return ConvertDescriptorArrayToDescriptor(props);
+}
+
+
+// ES6 7.3.9
+function GetMethod(obj, p) {
+  var func = obj[p];
+  if (IS_NULL_OR_UNDEFINED(func)) return UNDEFINED;
+  if (IS_CALLABLE(func)) return func;
+  throw MakeTypeError(kCalledNonCallable, typeof func);
+}
+
+
+// Harmony proxies.
+function DefineProxyProperty(obj, p, attributes, should_throw) {
+  // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+  if (IS_SYMBOL(p)) return false;
+
+  var handler = %JSProxyGetHandler(obj);
+  var result = CallTrap2(handler, "defineProperty", UNDEFINED, p, attributes);
+  if (!result) {
+    if (should_throw) {
+      throw MakeTypeError(kIllegalInvocation);
+    } else {
+      return false;
+    }
+  }
+  return true;
+}
+
+
+// ES6 9.1.6 [[DefineOwnProperty]](P, Desc)
+function DefineObjectProperty(obj, p, desc, should_throw) {
+  var current_array = %GetOwnProperty_Legacy(obj, TO_NAME(p));
+  var current = ConvertDescriptorArrayToDescriptor(current_array);
+  var extensible = %object_is_extensible(obj);
+
+  if (IS_UNDEFINED(current) && !extensible) {
+    if (should_throw) {
+      throw MakeTypeError(kDefineDisallowed, p);
+    } else {
+      return false;
+    }
+  }
+
+  if (!IS_UNDEFINED(current)) {
+    if ((IsGenericDescriptor(desc) ||
+         IsDataDescriptor(desc) == IsDataDescriptor(current)) &&
+        (!desc.hasEnumerable() ||
+         SameValue(desc.isEnumerable(), current.isEnumerable())) &&
+        (!desc.hasConfigurable() ||
+         SameValue(desc.isConfigurable(), current.isConfigurable())) &&
+        (!desc.hasWritable() ||
+         SameValue(desc.isWritable(), current.isWritable())) &&
+        (!desc.hasValue() ||
+         SameValue(desc.getValue(), current.getValue())) &&
+        (!desc.hasGetter() ||
+         SameValue(desc.getGet(), current.getGet())) &&
+        (!desc.hasSetter() ||
+         SameValue(desc.getSet(), current.getSet()))) {
+      return true;
+    }
+    if (!current.isConfigurable()) {
+      // Step 7
+      if (desc.isConfigurable() ||
+          (desc.hasEnumerable() &&
+           desc.isEnumerable() != current.isEnumerable())) {
+        if (should_throw) {
+          throw MakeTypeError(kRedefineDisallowed, p);
+        } else {
+          return false;
+        }
+      }
+      // Step 8
+      if (!IsGenericDescriptor(desc)) {
+        // Step 9a
+        if (IsDataDescriptor(current) != IsDataDescriptor(desc)) {
+          if (should_throw) {
+            throw MakeTypeError(kRedefineDisallowed, p);
+          } else {
+            return false;
+          }
+        }
+        // Step 10a
+        if (IsDataDescriptor(current) && IsDataDescriptor(desc)) {
+          var currentIsWritable = current.isWritable();
+          if (currentIsWritable != desc.isWritable()) {
+            if (!currentIsWritable || IS_STRONG(obj)) {
+              if (should_throw) {
+                throw currentIsWritable
+                    ? MakeTypeError(kStrongRedefineDisallowed, obj, p)
+                    : MakeTypeError(kRedefineDisallowed, p);
+              } else {
+                return false;
+              }
+            }
+          }
+          if (!currentIsWritable && desc.hasValue() &&
+              !SameValue(desc.getValue(), current.getValue())) {
+            if (should_throw) {
+              throw MakeTypeError(kRedefineDisallowed, p);
+            } else {
+              return false;
+            }
+          }
+        }
+        // Step 11
+        if (IsAccessorDescriptor(desc) && IsAccessorDescriptor(current)) {
+          if (desc.hasSetter() &&
+              !SameValue(desc.getSet(), current.getSet())) {
+            if (should_throw) {
+              throw MakeTypeError(kRedefineDisallowed, p);
+            } else {
+              return false;
+            }
+          }
+          if (desc.hasGetter() && !SameValue(desc.getGet(),current.getGet())) {
+            if (should_throw) {
+              throw MakeTypeError(kRedefineDisallowed, p);
+            } else {
+              return false;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // Send flags - enumerable and configurable are common - writable is
+  // only send to the data descriptor.
+  // Take special care if enumerable and configurable is not defined on
+  // desc (we need to preserve the existing values from current).
+  var flag = NONE;
+  if (desc.hasEnumerable()) {
+    flag |= desc.isEnumerable() ? 0 : DONT_ENUM;
+  } else if (!IS_UNDEFINED(current)) {
+    flag |= current.isEnumerable() ? 0 : DONT_ENUM;
+  } else {
+    flag |= DONT_ENUM;
+  }
+
+  if (desc.hasConfigurable()) {
+    flag |= desc.isConfigurable() ? 0 : DONT_DELETE;
+  } else if (!IS_UNDEFINED(current)) {
+    flag |= current.isConfigurable() ? 0 : DONT_DELETE;
+  } else
+    flag |= DONT_DELETE;
+
+  if (IsDataDescriptor(desc) ||
+      (IsGenericDescriptor(desc) &&
+       (IS_UNDEFINED(current) || IsDataDescriptor(current)))) {
+    // There are 3 cases that lead here:
+    // Step 4a - defining a new data property.
+    // Steps 9b & 12 - replacing an existing accessor property with a data
+    //                 property.
+    // Step 12 - updating an existing data property with a data or generic
+    //           descriptor.
+
+    if (desc.hasWritable()) {
+      flag |= desc.isWritable() ? 0 : READ_ONLY;
+    } else if (!IS_UNDEFINED(current)) {
+      flag |= current.isWritable() ? 0 : READ_ONLY;
+    } else {
+      flag |= READ_ONLY;
+    }
+
+    var value = UNDEFINED;  // Default value is undefined.
+    if (desc.hasValue()) {
+      value = desc.getValue();
+    } else if (!IS_UNDEFINED(current) && IsDataDescriptor(current)) {
+      value = current.getValue();
+    }
+
+    %DefineDataPropertyUnchecked(obj, p, value, flag);
+  } else {
+    // There are 3 cases that lead here:
+    // Step 4b - defining a new accessor property.
+    // Steps 9c & 12 - replacing an existing data property with an accessor
+    //                 property.
+    // Step 12 - updating an existing accessor property with an accessor
+    //           descriptor.
+    var getter = null;
+    if (desc.hasGetter()) {
+      getter = desc.getGet();
+    } else if (IsAccessorDescriptor(current) && current.hasGetter()) {
+      getter = current.getGet();
+    }
+    var setter = null;
+    if (desc.hasSetter()) {
+      setter = desc.getSet();
+    } else if (IsAccessorDescriptor(current) && current.hasSetter()) {
+      setter = current.getSet();
+    }
+    %DefineAccessorPropertyUnchecked(obj, p, getter, setter, flag);
+  }
+  return true;
+}
+
+
+// ES5 section 15.4.5.1.
+function DefineArrayProperty(obj, p, desc, should_throw) {
+  // Step 3 - Special handling for array index.
+  if (!IS_SYMBOL(p)) {
+    var index = TO_UINT32(p);
+    var emit_splice = false;
+    if (TO_STRING(index) == p && index != 4294967295) {
+      var length = obj.length;
+      if (index >= length && %IsObserved(obj)) {
+        emit_splice = true;
+        ObserveBeginPerformSplice(obj);
+      }
+
+      var length_desc = GetOwnPropertyJS(obj, "length");
+      if ((index >= length && !length_desc.isWritable()) ||
+          !DefineObjectProperty(obj, p, desc, true)) {
+        if (emit_splice)
+          ObserveEndPerformSplice(obj);
+        if (should_throw) {
+          throw MakeTypeError(kDefineDisallowed, p);
+        } else {
+          return false;
+        }
+      }
+      if (index >= length) {
+        obj.length = index + 1;
+      }
+      if (emit_splice) {
+        ObserveEndPerformSplice(obj);
+        ObserveEnqueueSpliceRecord(obj, length, [], index + 1 - length);
+      }
+      return true;
+    }
+  }
+
+  // Step 5 - Fallback to default implementation.
+  return DefineObjectProperty(obj, p, desc, should_throw);
+}
+
+
+// ES5 section 8.12.9, ES5 section 15.4.5.1 and Harmony proxies.
+function DefineOwnProperty(obj, p, desc, should_throw) {
+  if (IS_PROXY(obj)) {
+    // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+    if (IS_SYMBOL(p)) return false;
+
+    var attributes = FromGenericPropertyDescriptor(desc);
+    return DefineProxyProperty(obj, p, attributes, should_throw);
+  } else if (IS_ARRAY(obj)) {
+    return DefineArrayProperty(obj, p, desc, should_throw);
+  } else {
+    return DefineObjectProperty(obj, p, desc, should_throw);
+  }
+}
+
+
+// ES6 section 19.1.2.9
+function ObjectGetPrototypeOf(obj) {
+  return %_GetPrototype(TO_OBJECT(obj));
+}
+
+// ES6 section 19.1.2.18.
+function ObjectSetPrototypeOf(obj, proto) {
+  CHECK_OBJECT_COERCIBLE(obj, "Object.setPrototypeOf");
+
+  if (proto !== null && !IS_RECEIVER(proto)) {
+    throw MakeTypeError(kProtoObjectOrNull, proto);
+  }
+
+  if (IS_RECEIVER(obj)) {
+    %SetPrototype(obj, proto);
+  }
+
+  return obj;
+}
+
+
+// ES6 section 19.1.2.6
+function ObjectGetOwnPropertyDescriptor(obj, p) {
+  return %GetOwnProperty(obj, p);
+}
+
+
+// ES5 section 15.2.3.4.
+function ObjectGetOwnPropertyNames(obj) {
+  obj = TO_OBJECT(obj);
+  return %GetOwnPropertyKeys(obj, PROPERTY_FILTER_SKIP_SYMBOLS);
+}
+
+
+// ES5 section 15.2.3.6.
+function ObjectDefineProperty(obj, p, attributes) {
+  // The new pure-C++ implementation doesn't support O.o.
+  // TODO(jkummerow): Implement missing features and remove fallback path.
+  if (%IsObserved(obj)) {
+    if (!IS_RECEIVER(obj)) {
+      throw MakeTypeError(kCalledOnNonObject, "Object.defineProperty");
+    }
+    var name = TO_NAME(p);
+    var desc = ToPropertyDescriptor(attributes);
+    DefineOwnProperty(obj, name, desc, true);
+    return obj;
+  }
+  return %ObjectDefineProperty(obj, p, attributes);
+}
+
+
+function GetOwnEnumerablePropertyNames(object) {
+  return %GetOwnPropertyKeys(object, PROPERTY_FILTER_ONLY_ENUMERABLE);
+}
+
+
+// ES5 section 15.2.3.7.
+function ObjectDefineProperties(obj, properties) {
+  // The new pure-C++ implementation doesn't support O.o.
+  // TODO(jkummerow): Implement missing features and remove fallback path.
+  if (%IsObserved(obj)) {
+    if (!IS_RECEIVER(obj)) {
+      throw MakeTypeError(kCalledOnNonObject, "Object.defineProperties");
+    }
+    var props = TO_OBJECT(properties);
+    var names = GetOwnEnumerablePropertyNames(props);
+    var descriptors = new InternalArray();
+    for (var i = 0; i < names.length; i++) {
+      descriptors.push(ToPropertyDescriptor(props[names[i]]));
+    }
+    for (var i = 0; i < names.length; i++) {
+      DefineOwnProperty(obj, names[i], descriptors[i], true);
+    }
+    return obj;
+  }
+  return %ObjectDefineProperties(obj, properties);
+}
+
+
+// ES6 B.2.2.1.1
+function ObjectGetProto() {
+  return %_GetPrototype(TO_OBJECT(this));
+}
+
+
+// ES6 B.2.2.1.2
+function ObjectSetProto(proto) {
+  CHECK_OBJECT_COERCIBLE(this, "Object.prototype.__proto__");
+
+  if ((IS_RECEIVER(proto) || IS_NULL(proto)) && IS_RECEIVER(this)) {
+    %SetPrototype(this, proto);
+  }
+}
+
+
+// ES6 19.1.1.1
+function ObjectConstructor(x) {
+  if (GlobalObject != new.target && !IS_UNDEFINED(new.target)) {
+    return this;
+  }
+  if (IS_NULL(x) || IS_UNDEFINED(x)) return {};
+  return TO_OBJECT(x);
+}
+
+
+// ----------------------------------------------------------------------------
+// Object
+
+%SetNativeFlag(GlobalObject);
+%SetCode(GlobalObject, ObjectConstructor);
+
+%AddNamedProperty(GlobalObject.prototype, "constructor", GlobalObject,
+                  DONT_ENUM);
+
+// Set up non-enumerable functions on the Object.prototype object.
+utils.InstallFunctions(GlobalObject.prototype, DONT_ENUM, [
+  "toString", ObjectToString,
+  "toLocaleString", ObjectToLocaleString,
+  "valueOf", ObjectValueOf,
+  "hasOwnProperty", ObjectHasOwnProperty,
+  "isPrototypeOf", ObjectIsPrototypeOf,
+  "propertyIsEnumerable", ObjectPropertyIsEnumerable,
+  "__defineGetter__", ObjectDefineGetter,
+  "__lookupGetter__", ObjectLookupGetter,
+  "__defineSetter__", ObjectDefineSetter,
+  "__lookupSetter__", ObjectLookupSetter
+]);
+utils.InstallGetterSetter(GlobalObject.prototype, "__proto__", ObjectGetProto,
+                    ObjectSetProto);
+
+// Set up non-enumerable functions in the Object object.
+utils.InstallFunctions(GlobalObject, DONT_ENUM, [
+  // assign is added in bootstrapper.cc.
+  // keys is added in bootstrapper.cc.
+  "defineProperty", ObjectDefineProperty,
+  "defineProperties", ObjectDefineProperties,
+  "getPrototypeOf", ObjectGetPrototypeOf,
+  "setPrototypeOf", ObjectSetPrototypeOf,
+  "getOwnPropertyDescriptor", ObjectGetOwnPropertyDescriptor,
+  "getOwnPropertyNames", ObjectGetOwnPropertyNames,
+  // getOwnPropertySymbols is added in symbol.js.
+  "is", SameValue,  // ECMA-262, Edition 6, section 19.1.2.10
+  // deliverChangeRecords, getNotifier, observe and unobserve are added
+  // in object-observe.js.
+]);
+
+
+// ----------------------------------------------------------------------------
+// Boolean
+
+function BooleanConstructor(x) {
+  // TODO(bmeurer): Move this to toplevel.
+  "use strict";
+  if (!IS_UNDEFINED(new.target)) {
+    %_SetValueOf(this, TO_BOOLEAN(x));
+  } else {
+    return TO_BOOLEAN(x);
+  }
+}
+
+
+function BooleanToString() {
+  // NOTE: Both Boolean objects and values can enter here as
+  // 'this'. This is not as dictated by ECMA-262.
+  var b = this;
+  if (!IS_BOOLEAN(b)) {
+    if (!IS_BOOLEAN_WRAPPER(b)) {
+      throw MakeTypeError(kNotGeneric, 'Boolean.prototype.toString');
+    }
+    b = %_ValueOf(b);
+  }
+  return b ? 'true' : 'false';
+}
+
+
+function BooleanValueOf() {
+  // NOTE: Both Boolean objects and values can enter here as
+  // 'this'. This is not as dictated by ECMA-262.
+  if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this)) {
+    throw MakeTypeError(kNotGeneric, 'Boolean.prototype.valueOf');
+  }
+  return %_ValueOf(this);
+}
+
+
+// ----------------------------------------------------------------------------
+
+%SetCode(GlobalBoolean, BooleanConstructor);
+%FunctionSetPrototype(GlobalBoolean, new GlobalBoolean(false));
+%AddNamedProperty(GlobalBoolean.prototype, "constructor", GlobalBoolean,
+                  DONT_ENUM);
+
+utils.InstallFunctions(GlobalBoolean.prototype, DONT_ENUM, [
+  "toString", BooleanToString,
+  "valueOf", BooleanValueOf
+]);
+
+
+// ----------------------------------------------------------------------------
+// Number
+
+// ES6 Number.prototype.toString([ radix ])
+function NumberToStringJS(radix) {
+  // NOTE: Both Number objects and values can enter here as
+  // 'this'. This is not as dictated by ECMA-262.
+  var number = this;
+  if (!IS_NUMBER(this)) {
+    if (!IS_NUMBER_WRAPPER(this)) {
+      throw MakeTypeError(kNotGeneric, 'Number.prototype.toString');
+    }
+    // Get the value of this number in case it's an object.
+    number = %_ValueOf(this);
+  }
+  // Fast case: Convert number in radix 10.
+  if (IS_UNDEFINED(radix) || radix === 10) {
+    return %_NumberToString(number);
+  }
+
+  // Convert the radix to an integer and check the range.
+  radix = TO_INTEGER(radix);
+  if (radix < 2 || radix > 36) throw MakeRangeError(kToRadixFormatRange);
+  // Convert the number to a string in the given radix.
+  return %NumberToRadixString(number, radix);
+}
+
+
+// ES6 20.1.3.4 Number.prototype.toLocaleString([reserved1 [, reserved2]])
+function NumberToLocaleString() {
+  return %_Call(NumberToStringJS, this);
+}
+
+
+// ES6 20.1.3.7 Number.prototype.valueOf()
+function NumberValueOf() {
+  // NOTE: Both Number objects and values can enter here as
+  // 'this'. This is not as dictated by ECMA-262.
+  if (!IS_NUMBER(this) && !IS_NUMBER_WRAPPER(this)) {
+    throw MakeTypeError(kNotGeneric, 'Number.prototype.valueOf');
+  }
+  return %_ValueOf(this);
+}
+
+
+// ES6 20.1.3.3 Number.prototype.toFixed(fractionDigits)
+function NumberToFixedJS(fractionDigits) {
+  var x = this;
+  if (!IS_NUMBER(this)) {
+    if (!IS_NUMBER_WRAPPER(this)) {
+      throw MakeTypeError(kIncompatibleMethodReceiver,
+                          "Number.prototype.toFixed", this);
+    }
+    // Get the value of this number in case it's an object.
+    x = %_ValueOf(this);
+  }
+  var f = TO_INTEGER(fractionDigits);
+
+  if (f < 0 || f > 20) {
+    throw MakeRangeError(kNumberFormatRange, "toFixed() digits");
+  }
+
+  if (NUMBER_IS_NAN(x)) return "NaN";
+  if (x == INFINITY) return "Infinity";
+  if (x == -INFINITY) return "-Infinity";
+
+  return %NumberToFixed(x, f);
+}
+
+
+// ES6 20.1.3.2 Number.prototype.toExponential(fractionDigits)
+function NumberToExponentialJS(fractionDigits) {
+  var x = this;
+  if (!IS_NUMBER(this)) {
+    if (!IS_NUMBER_WRAPPER(this)) {
+      throw MakeTypeError(kIncompatibleMethodReceiver,
+                          "Number.prototype.toExponential", this);
+    }
+    // Get the value of this number in case it's an object.
+    x = %_ValueOf(this);
+  }
+  var f = IS_UNDEFINED(fractionDigits) ? UNDEFINED : TO_INTEGER(fractionDigits);
+
+  if (NUMBER_IS_NAN(x)) return "NaN";
+  if (x == INFINITY) return "Infinity";
+  if (x == -INFINITY) return "-Infinity";
+
+  if (IS_UNDEFINED(f)) {
+    f = -1;  // Signal for runtime function that f is not defined.
+  } else if (f < 0 || f > 20) {
+    throw MakeRangeError(kNumberFormatRange, "toExponential()");
+  }
+  return %NumberToExponential(x, f);
+}
+
+
+// ES6 20.1.3.5 Number.prototype.toPrecision(precision)
+function NumberToPrecisionJS(precision) {
+  var x = this;
+  if (!IS_NUMBER(this)) {
+    if (!IS_NUMBER_WRAPPER(this)) {
+      throw MakeTypeError(kIncompatibleMethodReceiver,
+                          "Number.prototype.toPrecision", this);
+    }
+    // Get the value of this number in case it's an object.
+    x = %_ValueOf(this);
+  }
+  if (IS_UNDEFINED(precision)) return TO_STRING(x);
+  var p = TO_INTEGER(precision);
+
+  if (NUMBER_IS_NAN(x)) return "NaN";
+  if (x == INFINITY) return "Infinity";
+  if (x == -INFINITY) return "-Infinity";
+
+  if (p < 1 || p > 21) {
+    throw MakeRangeError(kToPrecisionFormatRange);
+  }
+  return %NumberToPrecision(x, p);
+}
+
+
+// Harmony isFinite.
+function NumberIsFinite(number) {
+  return IS_NUMBER(number) && NUMBER_IS_FINITE(number);
+}
+
+
+// Harmony isInteger
+function NumberIsInteger(number) {
+  return NumberIsFinite(number) && TO_INTEGER(number) == number;
+}
+
+
+// Harmony isNaN.
+function NumberIsNaN(number) {
+  return IS_NUMBER(number) && NUMBER_IS_NAN(number);
+}
+
+
+// Harmony isSafeInteger
+function NumberIsSafeInteger(number) {
+  if (NumberIsFinite(number)) {
+    var integral = TO_INTEGER(number);
+    if (integral == number) {
+      return MathAbs(integral) <= kMaxSafeInteger;
+    }
+  }
+  return false;
+}
+
+
+// ----------------------------------------------------------------------------
+
+%FunctionSetPrototype(GlobalNumber, new GlobalNumber(0));
+
+%OptimizeObjectForAddingMultipleProperties(GlobalNumber.prototype, 8);
+// Set up the constructor property on the Number prototype object.
+%AddNamedProperty(GlobalNumber.prototype, "constructor", GlobalNumber,
+                  DONT_ENUM);
+
+utils.InstallConstants(GlobalNumber, [
+  // ECMA-262 section 15.7.3.1.
+  "MAX_VALUE", 1.7976931348623157e+308,
+  // ECMA-262 section 15.7.3.2.
+  "MIN_VALUE", 5e-324,
+  // ECMA-262 section 15.7.3.3.
+  "NaN", NaN,
+  // ECMA-262 section 15.7.3.4.
+  "NEGATIVE_INFINITY", -INFINITY,
+  // ECMA-262 section 15.7.3.5.
+  "POSITIVE_INFINITY", INFINITY,
+
+  // --- Harmony constants (no spec refs until settled.)
+
+  "MAX_SAFE_INTEGER", %_MathPow(2, 53) - 1,
+  "MIN_SAFE_INTEGER", -%_MathPow(2, 53) + 1,
+  "EPSILON", %_MathPow(2, -52)
+]);
+
+// Set up non-enumerable functions on the Number prototype object.
+utils.InstallFunctions(GlobalNumber.prototype, DONT_ENUM, [
+  "toString", NumberToStringJS,
+  "toLocaleString", NumberToLocaleString,
+  "valueOf", NumberValueOf,
+  "toFixed", NumberToFixedJS,
+  "toExponential", NumberToExponentialJS,
+  "toPrecision", NumberToPrecisionJS
+]);
+
+// Harmony Number constructor additions
+utils.InstallFunctions(GlobalNumber, DONT_ENUM, [
+  "isFinite", NumberIsFinite,
+  "isInteger", NumberIsInteger,
+  "isNaN", NumberIsNaN,
+  "isSafeInteger", NumberIsSafeInteger,
+  "parseInt", GlobalParseInt,
+  "parseFloat", GlobalParseFloat
+]);
+
+%SetForceInlineFlag(NumberIsNaN);
+
+
+// ----------------------------------------------------------------------------
+// Iterator related spec functions.
+
+// ES6 7.4.1 GetIterator(obj, method)
+function GetIterator(obj, method) {
+  if (IS_UNDEFINED(method)) {
+    method = obj[iteratorSymbol];
+  }
+  if (!IS_CALLABLE(method)) {
+    throw MakeTypeError(kNotIterable, obj);
+  }
+  var iterator = %_Call(method, obj);
+  if (!IS_RECEIVER(iterator)) {
+    throw MakeTypeError(kNotAnIterator, iterator);
+  }
+  return iterator;
+}
+
+// ----------------------------------------------------------------------------
+// Exports
+
+utils.Export(function(to) {
+  to.GetIterator = GetIterator;
+  to.GetMethod = GetMethod;
+  to.IsFinite = GlobalIsFinite;
+  to.IsNaN = GlobalIsNaN;
+  to.NumberIsNaN = NumberIsNaN;
+  to.ObjectDefineProperties = ObjectDefineProperties;
+  to.ObjectDefineProperty = ObjectDefineProperty;
+  to.ObjectHasOwnProperty = ObjectHasOwnProperty;
+});
+
+%InstallToContext([
+  "object_value_of", ObjectValueOf,
+]);
+
+})
diff --git a/src/js/weak-collection.js b/src/js/weak-collection.js
new file mode 100644
index 0000000..308b9ed
--- /dev/null
+++ b/src/js/weak-collection.js
@@ -0,0 +1,190 @@
+// Copyright 2012 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.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GetExistingHash;
+var GetHash;
+var GlobalObject = global.Object;
+var GlobalWeakMap = global.WeakMap;
+var GlobalWeakSet = global.WeakSet;
+var MakeTypeError;
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+
+utils.Import(function(from) {
+  GetExistingHash = from.GetExistingHash;
+  GetHash = from.GetHash;
+  MakeTypeError = from.MakeTypeError;
+});
+
+// -------------------------------------------------------------------
+// Harmony WeakMap
+
+function WeakMapConstructor(iterable) {
+  if (IS_UNDEFINED(new.target)) {
+    throw MakeTypeError(kConstructorNotFunction, "WeakMap");
+  }
+
+  %WeakCollectionInitialize(this);
+
+  if (!IS_NULL_OR_UNDEFINED(iterable)) {
+    var adder = this.set;
+    if (!IS_CALLABLE(adder)) {
+      throw MakeTypeError(kPropertyNotFunction, adder, 'set', this);
+    }
+    for (var nextItem of iterable) {
+      if (!IS_RECEIVER(nextItem)) {
+        throw MakeTypeError(kIteratorValueNotAnObject, nextItem);
+      }
+      %_Call(adder, this, nextItem[0], nextItem[1]);
+    }
+  }
+}
+
+
+function WeakMapGet(key) {
+  if (!IS_WEAKMAP(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'WeakMap.prototype.get', this);
+  }
+  if (!IS_RECEIVER(key)) return UNDEFINED;
+  var hash = GetExistingHash(key);
+  if (IS_UNDEFINED(hash)) return UNDEFINED;
+  return %WeakCollectionGet(this, key, hash);
+}
+
+
+function WeakMapSet(key, value) {
+  if (!IS_WEAKMAP(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'WeakMap.prototype.set', this);
+  }
+  if (!IS_RECEIVER(key)) throw MakeTypeError(kInvalidWeakMapKey);
+  return %WeakCollectionSet(this, key, value, GetHash(key));
+}
+
+
+function WeakMapHas(key) {
+  if (!IS_WEAKMAP(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'WeakMap.prototype.has', this);
+  }
+  if (!IS_RECEIVER(key)) return false;
+  var hash = GetExistingHash(key);
+  if (IS_UNDEFINED(hash)) return false;
+  return %WeakCollectionHas(this, key, hash);
+}
+
+
+function WeakMapDelete(key) {
+  if (!IS_WEAKMAP(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'WeakMap.prototype.delete', this);
+  }
+  if (!IS_RECEIVER(key)) return false;
+  var hash = GetExistingHash(key);
+  if (IS_UNDEFINED(hash)) return false;
+  return %WeakCollectionDelete(this, key, hash);
+}
+
+
+// -------------------------------------------------------------------
+
+%SetCode(GlobalWeakMap, WeakMapConstructor);
+%FunctionSetLength(GlobalWeakMap, 0);
+%FunctionSetPrototype(GlobalWeakMap, new GlobalObject());
+%AddNamedProperty(GlobalWeakMap.prototype, "constructor", GlobalWeakMap,
+                  DONT_ENUM);
+%AddNamedProperty(GlobalWeakMap.prototype, toStringTagSymbol, "WeakMap",
+                  DONT_ENUM | READ_ONLY);
+
+// Set up the non-enumerable functions on the WeakMap prototype object.
+utils.InstallFunctions(GlobalWeakMap.prototype, DONT_ENUM, [
+  "get", WeakMapGet,
+  "set", WeakMapSet,
+  "has", WeakMapHas,
+  "delete", WeakMapDelete
+]);
+
+// -------------------------------------------------------------------
+// Harmony WeakSet
+
+function WeakSetConstructor(iterable) {
+  if (IS_UNDEFINED(new.target)) {
+    throw MakeTypeError(kConstructorNotFunction, "WeakSet");
+  }
+
+  %WeakCollectionInitialize(this);
+
+  if (!IS_NULL_OR_UNDEFINED(iterable)) {
+    var adder = this.add;
+    if (!IS_CALLABLE(adder)) {
+      throw MakeTypeError(kPropertyNotFunction, adder, 'add', this);
+    }
+    for (var value of iterable) {
+      %_Call(adder, this, value);
+    }
+  }
+}
+
+
+function WeakSetAdd(value) {
+  if (!IS_WEAKSET(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'WeakSet.prototype.add', this);
+  }
+  if (!IS_RECEIVER(value)) throw MakeTypeError(kInvalidWeakSetValue);
+  return %WeakCollectionSet(this, value, true, GetHash(value));
+}
+
+
+function WeakSetHas(value) {
+  if (!IS_WEAKSET(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'WeakSet.prototype.has', this);
+  }
+  if (!IS_RECEIVER(value)) return false;
+  var hash = GetExistingHash(value);
+  if (IS_UNDEFINED(hash)) return false;
+  return %WeakCollectionHas(this, value, hash);
+}
+
+
+function WeakSetDelete(value) {
+  if (!IS_WEAKSET(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'WeakSet.prototype.delete', this);
+  }
+  if (!IS_RECEIVER(value)) return false;
+  var hash = GetExistingHash(value);
+  if (IS_UNDEFINED(hash)) return false;
+  return %WeakCollectionDelete(this, value, hash);
+}
+
+
+// -------------------------------------------------------------------
+
+%SetCode(GlobalWeakSet, WeakSetConstructor);
+%FunctionSetLength(GlobalWeakSet, 0);
+%FunctionSetPrototype(GlobalWeakSet, new GlobalObject());
+%AddNamedProperty(GlobalWeakSet.prototype, "constructor", GlobalWeakSet,
+                 DONT_ENUM);
+%AddNamedProperty(GlobalWeakSet.prototype, toStringTagSymbol, "WeakSet",
+                  DONT_ENUM | READ_ONLY);
+
+// Set up the non-enumerable functions on the WeakSet prototype object.
+utils.InstallFunctions(GlobalWeakSet.prototype, DONT_ENUM, [
+  "add", WeakSetAdd,
+  "has", WeakSetHas,
+  "delete", WeakSetDelete
+]);
+
+})