Upgrade V8 to 5.1.281.57  DO NOT MERGE

FPIIM-449

Change-Id: Id981b686b4d587ac31697662eb98bb34be42ad90
(cherry picked from commit 3b9bc31999c9787eb726ecdbfd5796bfdec32a18)
diff --git a/src/js/array-iterator.js b/src/js/array-iterator.js
index 2609ebd..b3e25e9 100644
--- a/src/js/array-iterator.js
+++ b/src/js/array-iterator.js
@@ -109,6 +109,24 @@
   return CreateArrayIterator(this, ITERATOR_KIND_KEYS);
 }
 
+// TODO(littledan): Check for detached TypedArray in these three methods
+function TypedArrayEntries() {
+  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
+  return %_Call(ArrayEntries, this);
+}
+
+
+function TypedArrayValues() {
+  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
+  return %_Call(ArrayValues, this);
+}
+
+
+function TypedArrayKeys() {
+  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
+  return %_Call(ArrayKeys, this);
+}
+
 
 %FunctionSetPrototype(ArrayIterator, {__proto__: IteratorPrototype});
 %FunctionSetInstanceClassName(ArrayIterator, 'Array Iterator');
@@ -117,8 +135,6 @@
   'next', ArrayIteratorNext
 ]);
 utils.SetFunctionName(ArrayIteratorIterator, iteratorSymbol);
-%AddNamedProperty(ArrayIterator.prototype, iteratorSymbol,
-                  ArrayIteratorIterator, DONT_ENUM);
 %AddNamedProperty(ArrayIterator.prototype, toStringTagSymbol,
                   "Array Iterator", READ_ONLY | DONT_ENUM);
 
@@ -135,12 +151,13 @@
 %AddNamedProperty(GlobalArray.prototype, iteratorSymbol, ArrayValues,
                   DONT_ENUM);
 
+utils.InstallFunctions(GlobalTypedArray.prototype, DONT_ENUM, [
+  'entries', TypedArrayEntries,
+  'keys', TypedArrayKeys,
+  'values', TypedArrayValues
+]);
 %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);
+                  iteratorSymbol, TypedArrayValues, DONT_ENUM);
 
 // -------------------------------------------------------------------
 // Exports
diff --git a/src/js/array.js b/src/js/array.js
index 0a5e283..1406df3 100644
--- a/src/js/array.js
+++ b/src/js/array.js
@@ -73,17 +73,13 @@
   }
 }
 
+function KeySortCompare(a, b) {
+  return a - b;
+}
 
-// 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)) {
+    var keys = new InternalArray();
     // It's an interval
     var limit = indices;
     for (var i = 0; i < limit; ++i) {
@@ -92,61 +88,34 @@
         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;
   }
-  return keys;
+  return InnerArraySort(indices, indices.length, KeySortCompare);
 }
 
 
-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++) {
+function SparseJoinWithSeparatorJS(array, keys, length, convert, separator) {
+  var keys_length = keys.length;
+  var elements = new InternalArray(keys_length * 2);
+  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;
-    }
+    var e = array[key];
+    elements[i * 2] = key;
+    elements[i * 2 + 1] = IS_STRING(e) ? e : convert(e);
   }
-  return %SparseJoinWithSeparator(elements, len, separator);
+  return %SparseJoinWithSeparator(elements, length, separator);
 }
 
 
 // Optimized for sparse arrays if separator is ''.
-function SparseJoin(array, len, convert) {
-  var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
-  var last_key = -1;
+function SparseJoin(array, keys, convert) {
   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;
-    }
+    var e = array[keys[i]];
+    elements[i] = IS_STRING(e) ? e : convert(e);
   }
-  return %StringBuilderConcat(elements, elements_length, '');
+  return %StringBuilderConcat(elements, keys_length, '');
 }
 
 
@@ -167,94 +136,122 @@
     (touched > estimated_elements * 4);
 }
 
+function Stack() {
+  this.length = 0;
+  this.values = new InternalArray();
+}
+
+// Predeclare the instance variables on the prototype. Otherwise setting them in
+// the constructor will leak the instance through settings on Object.prototype.
+Stack.prototype.length = null;
+Stack.prototype.values = null;
+
+function StackPush(stack, value) {
+  stack.values[stack.length++] = value;
+}
+
+function StackPop(stack) {
+  stack.values[--stack.length] = null
+}
+
+function StackHas(stack, v) {
+  var length = stack.length;
+  var values = stack.values;
+  for (var i = 0; i < length; i++) {
+    if (values[i] === v) return true;
+  }
+  return false;
+}
+
+// Global list of arrays visited during toString, toLocaleString and
+// join invocations.
+var visited_arrays = new Stack();
+
+function DoJoin(array, length, is_array, separator, convert) {
+  if (UseSparseVariant(array, length, is_array, length)) {
+    %NormalizeElements(array);
+    var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, length));
+    if (separator === '') {
+      if (keys.length === 0) return '';
+      return SparseJoin(array, keys, convert);
+    } else {
+      return SparseJoinWithSeparatorJS(array, keys, length, convert, separator);
+    }
+  }
+
+  // Fast case for one-element arrays.
+  if (length === 1) {
+    var e = array[0];
+    return IS_STRING(e) ? e : 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 === '') {
+    for (var i = 0; i < length; i++) {
+      var e = array[i];
+      elements[i] = IS_STRING(e) ? e : convert(e);
+    }
+    return %StringBuilderConcat(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.
+  var e = array[0];
+  if (IS_NUMBER(e)) {
+    elements[0] = %_NumberToString(e);
+    for (var i = 1; i < length; i++) {
+      e = array[i];
+      if (IS_NUMBER(e)) {
+        elements[i] = %_NumberToString(e);
+      } else {
+        elements[i] = IS_STRING(e) ? e : convert(e);
+      }
+    }
+  } else {
+    elements[0] = IS_STRING(e) ? e : convert(e);
+    for (var i = 1; i < length; i++) {
+      e = array[i];
+      elements[i] = IS_STRING(e) ? e : convert(e);
+    }
+  }
+  return %StringBuilderJoin(elements, length, separator);
+}
 
 function Join(array, length, separator, convert) {
-  if (length == 0) return '';
+  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 '';
+    if (StackHas(visited_arrays, array)) return '';
+    StackPush(visited_arrays, array);
   }
 
   // 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;
-      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;
-      }
-    }
-    return %StringBuilderJoin(elements, length, separator);
+    return DoJoin(array, length, is_array, separator, convert);
   } 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;
+    if (is_array) StackPop(visited_arrays);
   }
 }
 
 
 function ConvertToString(x) {
-  if (IS_NULL_OR_UNDEFINED(x)) {
-    return '';
-  } else {
-    return TO_STRING(x);
-  }
+  if (IS_NULL_OR_UNDEFINED(x)) return '';
+  return TO_STRING(x);
 }
 
 
 function ConvertToLocaleString(e) {
-  if (IS_NULL_OR_UNDEFINED(e)) {
-    return '';
-  } else {
-    return TO_STRING(e.toLocaleString());
-  }
+  if (IS_NULL_OR_UNDEFINED(e)) return '';
+  return TO_STRING(e.toLocaleString());
 }
 
 
@@ -275,12 +272,10 @@
     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);
-          }
+      if (key >= start_i) {
+        var current = array[key];
+        if (!IS_UNDEFINED(current) || key in array) {
+          DefineIndexedProperty(deleted_elements, key - start_i, current);
         }
       }
     }
@@ -317,21 +312,19 @@
     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);
-            }
+      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);
           }
         }
       }
@@ -1069,8 +1062,7 @@
       } 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)) {
+          if (!HAS_OWN_PROPERTY(obj, index) && HAS_OWN_PROPERTY(proto, index)) {
             obj[index] = proto[index];
             if (index >= max) { max = index + 1; }
           }
@@ -1097,8 +1089,7 @@
       } else {
         for (var i = 0; i < indices.length; i++) {
           var index = indices[i];
-          if (!IS_UNDEFINED(index) && from <= index &&
-              HAS_OWN_PROPERTY(proto, index)) {
+          if (from <= index && HAS_OWN_PROPERTY(proto, index)) {
             obj[index] = UNDEFINED;
           }
         }
@@ -1247,10 +1238,19 @@
   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);
+  if (IS_UNDEFINED(receiver)) {
+    for (var i = 0; i < length; i++) {
+      if (HAS_INDEX(array, i, is_array)) {
+        var element = array[i];
+        f(element, i, array);
+      }
+    }
+  } else {
+    for (var i = 0; i < length; i++) {
+      if (HAS_INDEX(array, i, is_array)) {
+        var element = array[i];
+        %_Call(f, receiver, element, i, array);
+      }
     }
   }
 }
@@ -1347,7 +1347,7 @@
   if (IS_UNDEFINED(index)) {
     index = 0;
   } else {
-    index = TO_INTEGER(index);
+    index = TO_INTEGER(index) + 0;  // Add 0 to convert -0 to 0
     // If index is negative, index from the end of the array.
     if (index < 0) {
       index = length + index;
@@ -1373,7 +1373,7 @@
       while (i < n && sortedKeys[i] < index) i++;
       while (i < n) {
         var key = sortedKeys[i];
-        if (!IS_UNDEFINED(key) && array[key] === element) return key;
+        if (array[key] === element) return key;
         i++;
       }
       return -1;
@@ -1409,7 +1409,7 @@
   if (argumentsLength < 2) {
     index = length - 1;
   } else {
-    index = TO_INTEGER(index);
+    index = TO_INTEGER(index) + 0;  // Add 0 to convert -0 to 0
     // 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.
@@ -1432,7 +1432,7 @@
       var i = sortedKeys.length - 1;
       while (i >= 0) {
         var key = sortedKeys[i];
-        if (!IS_UNDEFINED(key) && array[key] === element) return key;
+        if (array[key] === element) return key;
         i--;
       }
       return -1;
@@ -1946,6 +1946,10 @@
   to.InnerArraySort = InnerArraySort;
   to.InnerArrayToLocaleString = InnerArrayToLocaleString;
   to.PackedArrayReverse = PackedArrayReverse;
+  to.Stack = Stack;
+  to.StackHas = StackHas;
+  to.StackPush = StackPush;
+  to.StackPop = StackPop;
 });
 
 %InstallToContext([
diff --git a/src/js/harmony-atomics.js b/src/js/harmony-atomics.js
index b861a2a..9f80227 100644
--- a/src/js/harmony-atomics.js
+++ b/src/js/harmony-atomics.js
@@ -12,12 +12,14 @@
 // Imports
 
 var GlobalObject = global.Object;
+var MakeRangeError;
 var MakeTypeError;
 var MaxSimple;
 var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
 
 utils.Import(function(from) {
   MakeTypeError = from.MakeTypeError;
+  MakeRangeError = from.MakeRangeError;
   MaxSimple = from.MaxSimple;
 });
 
@@ -37,14 +39,24 @@
   }
 }
 
+// https://tc39.github.io/ecmascript_sharedmem/shmem.html#Atomics.ValidateAtomicAccess
+function ValidateIndex(index, length) {
+  var numberIndex = TO_NUMBER(index);
+  var accessIndex = TO_INTEGER(numberIndex);
+  if (numberIndex !== accessIndex) {
+    throw MakeRangeError(kInvalidAtomicAccessIndex);
+  }
+  if (accessIndex < 0 || accessIndex >= length) {
+    throw MakeRangeError(kInvalidAtomicAccessIndex);
+  }
+  return accessIndex;
+}
+
 //-------------------------------------------------------------------
 
 function AtomicsCompareExchangeJS(sta, index, oldValue, newValue) {
   CheckSharedIntegerTypedArray(sta);
-  index = TO_INTEGER(index);
-  if (index < 0 || index >= %_TypedArrayGetLength(sta)) {
-    return UNDEFINED;
-  }
+  index = ValidateIndex(index, %_TypedArrayGetLength(sta));
   oldValue = TO_NUMBER(oldValue);
   newValue = TO_NUMBER(newValue);
   return %_AtomicsCompareExchange(sta, index, oldValue, newValue);
@@ -52,79 +64,55 @@
 
 function AtomicsLoadJS(sta, index) {
   CheckSharedIntegerTypedArray(sta);
-  index = TO_INTEGER(index);
-  if (index < 0 || index >= %_TypedArrayGetLength(sta)) {
-    return UNDEFINED;
-  }
+  index = ValidateIndex(index, %_TypedArrayGetLength(sta));
   return %_AtomicsLoad(sta, index);
 }
 
 function AtomicsStoreJS(sta, index, value) {
   CheckSharedIntegerTypedArray(sta);
-  index = TO_INTEGER(index);
-  if (index < 0 || index >= %_TypedArrayGetLength(sta)) {
-    return UNDEFINED;
-  }
+  index = ValidateIndex(index, %_TypedArrayGetLength(sta));
   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;
-  }
+  index = ValidateIndex(index, %_TypedArrayGetLength(ia));
   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;
-  }
+  index = ValidateIndex(index, %_TypedArrayGetLength(ia));
   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;
-  }
+  index = ValidateIndex(index, %_TypedArrayGetLength(ia));
   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;
-  }
+  index = ValidateIndex(index, %_TypedArrayGetLength(ia));
   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;
-  }
+  index = ValidateIndex(index, %_TypedArrayGetLength(ia));
   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;
-  }
+  index = ValidateIndex(index, %_TypedArrayGetLength(ia));
   value = TO_NUMBER(value);
   return %_AtomicsExchange(ia, index, value);
 }
@@ -137,10 +125,7 @@
 
 function AtomicsFutexWaitJS(ia, index, value, timeout) {
   CheckSharedInteger32TypedArray(ia);
-  index = TO_INTEGER(index);
-  if (index < 0 || index >= %_TypedArrayGetLength(ia)) {
-    return UNDEFINED;
-  }
+  index = ValidateIndex(index, %_TypedArrayGetLength(ia));
   if (IS_UNDEFINED(timeout)) {
     timeout = INFINITY;
   } else {
@@ -156,20 +141,17 @@
 
 function AtomicsFutexWakeJS(ia, index, count) {
   CheckSharedInteger32TypedArray(ia);
-  index = TO_INTEGER(index);
-  if (index < 0 || index >= %_TypedArrayGetLength(ia)) {
-    return UNDEFINED;
-  }
+  index = ValidateIndex(index, %_TypedArrayGetLength(ia));
   count = MaxSimple(0, TO_INTEGER(count));
   return %AtomicsFutexWake(ia, index, count);
 }
 
 function AtomicsFutexWakeOrRequeueJS(ia, index1, count, value, index2) {
   CheckSharedInteger32TypedArray(ia);
-  index1 = TO_INTEGER(index1);
+  index1 = ValidateIndex(index1, %_TypedArrayGetLength(ia));
   count = MaxSimple(0, TO_INTEGER(count));
   value = TO_INT32(value);
-  index2 = TO_INTEGER(index2);
+  index2 = ValidateIndex(index2, %_TypedArrayGetLength(ia));
   if (index1 < 0 || index1 >= %_TypedArrayGetLength(ia) ||
       index2 < 0 || index2 >= %_TypedArrayGetLength(ia)) {
     return UNDEFINED;
diff --git a/src/js/harmony-regexp-exec.js b/src/js/harmony-regexp-exec.js
new file mode 100644
index 0000000..e2eece9
--- /dev/null
+++ b/src/js/harmony-regexp-exec.js
@@ -0,0 +1,37 @@
+// 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 GlobalRegExp = global.RegExp;
+var RegExpSubclassExecJS = utils.ImportNow("RegExpSubclassExecJS");
+var RegExpSubclassMatch = utils.ImportNow("RegExpSubclassMatch");
+var RegExpSubclassReplace = utils.ImportNow("RegExpSubclassReplace");
+var RegExpSubclassSearch = utils.ImportNow("RegExpSubclassSearch");
+var RegExpSubclassSplit = utils.ImportNow("RegExpSubclassSplit");
+var RegExpSubclassTest = utils.ImportNow("RegExpSubclassTest");
+var matchSymbol = utils.ImportNow("match_symbol");
+var replaceSymbol = utils.ImportNow("replace_symbol");
+var searchSymbol = utils.ImportNow("search_symbol");
+var splitSymbol = utils.ImportNow("split_symbol");
+
+utils.OverrideFunction(GlobalRegExp.prototype, "exec",
+                       RegExpSubclassExecJS, true);
+utils.OverrideFunction(GlobalRegExp.prototype, matchSymbol,
+                       RegExpSubclassMatch, true);
+utils.OverrideFunction(GlobalRegExp.prototype, replaceSymbol,
+                       RegExpSubclassReplace, true);
+utils.OverrideFunction(GlobalRegExp.prototype, searchSymbol,
+                       RegExpSubclassSearch, true);
+utils.OverrideFunction(GlobalRegExp.prototype, splitSymbol,
+                       RegExpSubclassSplit, true);
+utils.OverrideFunction(GlobalRegExp.prototype, "test",
+                       RegExpSubclassTest, true);
+
+})
diff --git a/src/js/harmony-regexp.js b/src/js/harmony-regexp.js
deleted file mode 100644
index f76ef86..0000000
--- a/src/js/harmony-regexp.js
+++ /dev/null
@@ -1,60 +0,0 @@
-// 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-string-padding.js b/src/js/harmony-string-padding.js
new file mode 100644
index 0000000..a6c6c47
--- /dev/null
+++ b/src/js/harmony-string-padding.js
@@ -0,0 +1,77 @@
+// Copyright 2016 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 GlobalString = global.String;
+var MakeTypeError;
+
+utils.Import(function(from) {
+  MakeTypeError = from.MakeTypeError;
+});
+
+// -------------------------------------------------------------------
+// http://tc39.github.io/proposal-string-pad-start-end/
+
+function StringPad(thisString, maxLength, fillString) {
+  maxLength = TO_LENGTH(maxLength);
+  var stringLength = thisString.length;
+
+  if (maxLength <= stringLength) return "";
+
+  if (IS_UNDEFINED(fillString)) {
+    fillString = " ";
+  } else {
+    fillString = TO_STRING(fillString);
+    if (fillString === "") {
+      fillString = " ";
+    }
+  }
+
+  var fillLength = maxLength - stringLength;
+  var repetitions = (fillLength / fillString.length) | 0;
+  var remainingChars = (fillLength - fillString.length * repetitions) | 0;
+
+  var filler = "";
+  while (true) {
+    if (repetitions & 1) filler += fillString;
+    repetitions >>= 1;
+    if (repetitions === 0) break;
+    fillString += fillString;
+  }
+
+  if (remainingChars) {
+    filler += %_SubString(fillString, 0, remainingChars);
+  }
+
+  return filler;
+}
+
+function StringPadStart(maxLength, fillString) {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.padStart")
+  var thisString = TO_STRING(this);
+
+  return StringPad(thisString, maxLength, fillString) + thisString;
+}
+%FunctionSetLength(StringPadStart, 1);
+
+function StringPadEnd(maxLength, fillString) {
+  CHECK_OBJECT_COERCIBLE(this, "String.prototype.padEnd")
+  var thisString = TO_STRING(this);
+
+  return thisString + StringPad(thisString, maxLength, fillString);
+}
+%FunctionSetLength(StringPadEnd, 1);
+
+utils.InstallFunctions(GlobalString.prototype, DONT_ENUM, [
+  "padStart", StringPadStart,
+  "padEnd", StringPadEnd
+]);
+
+});
diff --git a/src/js/harmony-unicode-regexps.js b/src/js/harmony-unicode-regexps.js
index b24bbdf..16d06ba 100644
--- a/src/js/harmony-unicode-regexps.js
+++ b/src/js/harmony-unicode-regexps.js
@@ -31,10 +31,9 @@
     }
     throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.unicode");
   }
-  return !!REGEXP_UNICODE(this);
+  return TO_BOOLEAN(REGEXP_UNICODE(this));
 }
-%FunctionSetName(RegExpGetUnicode, "RegExp.prototype.unicode");
-%SetNativeFlag(RegExpGetUnicode);
+%SetForceInlineFlag(RegExpGetUnicode);
 
 utils.InstallGetter(GlobalRegExp.prototype, 'unicode', RegExpGetUnicode);
 
diff --git a/src/js/i18n.js b/src/js/i18n.js
index 7b2f5a1..845289a 100644
--- a/src/js/i18n.js
+++ b/src/js/i18n.js
@@ -20,26 +20,30 @@
 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 InstallFunctions = utils.InstallFunctions;
+var InstallGetter = utils.InstallGetter;
+var InternalPackedArray = utils.InternalPackedArray;
+var InternalRegExpMatch;
+var InternalRegExpReplace
+var IsFinite;
+var IsNaN;
 var MakeError;
 var MakeRangeError;
 var MakeTypeError;
-var MathFloor;
 var ObjectDefineProperties = utils.ImportNow("ObjectDefineProperties");
 var ObjectDefineProperty = utils.ImportNow("ObjectDefineProperty");
+var ObjectHasOwnProperty = utils.ImportNow("ObjectHasOwnProperty");
+var OverrideFunction = utils.OverrideFunction;
 var patternSymbol = utils.ImportNow("intl_pattern_symbol");
-var RegExpTest;
 var resolvedSymbol = utils.ImportNow("intl_resolved_symbol");
+var SetFunctionName = utils.SetFunctionName;
 var StringIndexOf;
 var StringLastIndexOf;
-var StringMatch;
-var StringReplace;
 var StringSplit;
 var StringSubstr;
 var StringSubstring;
@@ -53,17 +57,72 @@
   MakeError = from.MakeError;
   MakeRangeError = from.MakeRangeError;
   MakeTypeError = from.MakeTypeError;
-  MathFloor = from.MathFloor;
-  RegExpTest = from.RegExpTest;
+  InternalRegExpMatch = from.InternalRegExpMatch;
+  InternalRegExpReplace = from.InternalRegExpReplace;
   StringIndexOf = from.StringIndexOf;
   StringLastIndexOf = from.StringLastIndexOf;
-  StringMatch = from.StringMatch;
-  StringReplace = from.StringReplace;
   StringSplit = from.StringSplit;
   StringSubstr = from.StringSubstr;
   StringSubstring = from.StringSubstring;
 });
 
+// Utilities for definitions
+
+function InstallFunction(object, name, func) {
+  InstallFunctions(object, DONT_ENUM, [name, func]);
+}
+
+
+function InstallConstructor(object, name, func) {
+  %CheckIsBootstrapping();
+  SetFunctionName(func, name);
+  %AddNamedProperty(object, name, func, DONT_ENUM);
+  %SetNativeFlag(func);
+  %ToFastProperties(object);
+}
+
+/**
+ * Adds bound method to the prototype of the given object.
+ */
+function AddBoundMethod(obj, methodName, implementation, length) {
+  %CheckIsBootstrapping();
+  var internalName = %CreatePrivateSymbol(methodName);
+  var getter = function() {
+    if (!%IsInitializedIntlObject(this)) {
+      throw MakeTypeError(kMethodCalledOnWrongObject, methodName);
+    }
+    if (IS_UNDEFINED(this[internalName])) {
+      var boundMethod;
+      if (IS_UNDEFINED(length) || length === 2) {
+        boundMethod = (x, y) => implementation(this, x, y);
+      } else if (length === 1) {
+        boundMethod = x => implementation(this, x);
+      } else {
+        boundMethod = (...args) => {
+          // DateTimeFormat.format needs to be 0 arg method, but can stil
+          // receive optional dateValue param. If one was provided, pass it
+          // along.
+          if (args.length > 0) {
+            return implementation(this, args[0]);
+          } else {
+            return implementation(this);
+          }
+        }
+      }
+      // TODO(littledan): Once function name reform is shipped, remove the
+      // following line and wrap the boundMethod definition in an anonymous
+      // function macro.
+      %FunctionSetName(boundMethod, '__bound' + methodName + '__');
+      %FunctionRemovePrototype(boundMethod);
+      %SetNativeFlag(boundMethod);
+      this[internalName] = boundMethod;
+    }
+    return this[internalName];
+  };
+
+  InstallGetter(obj.prototype, methodName, getter, DONT_ENUM);
+}
+
 // -------------------------------------------------------------------
 
 var Intl = {};
@@ -197,74 +256,13 @@
   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 (arguments.length > 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()))) {
+  if (IS_NULL(InternalRegExpMatch(GetServiceRE(), service))) {
     throw MakeError(kWrongServiceType, service);
   }
 
@@ -312,10 +310,8 @@
   var matchedLocales = [];
   for (var i = 0; i < requestedLocales.length; ++i) {
     // Remove -u- extension.
-    var locale = %_Call(StringReplace,
-                        requestedLocales[i],
-                        GetUnicodeExtensionRE(),
-                        '');
+    var locale = InternalRegExpReplace(
+        GetUnicodeExtensionRE(), requestedLocales[i], '');
     do {
       if (!IS_UNDEFINED(availableLocales[locale])) {
         // Push requested locale not the resolved one.
@@ -421,7 +417,7 @@
  * lookup algorithm.
  */
 function lookupMatcher(service, requestedLocales) {
-  if (IS_NULL(%_Call(StringMatch, service, GetServiceRE()))) {
+  if (IS_NULL(InternalRegExpMatch(GetServiceRE(), service))) {
     throw MakeError(kWrongServiceType, service);
   }
 
@@ -432,13 +428,13 @@
 
   for (var i = 0; i < requestedLocales.length; ++i) {
     // Remove all extensions.
-    var locale = %_Call(StringReplace, requestedLocales[i],
-                        GetAnyExtensionRE(), '');
+    var locale = InternalRegExpReplace(
+        GetAnyExtensionRE(), requestedLocales[i], '');
     do {
       if (!IS_UNDEFINED(AVAILABLE_LOCALES[service][locale])) {
         // Return the resolved locale and extension.
-        var extensionMatch =
-            %_Call(StringMatch, requestedLocales[i], GetUnicodeExtensionRE());
+        var extensionMatch = InternalRegExpMatch(
+            GetUnicodeExtensionRE(), requestedLocales[i]);
         var extension = IS_NULL(extensionMatch) ? '' : extensionMatch[0];
         return {'locale': locale, 'extension': extension, 'position': i};
       }
@@ -535,7 +531,7 @@
   }
 
   for (var key in keyValues) {
-    if (%HasOwnProperty(keyValues, key)) {
+    if (HAS_OWN_PROPERTY(keyValues, key)) {
       var value = UNDEFINED;
       var map = keyValues[key];
       if (!IS_UNDEFINED(map.property)) {
@@ -551,7 +547,7 @@
       // 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)) {
+      if (HAS_OWN_PROPERTY(extensionMap, key)) {
         value = extensionMap[key];
         if (!IS_UNDEFINED(value)) {
           updateProperty(map.property, map.type, value);
@@ -612,8 +608,8 @@
   }
 
   // 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);
+  var resolvedBase = new GlobalRegExp('^' + locales[1].base, 'g');
+  return InternalRegExpReplace(resolvedBase, resolved, locales[0].base);
 }
 
 
@@ -627,10 +623,10 @@
   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) {
+    if (HAS_OWN_PROPERTY(available, i)) {
+      var parts = InternalRegExpMatch(
+          /^([a-z]{2,3})-([A-Z][a-z]{3})-([A-Z]{2})$/, i);
+      if (!IS_NULL(parts)) {
         // Build xx-ZZ. We don't care about the actual value,
         // as long it's not undefined.
         available[parts[1] + '-' + parts[3]] = null;
@@ -700,7 +696,7 @@
  * 'of', 'au' and 'es' are special-cased and lowercased.
  */
 function toTitleCaseTimezoneLocation(location) {
-  var match = %_Call(StringMatch, location, GetTimezoneNameLocationPartRE());
+  var match = InternalRegExpMatch(GetTimezoneNameLocationPartRE(), location)
   if (IS_NULL(match)) throw MakeRangeError(kExpectedLocation, location);
 
   var result = toTitleCaseWord(match[1]);
@@ -797,7 +793,7 @@
  */
 function isValidLanguageTag(locale) {
   // Check if it's well-formed, including grandfadered tags.
-  if (!%_Call(RegExpTest, GetLanguageTagRE(), locale)) {
+  if (IS_NULL(InternalRegExpMatch(GetLanguageTagRE(), locale))) {
     return false;
   }
 
@@ -809,17 +805,17 @@
   // Check if there are any duplicate variants or singletons (extensions).
 
   // Remove private use section.
-  locale = %_Call(StringSplit, locale, /-x-/)[0];
+  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, /-/);
+  var parts = %_Call(StringSplit, locale, '-');
   for (var i = 1; i < parts.length; i++) {
     var value = parts[i];
-    if (%_Call(RegExpTest, GetLanguageVariantRE(), value) &&
+    if (!IS_NULL(InternalRegExpMatch(GetLanguageVariantRE(), value)) &&
         extensions.length === 0) {
       if (%_Call(ArrayIndexOf, variants, value) === -1) {
         %_Call(ArrayPush, variants, value);
@@ -828,7 +824,7 @@
       }
     }
 
-    if (%_Call(RegExpTest, GetLanguageSingletonRE(), value)) {
+    if (!IS_NULL(InternalRegExpMatch(GetLanguageSingletonRE(), value))) {
       if (%_Call(ArrayIndexOf, extensions, value) === -1) {
         %_Call(ArrayPush, extensions, value);
       } else {
@@ -943,7 +939,7 @@
 
   var collation = 'default';
   var extension = '';
-  if (%HasOwnProperty(extensionMap, 'co') && internalOptions.usage === 'sort') {
+  if (HAS_OWN_PROPERTY(extensionMap, 'co') && internalOptions.usage === 'sort') {
 
     /**
      * Allowed -u-co- values. List taken from:
@@ -1001,7 +997,7 @@
  *
  * @constructor
  */
-%AddNamedProperty(Intl, 'Collator', function() {
+InstallConstructor(Intl, 'Collator', function() {
     var locales = arguments[0];
     var options = arguments[1];
 
@@ -1011,15 +1007,14 @@
     }
 
     return initializeCollator(TO_OBJECT(this), locales, options);
-  },
-  DONT_ENUM
+  }
 );
 
 
 /**
  * Collator resolvedOptions method.
  */
-%AddNamedProperty(Intl.Collator.prototype, 'resolvedOptions', function() {
+InstallFunction(Intl.Collator.prototype, 'resolvedOptions', function() {
     if (!IS_UNDEFINED(new.target)) {
       throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
     }
@@ -1041,12 +1036,8 @@
       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);
 
 
 /**
@@ -1055,18 +1046,14 @@
  * order in the returned list as in the input list.
  * Options are optional parameter.
  */
-%AddNamedProperty(Intl.Collator, 'supportedLocalesOf', function(locales) {
+InstallFunction(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);
 
 
 /**
@@ -1085,7 +1072,7 @@
 };
 
 
-addBoundMethod(Intl.Collator, 'compare', compare, 2);
+AddBoundMethod(Intl.Collator, 'compare', compare, 2);
 
 /**
  * Verifies that the input is a well-formed ISO 4217 currency code.
@@ -1093,9 +1080,8 @@
  * For example \u00DFP (Eszett+P) becomes SSP.
  */
 function isWellFormedCurrencyCode(currency) {
-  return typeof currency == "string" &&
-      currency.length == 3 &&
-      %_Call(StringMatch, currency, /[^A-Za-z]/) == null;
+  return typeof currency == "string" && currency.length == 3 &&
+      IS_NULL(InternalRegExpMatch(/[^A-Za-z]/, currency));
 }
 
 
@@ -1110,7 +1096,7 @@
     if (IsNaN(value) || value < min || value > max) {
       throw MakeRangeError(kPropertyValueOutOfRange, property);
     }
-    return MathFloor(value);
+    return %math_floor(value);
   }
 
   return fallback;
@@ -1225,10 +1211,10 @@
     style: {value: internalOptions.style, writable: true},
     useGrouping: {writable: true}
   });
-  if (%HasOwnProperty(internalOptions, 'minimumSignificantDigits')) {
+  if (HAS_OWN_PROPERTY(internalOptions, 'minimumSignificantDigits')) {
     defineWEProperty(resolved, 'minimumSignificantDigits', UNDEFINED);
   }
-  if (%HasOwnProperty(internalOptions, 'maximumSignificantDigits')) {
+  if (HAS_OWN_PROPERTY(internalOptions, 'maximumSignificantDigits')) {
     defineWEProperty(resolved, 'maximumSignificantDigits', UNDEFINED);
   }
   var formatter = %CreateNumberFormat(requestedLocale,
@@ -1254,7 +1240,7 @@
  *
  * @constructor
  */
-%AddNamedProperty(Intl, 'NumberFormat', function() {
+InstallConstructor(Intl, 'NumberFormat', function() {
     var locales = arguments[0];
     var options = arguments[1];
 
@@ -1264,15 +1250,14 @@
     }
 
     return initializeNumberFormat(TO_OBJECT(this), locales, options);
-  },
-  DONT_ENUM
+  }
 );
 
 
 /**
  * NumberFormat resolvedOptions method.
  */
-%AddNamedProperty(Intl.NumberFormat.prototype, 'resolvedOptions', function() {
+InstallFunction(Intl.NumberFormat.prototype, 'resolvedOptions', function() {
     if (!IS_UNDEFINED(new.target)) {
       throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
     }
@@ -1301,24 +1286,19 @@
                         format[resolvedSymbol].currencyDisplay);
     }
 
-    if (%HasOwnProperty(format[resolvedSymbol], 'minimumSignificantDigits')) {
+    if (HAS_OWN_PROPERTY(format[resolvedSymbol], 'minimumSignificantDigits')) {
       defineWECProperty(result, 'minimumSignificantDigits',
                         format[resolvedSymbol].minimumSignificantDigits);
     }
 
-    if (%HasOwnProperty(format[resolvedSymbol], 'maximumSignificantDigits')) {
+    if (HAS_OWN_PROPERTY(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);
 
 
 /**
@@ -1327,18 +1307,14 @@
  * order in the returned list as in the input list.
  * Options are optional parameter.
  */
-%AddNamedProperty(Intl.NumberFormat, 'supportedLocalesOf', function(locales) {
+InstallFunction(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);
 
 
 /**
@@ -1364,8 +1340,8 @@
 }
 
 
-addBoundMethod(Intl.NumberFormat, 'format', formatNumber, 1);
-addBoundMethod(Intl.NumberFormat, 'v8Parse', parseNumber, 1);
+AddBoundMethod(Intl.NumberFormat, 'format', formatNumber, 1);
+AddBoundMethod(Intl.NumberFormat, 'v8Parse', parseNumber, 1);
 
 /**
  * Returns a string that matches LDML representation of the options object.
@@ -1435,57 +1411,57 @@
  */
 function fromLDMLString(ldmlString) {
   // First remove '' quoted text, so we lose 'Uhr' strings.
-  ldmlString = %_Call(StringReplace, ldmlString, GetQuotedStringRE(), '');
+  ldmlString = InternalRegExpReplace(GetQuotedStringRE(), ldmlString, '');
 
   var options = {};
-  var match = %_Call(StringMatch, ldmlString, /E{3,5}/g);
+  var match = InternalRegExpMatch(/E{3,5}/, ldmlString);
   options = appendToDateTimeObject(
       options, 'weekday', match, {EEEEE: 'narrow', EEE: 'short', EEEE: 'long'});
 
-  match = %_Call(StringMatch, ldmlString, /G{3,5}/g);
+  match = InternalRegExpMatch(/G{3,5}/, ldmlString);
   options = appendToDateTimeObject(
       options, 'era', match, {GGGGG: 'narrow', GGG: 'short', GGGG: 'long'});
 
-  match = %_Call(StringMatch, ldmlString, /y{1,2}/g);
+  match = InternalRegExpMatch(/y{1,2}/, ldmlString);
   options = appendToDateTimeObject(
       options, 'year', match, {y: 'numeric', yy: '2-digit'});
 
-  match = %_Call(StringMatch, ldmlString, /M{1,5}/g);
+  match = InternalRegExpMatch(/M{1,5}/, ldmlString);
   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);
+  match = InternalRegExpMatch(/L{1,5}/, ldmlString);
   options = appendToDateTimeObject(options, 'month', match, {LL: '2-digit',
       L: 'numeric', LLLLL: 'narrow', LLL: 'short', LLLL: 'long'});
 
-  match = %_Call(StringMatch, ldmlString, /d{1,2}/g);
+  match = InternalRegExpMatch(/d{1,2}/, ldmlString);
   options = appendToDateTimeObject(
       options, 'day', match, {d: 'numeric', dd: '2-digit'});
 
-  match = %_Call(StringMatch, ldmlString, /h{1,2}/g);
+  match = InternalRegExpMatch(/h{1,2}/, ldmlString);
   if (match !== null) {
     options['hour12'] = true;
   }
   options = appendToDateTimeObject(
       options, 'hour', match, {h: 'numeric', hh: '2-digit'});
 
-  match = %_Call(StringMatch, ldmlString, /H{1,2}/g);
+  match = InternalRegExpMatch(/H{1,2}/, ldmlString);
   if (match !== null) {
     options['hour12'] = false;
   }
   options = appendToDateTimeObject(
       options, 'hour', match, {H: 'numeric', HH: '2-digit'});
 
-  match = %_Call(StringMatch, ldmlString, /m{1,2}/g);
+  match = InternalRegExpMatch(/m{1,2}/, ldmlString);
   options = appendToDateTimeObject(
       options, 'minute', match, {m: 'numeric', mm: '2-digit'});
 
-  match = %_Call(StringMatch, ldmlString, /s{1,2}/g);
+  match = InternalRegExpMatch(/s{1,2}/, ldmlString);
   options = appendToDateTimeObject(
       options, 'second', match, {s: 'numeric', ss: '2-digit'});
 
-  match = %_Call(StringMatch, ldmlString, /z|zzzz/g);
+  match = InternalRegExpMatch(/z|zzzz/, ldmlString);
   options = appendToDateTimeObject(
       options, 'timeZoneName', match, {z: 'short', zzzz: 'long'});
 
@@ -1495,7 +1471,7 @@
 
 function appendToDateTimeObject(options, option, match, pairs) {
   if (IS_NULL(match)) {
-    if (!%HasOwnProperty(options, option)) {
+    if (!HAS_OWN_PROPERTY(options, option)) {
       defineWEProperty(options, option, UNDEFINED);
     }
     return options;
@@ -1658,7 +1634,7 @@
  *
  * @constructor
  */
-%AddNamedProperty(Intl, 'DateTimeFormat', function() {
+InstallConstructor(Intl, 'DateTimeFormat', function() {
     var locales = arguments[0];
     var options = arguments[1];
 
@@ -1668,15 +1644,14 @@
     }
 
     return initializeDateTimeFormat(TO_OBJECT(this), locales, options);
-  },
-  DONT_ENUM
+  }
 );
 
 
 /**
  * DateTimeFormat resolvedOptions method.
  */
-%AddNamedProperty(Intl.DateTimeFormat.prototype, 'resolvedOptions', function() {
+InstallFunction(Intl.DateTimeFormat.prototype, 'resolvedOptions', function() {
     if (!IS_UNDEFINED(new.target)) {
       throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
     }
@@ -1735,13 +1710,8 @@
     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);
 
 
 /**
@@ -1750,18 +1720,14 @@
  * order in the returned list as in the input list.
  * Options are optional parameter.
  */
-%AddNamedProperty(Intl.DateTimeFormat, 'supportedLocalesOf', function(locales) {
+InstallFunction(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);
 
 
 /**
@@ -1797,8 +1763,8 @@
 
 
 // 0 because date is optional argument.
-addBoundMethod(Intl.DateTimeFormat, 'format', formatDate, 0);
-addBoundMethod(Intl.DateTimeFormat, 'v8Parse', parseDate, 1);
+AddBoundMethod(Intl.DateTimeFormat, 'format', formatDate, 0);
+AddBoundMethod(Intl.DateTimeFormat, 'v8Parse', parseDate, 1);
 
 
 /**
@@ -1822,7 +1788,7 @@
 
   // We expect only _, '-' and / beside ASCII letters.
   // All inputs should conform to Area/Location(/Location)* from now on.
-  var match = %_Call(StringMatch, tzID, GetTimezoneNameCheckRE());
+  var match = InternalRegExpMatch(GetTimezoneNameCheckRE(), tzID);
   if (IS_NULL(match)) throw MakeRangeError(kExpectedTimezoneID, tzID);
 
   var result = toTitleCaseTimezoneLocation(match[1]) + '/' +
@@ -1885,7 +1851,7 @@
  *
  * @constructor
  */
-%AddNamedProperty(Intl, 'v8BreakIterator', function() {
+InstallConstructor(Intl, 'v8BreakIterator', function() {
     var locales = arguments[0];
     var options = arguments[1];
 
@@ -1895,15 +1861,14 @@
     }
 
     return initializeBreakIterator(TO_OBJECT(this), locales, options);
-  },
-  DONT_ENUM
+  }
 );
 
 
 /**
  * BreakIterator resolvedOptions method.
  */
-%AddNamedProperty(Intl.v8BreakIterator.prototype, 'resolvedOptions',
+InstallFunction(Intl.v8BreakIterator.prototype, 'resolvedOptions',
   function() {
     if (!IS_UNDEFINED(new.target)) {
       throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
@@ -1922,13 +1887,8 @@
       locale: locale,
       type: segmenter[resolvedSymbol].type
     };
-  },
-  DONT_ENUM
+  }
 );
-%FunctionSetName(Intl.v8BreakIterator.prototype.resolvedOptions,
-                 'resolvedOptions');
-%FunctionRemovePrototype(Intl.v8BreakIterator.prototype.resolvedOptions);
-%SetNativeFlag(Intl.v8BreakIterator.prototype.resolvedOptions);
 
 
 /**
@@ -1937,19 +1897,15 @@
  * order in the returned list as in the input list.
  * Options are optional parameter.
  */
-%AddNamedProperty(Intl.v8BreakIterator, 'supportedLocalesOf',
+InstallFunction(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);
 
 
 /**
@@ -1994,11 +1950,11 @@
 }
 
 
-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);
+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 = {
@@ -2036,18 +1992,6 @@
   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.
diff --git a/src/js/json.js b/src/js/json.js
index 73d7802..c6dbed9 100644
--- a/src/js/json.js
+++ b/src/js/json.js
@@ -19,6 +19,10 @@
 var MaxSimple;
 var MinSimple;
 var ObjectHasOwnProperty;
+var Stack;
+var StackHas;
+var StackPop;
+var StackPush;
 var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
 
 utils.Import(function(from) {
@@ -26,6 +30,10 @@
   MaxSimple = from.MaxSimple;
   MinSimple = from.MinSimple;
   ObjectHasOwnProperty = from.ObjectHasOwnProperty;
+  Stack = from.Stack;
+  StackHas = from.StackHas;
+  StackPop = from.StackPop;
+  StackPush = from.StackPush;
 });
 
 // -------------------------------------------------------------------
@@ -51,7 +59,9 @@
         }
       }
     } else {
-      for (var p of %object_keys(val)) {
+      var keys = %object_keys(val);
+      for (var i = 0; i < keys.length; i++) {
+        var p = keys[i];
         var newElement = InternalizeJSONProperty(val, p, reviver);
         if (IS_UNDEFINED(newElement)) {
           %reflect_delete_property(val, p);
@@ -76,7 +86,8 @@
 
 
 function SerializeArray(value, replacer, stack, indent, gap) {
-  if (!%PushIfAbsent(stack, value)) throw MakeTypeError(kCircularStructure);
+  if (StackHas(stack, value)) throw MakeTypeError(kCircularStructure);
+  StackPush(stack, value);
   var stepback = indent;
   indent += gap;
   var partial = new InternalArray();
@@ -99,13 +110,14 @@
   } else {
     final = "[]";
   }
-  stack.pop();
+  StackPop(stack);
   return final;
 }
 
 
 function SerializeObject(value, replacer, stack, indent, gap) {
-  if (!%PushIfAbsent(stack, value)) throw MakeTypeError(kCircularStructure);
+  if (StackHas(stack, value)) throw MakeTypeError(kCircularStructure);
+  StackPush(stack, value);
   var stepback = indent;
   indent += gap;
   var partial = new InternalArray();
@@ -122,7 +134,9 @@
       }
     }
   } else {
-    for (var p of %object_keys(value)) {
+    var keys = %object_keys(value);
+    for (var i = 0; i < keys.length; i++) {
+      var p = keys[i];
       var strP = JSONSerialize(p, value, replacer, stack, indent, gap);
       if (!IS_UNDEFINED(strP)) {
         var member = %QuoteJSONString(p) + ":";
@@ -142,7 +156,7 @@
   } else {
     final = "{}";
   }
-  stack.pop();
+  StackPop(stack);
   return final;
 }
 
@@ -237,7 +251,7 @@
   if (!IS_CALLABLE(replacer) && !property_list && !gap && !IS_PROXY(value)) {
     return %BasicJSONStringify(value);
   }
-  return JSONSerialize('', {'': value}, replacer, new InternalArray(), "", gap);
+  return JSONSerialize('', {'': value}, replacer, new Stack(), "", gap);
 }
 
 // -------------------------------------------------------------------
@@ -275,7 +289,7 @@
   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(), "", "");
+  return JSONSerialize(key, holder, UNDEFINED, new Stack(), "", "");
 }
 
 %InstallToContext(["json_serialize_adapter", JsonSerializeAdapter]);
diff --git a/src/js/macros.py b/src/js/macros.py
index b2a7856..a4c7f53 100644
--- a/src/js/macros.py
+++ b/src/js/macros.py
@@ -88,9 +88,9 @@
 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_TYPEDARRAY(arg)        = (%_IsTypedArray(arg));
 macro IS_UNDEFINED(arg)         = (arg === (void 0));
 macro IS_WEAKMAP(arg)           = (%_ClassOf(arg) === 'WeakMap');
 macro IS_WEAKSET(arg)           = (%_ClassOf(arg) === 'WeakSet');
@@ -122,12 +122,12 @@
 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));
+macro HAS_OWN_PROPERTY(obj, key) = (%_Call(ObjectHasOwnProperty, obj, key));
+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_PRIVATE(obj, key) = HAS_OWN_PROPERTY(obj, key);
 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);
@@ -255,7 +255,6 @@
 define kForcedGC = 7;
 define kSloppyMode = 8;
 define kStrictMode = 9;
-define kStrongMode = 10;
 define kRegExpPrototypeStickyGetter = 11;
 define kRegExpPrototypeToString = 12;
 define kRegExpPrototypeUnicodeGetter = 13;
@@ -265,3 +264,15 @@
 define kPromiseChain = 17;
 define kPromiseAccept = 18;
 define kPromiseDefer = 19;
+define kHtmlCommentInExternalScript = 20;
+define kHtmlComment = 21;
+define kSloppyModeBlockScopedFunctionRedefinition = 22;
+define kForInInitializer = 23;
+define kArrayProtectorDirtied = 24;
+define kArraySpeciesModified = 25;
+define kArrayPrototypeConstructorModified = 26;
+define kArrayInstanceProtoModified = 27;
+define kArrayInstanceConstructorModified = 28;
+define kLegacyFunctionDeclaration = 29;
+define kRegExpPrototypeSourceGetter = 30;
+define kRegExpPrototypeOldFlagGetter = 31;
diff --git a/src/js/math.js b/src/js/math.js
index a698fd4..f8ad6b1 100644
--- a/src/js/math.js
+++ b/src/js/math.js
@@ -10,7 +10,6 @@
 // -------------------------------------------------------------------
 // Imports
 
-define kRandomBatchSize = 64;
 // The first two slots are reserved to persist PRNG state.
 define kRandomNumberStart = 2;
 
@@ -19,7 +18,7 @@
 var GlobalObject = global.Object;
 var InternalArray = utils.InternalArray;
 var NaN = %GetRootNaN();
-var nextRandomIndex = kRandomBatchSize;
+var nextRandomIndex = 0;
 var randomNumbers = UNDEFINED;
 var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
 
@@ -31,33 +30,13 @@
   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);
+  return %MathAtan2(y, x);
 }
 
 // ECMA 262 - 15.8.2.8
@@ -65,11 +44,6 @@
   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));
@@ -82,34 +56,24 @@
 
 // ECMA 262 - 15.8.2.14
 function MathRandom() {
-  if (nextRandomIndex >= kRandomBatchSize) {
+  // While creating a startup snapshot, %GenerateRandomNumbers returns a
+  // normal array containing a single random number, and has to be called for
+  // every new random number.
+  // Otherwise, it returns a pre-populated typed array of random numbers. The
+  // first two elements are reserved for the PRNG state.
+  if (nextRandomIndex <= kRandomNumberStart) {
     randomNumbers = %GenerateRandomNumbers(randomNumbers);
-    nextRandomIndex = kRandomNumberStart;
+    nextRandomIndex = randomNumbers.length;
   }
-  return randomNumbers[nextRandomIndex++];
+  return randomNumbers[--nextRandomIndex];
 }
 
 function MathRandomRaw() {
-  if (nextRandomIndex >= kRandomBatchSize) {
+  if (nextRandomIndex <= kRandomNumberStart) {
     randomNumbers = %GenerateRandomNumbers(randomNumbers);
-    nextRandomIndex = kRandomNumberStart;
+    nextRandomIndex = randomNumbers.length;
   }
-  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));
+  return %_DoubleLo(randomNumbers[--nextRandomIndex]) & 0x3FFFFFFF;
 }
 
 // ES6 draft 09-27-13, section 20.2.2.28.
@@ -121,23 +85,14 @@
   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));
+  if (x > 0) return MathLog(x + %math_sqrt(x * x + 1));
   // This is to prevent numerical errors caused by large negative x.
-  return -MathLog(-x + %_MathSqrt(x * x + 1));
+  return -MathLog(-x + %math_sqrt(x * x + 1));
 }
 
 // ES6 draft 09-27-13, section 20.2.2.3.
@@ -146,7 +101,7 @@
   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));
+  return MathLog(x + %math_sqrt(x + 1) * %math_sqrt(x - 1));
 }
 
 // ES6 draft 09-27-13, section 20.2.2.7.
@@ -185,17 +140,7 @@
     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);
+  return %math_sqrt(sum) * max;
 }
 
 // ES6 draft 09-27-13, section 20.2.2.9.
@@ -213,7 +158,7 @@
 endmacro
 
 function CubeRoot(x) {
-  var approx_hi = MathFloorJS(%_DoubleHi(x) / 3) + 0x2A9F7893;
+  var approx_hi = %math_floor(%_DoubleHi(x) / 3) + 0x2A9F7893;
   var approx = %_ConstructDouble(approx_hi | 0, 0);
   approx = NEWTON_ITERATION_CBRT(x, approx);
   approx = NEWTON_ITERATION_CBRT(x, approx);
@@ -223,6 +168,10 @@
 
 // -------------------------------------------------------------------
 
+%InstallToContext([
+  "math_pow", MathPowJS,
+]);
+
 %AddNamedProperty(GlobalMath, toStringTagSymbol, "Math", READ_ONLY | DONT_ENUM);
 
 // Set up math constants.
@@ -246,41 +195,22 @@
 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,
-  "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
@@ -288,7 +218,6 @@
 utils.Export(function(to) {
   to.MathAbs = MathAbs;
   to.MathExp = MathExp;
-  to.MathFloor = MathFloorJS;
   to.IntRandom = MathRandomRaw;
 });
 
diff --git a/src/js/messages.js b/src/js/messages.js
index feb14d3..f8cb967 100644
--- a/src/js/messages.js
+++ b/src/js/messages.js
@@ -23,7 +23,6 @@
     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");
@@ -34,6 +33,7 @@
 var InternalArray = utils.InternalArray;
 var internalErrorSymbol = utils.ImportNow("internal_error_symbol");
 var ObjectDefineProperty;
+var ObjectHasOwnProperty;
 var ObjectToString = utils.ImportNow("object_to_string");
 var Script = utils.ImportNow("Script");
 var stackTraceSymbol = utils.ImportNow("stack_trace_symbol");
@@ -56,6 +56,7 @@
   Int32x4ToString = from.Int32x4ToString;
   Int8x16ToString = from.Int8x16ToString;
   ObjectDefineProperty = from.ObjectDefineProperty;
+  ObjectHasOwnProperty = from.ObjectHasOwnProperty;
   StringCharAt = from.StringCharAt;
   StringIndexOf = from.StringIndexOf;
   StringSubstring = from.StringSubstring;
@@ -65,10 +66,6 @@
   Uint8x16ToString = from.Uint8x16ToString;
 });
 
-utils.ImportFromExperimental(function(from) {
-  FLAG_harmony_tostring = from.FLAG_harmony_tostring;
-});
-
 // -------------------------------------------------------------------
 
 var GlobalError;
@@ -85,13 +82,8 @@
   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 {
+  var tag = %GetDataProperty(O, toStringTagSymbol);
+  if (!IS_STRING(tag)) {
     tag = builtinTag;
   }
   return `[object ${tag}]`;
@@ -578,69 +570,90 @@
   SET_PRIVATE(this, callSiteStrictSymbol, TO_BOOLEAN(strict_mode));
 }
 
+function CheckCallSite(obj, name) {
+  if (!IS_RECEIVER(obj) || !HAS_PRIVATE(obj, callSiteFunctionSymbol)) {
+    throw MakeTypeError(kCallSiteMethod, name);
+  }
+}
+
 function CallSiteGetThis() {
+  CheckCallSite(this, "getThis");
   return GET_PRIVATE(this, callSiteStrictSymbol)
       ? UNDEFINED : GET_PRIVATE(this, callSiteReceiverSymbol);
 }
 
 function CallSiteGetFunction() {
+  CheckCallSite(this, "getFunction");
   return GET_PRIVATE(this, callSiteStrictSymbol)
       ? UNDEFINED : GET_PRIVATE(this, callSiteFunctionSymbol);
 }
 
 function CallSiteGetPosition() {
+  CheckCallSite(this, "getPosition");
   return GET_PRIVATE(this, callSitePositionSymbol);
 }
 
 function CallSiteGetTypeName() {
+  CheckCallSite(this, "getTypeName");
   return GetTypeName(GET_PRIVATE(this, callSiteReceiverSymbol), false);
 }
 
 function CallSiteIsToplevel() {
+  CheckCallSite(this, "isTopLevel");
   return %CallSiteIsToplevelRT(this);
 }
 
 function CallSiteIsEval() {
+  CheckCallSite(this, "isEval");
   return %CallSiteIsEvalRT(this);
 }
 
 function CallSiteGetEvalOrigin() {
+  CheckCallSite(this, "getEvalOrigin");
   var script = %FunctionGetScript(GET_PRIVATE(this, callSiteFunctionSymbol));
   return FormatEvalOrigin(script);
 }
 
 function CallSiteGetScriptNameOrSourceURL() {
+  CheckCallSite(this, "getScriptNameOrSourceURL");
   return %CallSiteGetScriptNameOrSourceUrlRT(this);
 }
 
 function CallSiteGetFunctionName() {
   // See if the function knows its own name
+  CheckCallSite(this, "getFunctionName");
   return %CallSiteGetFunctionNameRT(this);
 }
 
 function CallSiteGetMethodName() {
   // See if we can find a unique property on the receiver that holds
   // this function.
+  CheckCallSite(this, "getMethodName");
   return %CallSiteGetMethodNameRT(this);
 }
 
 function CallSiteGetFileName() {
+  CheckCallSite(this, "getFileName");
   return %CallSiteGetFileNameRT(this);
 }
 
 function CallSiteGetLineNumber() {
+  CheckCallSite(this, "getLineNumber");
   return %CallSiteGetLineNumberRT(this);
 }
 
 function CallSiteGetColumnNumber() {
+  CheckCallSite(this, "getColumnNumber");
   return %CallSiteGetColumnNumberRT(this);
 }
 
 function CallSiteIsNative() {
+  CheckCallSite(this, "isNative");
   return %CallSiteIsNativeRT(this);
 }
 
 function CallSiteIsConstructor() {
+  CheckCallSite(this, "isConstructor");
   return %CallSiteIsConstructorRT(this);
 }
 
diff --git a/src/js/prologue.js b/src/js/prologue.js
index 24225a0..f9589a5 100644
--- a/src/js/prologue.js
+++ b/src/js/prologue.js
@@ -126,6 +126,18 @@
 }
 
 
+function OverrideFunction(object, name, f, afterInitialBootstrap) {
+  %CheckIsBootstrapping();
+  %ObjectDefineProperty(object, name, { value: f,
+                                        writeable: true,
+                                        configurable: true,
+                                        enumerable: false });
+  SetFunctionName(f, name);
+  if (!afterInitialBootstrap) %FunctionRemovePrototype(f);
+  %SetNativeFlag(f);
+}
+
+
 // 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
@@ -175,18 +187,26 @@
     "GetMethod",
     "IsNaN",
     "MakeError",
+    "MakeRangeError",
     "MakeTypeError",
     "MapEntries",
     "MapIterator",
     "MapIteratorNext",
     "MaxSimple",
     "MinSimple",
+    "NumberIsInteger",
     "ObjectDefineProperty",
     "ObserveArrayMethods",
     "ObserveObjectMethods",
     "PromiseChain",
     "PromiseDeferred",
     "PromiseResolved",
+    "RegExpSubclassExecJS",
+    "RegExpSubclassMatch",
+    "RegExpSubclassReplace",
+    "RegExpSubclassSearch",
+    "RegExpSubclassSplit",
+    "RegExpSubclassTest",
     "SetIterator",
     "SetIteratorNext",
     "SetValues",
@@ -206,6 +226,10 @@
     "to_string_tag_symbol",
     "object_to_string",
     "species_symbol",
+    "match_symbol",
+    "replace_symbol",
+    "search_symbol",
+    "split_symbol",
   ];
 
   var filtered_exports = {};
@@ -284,6 +308,7 @@
 utils.InstallFunctions = InstallFunctions;
 utils.InstallGetter = InstallGetter;
 utils.InstallGetterSetter = InstallGetterSetter;
+utils.OverrideFunction = OverrideFunction;
 utils.SetUpLockedPrototype = SetUpLockedPrototype;
 utils.PostNatives = PostNatives;
 utils.PostExperimentals = PostExperimentals;
@@ -323,14 +348,14 @@
 // 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);
+  return function(...args) {
+    return %reflect_apply(func, thisArg, args);
   };
 };
 
 extrasUtils.uncurryThis = function uncurryThis(func) {
-  return function(thisArg) {
-    return %Apply(func, thisArg, arguments, 1, arguments.length - 1);
+  return function(thisArg, ...args) {
+    return %reflect_apply(func, thisArg, args);
   };
 };
 
diff --git a/src/js/promise.js b/src/js/promise.js
index 8cf6a36..bcf826a 100644
--- a/src/js/promise.js
+++ b/src/js/promise.js
@@ -61,13 +61,13 @@
 
 var GlobalPromise = function Promise(resolver) {
   if (resolver === promiseRawSymbol) {
-    return %NewObject(GlobalPromise, new.target);
+    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 promise = PromiseInit(%_NewObject(GlobalPromise, new.target));
   var callbacks = CreateResolvingFunctions(promise);
 
   try {
@@ -89,9 +89,6 @@
   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;
 }
 
@@ -217,8 +214,6 @@
   PromiseDone(promise, -1, r, promiseOnRejectSymbol)
 }
 
-// Convenience.
-
 function NewPromiseCapability(C) {
   if (C === GlobalPromise) {
     // Optimized case, avoid extra closure.
@@ -239,6 +234,9 @@
     result.reject = reject;
   });
 
+  if (!IS_CALLABLE(result.resolve) || !IS_CALLABLE(result.reject))
+      throw MakeTypeError(kPromiseNonCallable);
+
   return result;
 }
 
@@ -305,9 +303,6 @@
   }
   // Mark this promise as having handler.
   SET_PRIVATE(this, promiseHasHandlerSymbol, true);
-  if (DEBUG_IS_ACTIVE) {
-    %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this });
-  }
   return deferred.promise;
 }
 
diff --git a/src/js/regexp.js b/src/js/regexp.js
index e80d019..cc8cb41 100644
--- a/src/js/regexp.js
+++ b/src/js/regexp.js
@@ -4,26 +4,37 @@
 
 (function(global, utils) {
 
+'use strict';
+
 %CheckIsBootstrapping();
 
 // -------------------------------------------------------------------
 // Imports
 
+var AddIndexedProperty;
 var ExpandReplacement;
+var GlobalArray = global.Array;
 var GlobalObject = global.Object;
 var GlobalRegExp = global.RegExp;
 var GlobalRegExpPrototype;
 var InternalArray = utils.InternalArray;
 var InternalPackedArray = utils.InternalPackedArray;
 var MakeTypeError;
+var MaxSimple;
+var MinSimple;
 var matchSymbol = utils.ImportNow("match_symbol");
 var replaceSymbol = utils.ImportNow("replace_symbol");
 var searchSymbol = utils.ImportNow("search_symbol");
 var splitSymbol = utils.ImportNow("split_symbol");
+var SpeciesConstructor;
 
 utils.Import(function(from) {
+  AddIndexedProperty = from.AddIndexedProperty;
   ExpandReplacement = from.ExpandReplacement;
   MakeTypeError = from.MakeTypeError;
+  MaxSimple = from.MaxSimple;
+  MinSimple = from.MinSimple;
+  SpeciesConstructor = from.SpeciesConstructor;
 });
 
 // -------------------------------------------------------------------
@@ -44,6 +55,7 @@
 
 // -------------------------------------------------------------------
 
+// ES#sec-isregexp IsRegExp ( argument )
 function IsRegExp(o) {
   if (!IS_RECEIVER(o)) return false;
   var is_regexp = o[matchSymbol];
@@ -52,7 +64,8 @@
 }
 
 
-// ES6 section 21.2.3.2.2
+// ES#sec-regexpinitialize
+// Runtime Semantics: RegExpInitialize ( obj, pattern, flags )
 function RegExpInitialize(object, pattern, flags) {
   pattern = IS_UNDEFINED(pattern) ? '' : TO_STRING(pattern);
   flags = IS_UNDEFINED(flags) ? '' : TO_STRING(flags);
@@ -70,6 +83,8 @@
 }
 
 
+// ES#sec-regexp-pattern-flags
+// RegExp ( pattern, flags )
 function RegExpConstructor(pattern, flags) {
   var newtarget = new.target;
   var pattern_is_regexp = IsRegExp(pattern);
@@ -94,11 +109,12 @@
     if (IS_UNDEFINED(flags)) flags = input_pattern.flags;
   }
 
-  var object = %NewObject(GlobalRegExp, newtarget);
+  var object = %_NewObject(GlobalRegExp, newtarget);
   return RegExpInitialize(object, pattern, flags);
 }
 
 
+// ES#sec-regexp.prototype.compile RegExp.prototype.compile (pattern, flags)
 function RegExpCompileJS(pattern, flags) {
   if (!IS_REGEXP(this)) {
     throw MakeTypeError(kIncompatibleMethodReceiver,
@@ -163,6 +179,54 @@
 }
 
 
+// ES#sec-regexp.prototype.exec
+// RegExp.prototype.exec ( string )
+function RegExpSubclassExecJS(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(lastIndex);
+
+  var global = TO_BOOLEAN(REGEXP_GLOBAL(this));
+  var sticky = TO_BOOLEAN(REGEXP_STICKY(this));
+  var updateLastIndex = global || sticky;
+  if (updateLastIndex) {
+    if (i > string.length) {
+      this.lastIndex = 0;
+      return null;
+    }
+  } else {
+    i = 0;
+  }
+
+  // matchIndices is either null or the RegExpLastMatchInfo array.
+  // TODO(littledan): Whether a RegExp is sticky is compiled into the RegExp
+  // itself, but ES2015 allows monkey-patching this property to differ from
+  // the internal flags. If it differs, recompile a different RegExp?
+  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);
+}
+%FunctionRemovePrototype(RegExpSubclassExecJS);
+
+
+// Legacy implementation of RegExp.prototype.exec
 function RegExpExecJS(string) {
   if (!IS_REGEXP(this)) {
     throw MakeTypeError(kIncompatibleMethodReceiver,
@@ -202,10 +266,30 @@
 }
 
 
+// ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S )
+// Also takes an optional exec method in case our caller
+// has already fetched exec.
+function RegExpSubclassExec(regexp, string, exec) {
+  if (IS_UNDEFINED(exec)) {
+    exec = regexp.exec;
+  }
+  if (IS_CALLABLE(exec)) {
+    var result = %_Call(exec, regexp, string);
+    if (!IS_RECEIVER(result) && !IS_NULL(result)) {
+      throw MakeTypeError(kInvalidRegExpExecResult);
+    }
+    return result;
+  }
+  return %_Call(RegExpExecJS, regexp, string);
+}
+%SetForceInlineFlag(RegExpSubclassExec);
+
+
 // One-element cache for the simplified test regexp.
 var regexp_key;
 var regexp_val;
 
+// Legacy implementation of RegExp.prototype.test
 // 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
@@ -259,6 +343,19 @@
   }
 }
 
+
+// ES#sec-regexp.prototype.test RegExp.prototype.test ( S )
+function RegExpSubclassTest(string) {
+  if (!IS_RECEIVER(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        'RegExp.prototype.test', this);
+  }
+  string = TO_STRING(string);
+  var match = RegExpSubclassExec(this, string);
+  return !IS_NULL(match);
+}
+%FunctionRemovePrototype(RegExpSubclassTest);
+
 function TrimRegExp(regexp) {
   if (regexp_key !== regexp) {
     regexp_key = regexp;
@@ -273,27 +370,14 @@
 
 
 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 '/(?:)/';
-    }
-    if (!IS_RECEIVER(this)) {
-      throw MakeTypeError(
-          kIncompatibleMethodReceiver, 'RegExp.prototype.toString', this);
-    }
-    return '/' + TO_STRING(this.source) + '/' + TO_STRING(this.flags);
+  if (!IS_RECEIVER(this)) {
+    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;
+  if (this === GlobalRegExpPrototype) {
+    %IncrementUseCounter(kRegExpPrototypeToString);
+  }
+  return '/' + TO_STRING(this.source) + '/' + TO_STRING(this.flags);
 }
 
 
@@ -306,7 +390,8 @@
 }
 
 
-// ES6 21.2.5.11.
+// Legacy implementation of RegExp.prototype[Symbol.split] which
+// doesn't properly call the underlying exec, @@species methods
 function RegExpSplit(string, limit) {
   // TODO(yangguo): allow non-regexp receivers.
   if (!IS_REGEXP(this)) {
@@ -380,9 +465,85 @@
 }
 
 
-// ES6 21.2.5.6.
+// ES#sec-regexp.prototype-@@split
+// RegExp.prototype [ @@split ] ( string, limit )
+function RegExpSubclassSplit(string, limit) {
+  if (!IS_RECEIVER(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        "RegExp.prototype.@@split", this);
+  }
+  string = TO_STRING(string);
+  var constructor = SpeciesConstructor(this, GlobalRegExp);
+  var flags = TO_STRING(this.flags);
+
+  // TODO(adamk): this fast path is wrong with respect to this.global
+  // and this.sticky, but hopefully the spec will remove those gets
+  // and thus make the assumption of 'exec' having no side-effects
+  // more correct. Also, we doesn't ensure that 'exec' is actually
+  // a data property on RegExp.prototype.
+  var exec;
+  if (IS_REGEXP(this) && constructor === GlobalRegExp) {
+    exec = this.exec;
+    if (exec === RegExpSubclassExecJS) {
+      return %_Call(RegExpSplit, this, string, limit);
+    }
+  }
+
+  var unicode = %StringIndexOf(flags, 'u', 0) >= 0;
+  var sticky = %StringIndexOf(flags, 'y', 0) >= 0;
+  var newFlags = sticky ? flags : flags + "y";
+  var splitter = new constructor(this, newFlags);
+  var array = new GlobalArray();
+  var arrayIndex = 0;
+  var lim = (IS_UNDEFINED(limit)) ? kMaxUint32 : TO_UINT32(limit);
+  var size = string.length;
+  var prevStringIndex = 0;
+  if (lim === 0) return array;
+  var result;
+  if (size === 0) {
+    result = RegExpSubclassExec(splitter, string);
+    if (IS_NULL(result)) AddIndexedProperty(array, 0, string);
+    return array;
+  }
+  var stringIndex = prevStringIndex;
+  while (stringIndex < size) {
+    splitter.lastIndex = stringIndex;
+    result = RegExpSubclassExec(splitter, string, exec);
+    // Ensure exec will be read again on the next loop through.
+    exec = UNDEFINED;
+    if (IS_NULL(result)) {
+      stringIndex += AdvanceStringIndex(string, stringIndex, unicode);
+    } else {
+      var end = MinSimple(TO_LENGTH(splitter.lastIndex), size);
+      if (end === stringIndex) {
+        stringIndex += AdvanceStringIndex(string, stringIndex, unicode);
+      } else {
+        AddIndexedProperty(
+            array, arrayIndex,
+            %_SubString(string, prevStringIndex, stringIndex));
+        arrayIndex++;
+        if (arrayIndex === lim) return array;
+        prevStringIndex = end;
+        var numberOfCaptures = MaxSimple(TO_LENGTH(result.length), 0);
+        for (var i = 1; i < numberOfCaptures; i++) {
+          AddIndexedProperty(array, arrayIndex, result[i]);
+          arrayIndex++;
+          if (arrayIndex === lim) return array;
+        }
+        stringIndex = prevStringIndex;
+      }
+    }
+  }
+  AddIndexedProperty(array, arrayIndex,
+                     %_SubString(string, prevStringIndex, size));
+  return array;
+}
+%FunctionRemovePrototype(RegExpSubclassSplit);
+
+
+// Legacy implementation of RegExp.prototype[Symbol.match] which
+// doesn't properly call the underlying exec method
 function RegExpMatch(string) {
-  // TODO(yangguo): allow non-regexp receivers.
   if (!IS_REGEXP(this)) {
     throw MakeTypeError(kIncompatibleMethodReceiver,
                         "RegExp.prototype.@@match", this);
@@ -396,7 +557,41 @@
 }
 
 
-// ES6 21.2.5.8.
+// ES#sec-regexp.prototype-@@match
+// RegExp.prototype [ @@match ] ( string )
+function RegExpSubclassMatch(string) {
+  if (!IS_RECEIVER(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        "RegExp.prototype.@@match", this);
+  }
+  string = TO_STRING(string);
+  var global = this.global;
+  if (!global) return RegExpSubclassExec(this, string);
+  var unicode = this.unicode;
+  this.lastIndex = 0;
+  var array = new InternalArray();
+  var n = 0;
+  var result;
+  while (true) {
+    result = RegExpSubclassExec(this, string);
+    if (IS_NULL(result)) {
+      if (n === 0) return null;
+      break;
+    }
+    var matchStr = TO_STRING(result[0]);
+    array[n] = matchStr;
+    if (matchStr === "") SetAdvancedStringIndex(this, string, unicode);
+    n++;
+  }
+  var resultArray = [];
+  %MoveArrayContents(array, resultArray);
+  return resultArray;
+}
+%FunctionRemovePrototype(RegExpSubclassMatch);
+
+
+// Legacy implementation of RegExp.prototype[Symbol.replace] which
+// doesn't properly call the underlying exec method.
 
 // TODO(lrn): This array will survive indefinitely if replace is never
 // called again. However, it will be empty, since the contents are cleared
@@ -458,7 +653,7 @@
       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);
+        var func_result = %reflect_apply(replace, UNDEFINED, elem);
         // Overwrite the i'th element in the results with the string we got
         // back from the callback function.
         res[i] = TO_STRING(func_result);
@@ -512,7 +707,7 @@
     parameters[j] = index;
     parameters[j + 1] = subject;
 
-    replacement = %Apply(replace, UNDEFINED, parameters, 0, j + 2);
+    replacement = %reflect_apply(replace, UNDEFINED, parameters);
   }
 
   result += replacement;  // The add method converts to string if necessary.
@@ -523,7 +718,6 @@
 
 
 function RegExpReplace(string, replace) {
-  // TODO(littledan): allow non-regexp receivers.
   if (!IS_REGEXP(this)) {
     throw MakeTypeError(kIncompatibleMethodReceiver,
                         "RegExp.prototype.@@replace", this);
@@ -565,9 +759,206 @@
 }
 
 
-// ES6 21.2.5.9.
+// ES#sec-getsubstitution
+// GetSubstitution(matched, str, position, captures, replacement)
+// Expand the $-expressions in the string and return a new string with
+// the result.
+// TODO(littledan): Call this function from String.prototype.replace instead
+// of the very similar ExpandReplacement in src/js/string.js
+function GetSubstitution(matched, string, position, captures, replacement) {
+  var matchLength = matched.length;
+  var stringLength = string.length;
+  var capturesLength = captures.length;
+  var tailPos = position + matchLength;
+  var result = "";
+  var pos, expansion, peek, next, scaledIndex, advance, newScaledIndex;
+
+  var next = %StringIndexOf(replacement, '$', 0);
+  if (next < 0) {
+    result += replacement;
+    return result;
+  }
+
+  if (next > 0) result += %_SubString(replacement, 0, next);
+
+  while (true) {
+    expansion = '$';
+    pos = next + 1;
+    if (pos < replacement.length) {
+      peek = %_StringCharCodeAt(replacement, pos);
+      if (peek == 36) {         // $$
+        ++pos;
+        result += '$';
+      } else if (peek == 38) {  // $& - match
+        ++pos;
+        result += matched;
+      } else if (peek == 96) {  // $` - prefix
+        ++pos;
+        result += %_SubString(string, 0, position);
+      } else if (peek == 39) {  // $' - suffix
+        ++pos;
+        result += %_SubString(string, tailPos, stringLength);
+      } else if (peek >= 48 && peek <= 57) {
+        // Valid indices are $1 .. $9, $01 .. $09 and $10 .. $99
+        scaledIndex = (peek - 48);
+        advance = 1;
+        if (pos + 1 < replacement.length) {
+          next = %_StringCharCodeAt(replacement, pos + 1);
+          if (next >= 48 && next <= 57) {
+            newScaledIndex = scaledIndex * 10 + ((next - 48));
+            if (newScaledIndex < capturesLength) {
+              scaledIndex = newScaledIndex;
+              advance = 2;
+            }
+          }
+        }
+        if (scaledIndex != 0 && scaledIndex < capturesLength) {
+          var capture = captures[scaledIndex];
+          if (!IS_UNDEFINED(capture)) result += capture;
+          pos += advance;
+        } else {
+          result += '$';
+        }
+      } else {
+        result += '$';
+      }
+    } else {
+      result += '$';
+    }
+
+    // Go the the next $ in the replacement.
+    next = %StringIndexOf(replacement, '$', pos);
+
+    // Return if there are no more $ characters in the replacement. If we
+    // haven't reached the end, we need to append the suffix.
+    if (next < 0) {
+      if (pos < replacement.length) {
+        result += %_SubString(replacement, pos, replacement.length);
+      }
+      return result;
+    }
+
+    // Append substring between the previous and the next $ character.
+    if (next > pos) {
+      result += %_SubString(replacement, pos, next);
+    }
+  }
+  return result;
+}
+
+
+// ES#sec-advancestringindex
+// AdvanceStringIndex ( S, index, unicode )
+function AdvanceStringIndex(string, index, unicode) {
+  var increment = 1;
+  if (unicode) {
+    var first = %_StringCharCodeAt(string, index);
+    if (first >= 0xD800 && first <= 0xDBFF && string.length > index + 1) {
+      var second = %_StringCharCodeAt(string, index + 1);
+      if (second >= 0xDC00 && second <= 0xDFFF) {
+        increment = 2;
+      }
+    }
+  }
+  return increment;
+}
+
+
+function SetAdvancedStringIndex(regexp, string, unicode) {
+  var lastIndex = regexp.lastIndex;
+  regexp.lastIndex = lastIndex +
+                     AdvanceStringIndex(string, lastIndex, unicode);
+}
+
+
+// ES#sec-regexp.prototype-@@replace
+// RegExp.prototype [ @@replace ] ( string, replaceValue )
+function RegExpSubclassReplace(string, replace) {
+  if (!IS_RECEIVER(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        "RegExp.prototype.@@replace", this);
+  }
+  string = TO_STRING(string);
+  var length = string.length;
+  var functionalReplace = IS_CALLABLE(replace);
+  if (!functionalReplace) replace = TO_STRING(replace);
+  var global = TO_BOOLEAN(this.global);
+  if (global) {
+    var unicode = TO_BOOLEAN(this.unicode);
+    this.lastIndex = 0;
+  }
+
+  // TODO(adamk): this fast path is wrong with respect to this.global
+  // and this.sticky, but hopefully the spec will remove those gets
+  // and thus make the assumption of 'exec' having no side-effects
+  // more correct. Also, we doesn't ensure that 'exec' is actually
+  // a data property on RegExp.prototype, nor does the fast path
+  // correctly handle lastIndex setting.
+  var exec;
+  if (IS_REGEXP(this)) {
+    exec = this.exec;
+    if (exec === RegExpSubclassExecJS) {
+      return %_Call(RegExpReplace, this, string, replace);
+    }
+  }
+
+  var results = new InternalArray();
+  var result, replacement;
+  while (true) {
+    result = RegExpSubclassExec(this, string, exec);
+    // Ensure exec will be read again on the next loop through.
+    exec = UNDEFINED;
+    if (IS_NULL(result)) {
+      break;
+    } else {
+      results.push(result);
+      if (!global) break;
+      var matchStr = TO_STRING(result[0]);
+      if (matchStr === "") SetAdvancedStringIndex(this, string, unicode);
+    }
+  }
+  var accumulatedResult = "";
+  var nextSourcePosition = 0;
+  for (var i = 0; i < results.length; i++) {
+    result = results[i];
+    var capturesLength = MaxSimple(TO_LENGTH(result.length), 0);
+    var matched = TO_STRING(result[0]);
+    var matchedLength = matched.length;
+    var position = MaxSimple(MinSimple(TO_INTEGER(result.index), length), 0);
+    var captures = new InternalArray();
+    for (var n = 0; n < capturesLength; n++) {
+      var capture = result[n];
+      if (!IS_UNDEFINED(capture)) capture = TO_STRING(capture);
+      captures[n] = capture;
+    }
+    if (functionalReplace) {
+      var parameters = new InternalArray(capturesLength + 2);
+      for (var j = 0; j < capturesLength; j++) {
+        parameters[j] = captures[j];
+      }
+      parameters[j] = position;
+      parameters[j + 1] = string;
+      replacement = %reflect_apply(replace, UNDEFINED, parameters, 0,
+                                   parameters.length);
+    } else {
+      replacement = GetSubstitution(matched, string, position, captures,
+                                    replace);
+    }
+    if (position >= nextSourcePosition) {
+      accumulatedResult +=
+        %_SubString(string, nextSourcePosition, position) + replacement;
+      nextSourcePosition = position + matchedLength;
+    }
+  }
+  if (nextSourcePosition >= length) return accumulatedResult;
+  return accumulatedResult + %_SubString(string, nextSourcePosition, length);
+}
+%FunctionRemovePrototype(RegExpSubclassReplace);
+
+
+// Legacy implementation of RegExp.prototype[Symbol.search] which
+// doesn't properly use the overridden exec method
 function RegExpSearch(string) {
-  // TODO(yangguo): allow non-regexp receivers.
   if (!IS_REGEXP(this)) {
     throw MakeTypeError(kIncompatibleMethodReceiver,
                         "RegExp.prototype.@@search", this);
@@ -578,6 +969,24 @@
 }
 
 
+// ES#sec-regexp.prototype-@@search
+// RegExp.prototype [ @@search ] ( string )
+function RegExpSubclassSearch(string) {
+  if (!IS_RECEIVER(this)) {
+    throw MakeTypeError(kIncompatibleMethodReceiver,
+                        "RegExp.prototype.@@search", this);
+  }
+  string = TO_STRING(string);
+  var previousLastIndex = this.lastIndex;
+  this.lastIndex = 0;
+  var result = RegExpSubclassExec(this, string);
+  this.lastIndex = previousLastIndex;
+  if (IS_NULL(result)) return -1;
+  return result.index;
+}
+%FunctionRemovePrototype(RegExpSubclassSearch);
+
+
 // 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
@@ -639,19 +1048,35 @@
 }
 
 
+// ES6 21.2.5.3.
+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.4.
 function RegExpGetGlobal() {
   if (!IS_REGEXP(this)) {
     // TODO(littledan): Remove this RegExp compat workaround
     if (this === GlobalRegExpPrototype) {
+      %IncrementUseCounter(kRegExpPrototypeOldFlagGetter);
       return UNDEFINED;
     }
     throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.global");
   }
-  return !!REGEXP_GLOBAL(this);
+  return TO_BOOLEAN(REGEXP_GLOBAL(this));
 }
-%FunctionSetName(RegExpGetGlobal, "RegExp.prototype.global");
-%SetNativeFlag(RegExpGetGlobal);
+%SetForceInlineFlag(RegExpGetGlobal);
 
 
 // ES6 21.2.5.5.
@@ -659,14 +1084,13 @@
   if (!IS_REGEXP(this)) {
     // TODO(littledan): Remove this RegExp compat workaround
     if (this === GlobalRegExpPrototype) {
+      %IncrementUseCounter(kRegExpPrototypeOldFlagGetter);
       return UNDEFINED;
     }
     throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.ignoreCase");
   }
-  return !!REGEXP_IGNORE_CASE(this);
+  return TO_BOOLEAN(REGEXP_IGNORE_CASE(this));
 }
-%FunctionSetName(RegExpGetIgnoreCase, "RegExp.prototype.ignoreCase");
-%SetNativeFlag(RegExpGetIgnoreCase);
 
 
 // ES6 21.2.5.7.
@@ -674,14 +1098,13 @@
   if (!IS_REGEXP(this)) {
     // TODO(littledan): Remove this RegExp compat workaround
     if (this === GlobalRegExpPrototype) {
+      %IncrementUseCounter(kRegExpPrototypeOldFlagGetter);
       return UNDEFINED;
     }
     throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.multiline");
   }
-  return !!REGEXP_MULTILINE(this);
+  return TO_BOOLEAN(REGEXP_MULTILINE(this));
 }
-%FunctionSetName(RegExpGetMultiline, "RegExp.prototype.multiline");
-%SetNativeFlag(RegExpGetMultiline);
 
 
 // ES6 21.2.5.10.
@@ -689,14 +1112,29 @@
   if (!IS_REGEXP(this)) {
     // TODO(littledan): Remove this RegExp compat workaround
     if (this === GlobalRegExpPrototype) {
-      return UNDEFINED;
+      %IncrementUseCounter(kRegExpPrototypeSourceGetter);
+      return "(?:)";
     }
     throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.source");
   }
   return REGEXP_SOURCE(this);
 }
-%FunctionSetName(RegExpGetSource, "RegExp.prototype.source");
-%SetNativeFlag(RegExpGetSource);
+
+
+// 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 TO_BOOLEAN(REGEXP_STICKY(this));
+}
+%SetForceInlineFlag(RegExpGetSticky);
 
 // -------------------------------------------------------------------
 
@@ -718,10 +1156,12 @@
   splitSymbol, RegExpSplit,
 ]);
 
+utils.InstallGetter(GlobalRegExp.prototype, 'flags', RegExpGetFlags);
 utils.InstallGetter(GlobalRegExp.prototype, 'global', RegExpGetGlobal);
 utils.InstallGetter(GlobalRegExp.prototype, 'ignoreCase', RegExpGetIgnoreCase);
 utils.InstallGetter(GlobalRegExp.prototype, 'multiline', RegExpGetMultiline);
 utils.InstallGetter(GlobalRegExp.prototype, 'source', RegExpGetSource);
+utils.InstallGetter(GlobalRegExp.prototype, 'sticky', RegExpGetSticky);
 
 // 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.
@@ -769,12 +1209,39 @@
 %ToFastProperties(GlobalRegExp);
 
 // -------------------------------------------------------------------
+// Internal
+
+var InternalRegExpMatchInfo = new InternalPackedArray(2, "", UNDEFINED, 0, 0);
+
+function InternalRegExpMatch(regexp, subject) {
+  var matchInfo = %_RegExpExec(regexp, subject, 0, InternalRegExpMatchInfo);
+  if (!IS_NULL(matchInfo)) {
+    RETURN_NEW_RESULT_FROM_MATCH_INFO(matchInfo, subject);
+  }
+  return null;
+}
+
+function InternalRegExpReplace(regexp, subject, replacement) {
+  return %StringReplaceGlobalRegExpWithString(
+      subject, regexp, replacement, InternalRegExpMatchInfo);
+}
+
+// -------------------------------------------------------------------
 // Exports
 
 utils.Export(function(to) {
+  to.InternalRegExpMatch = InternalRegExpMatch;
+  to.InternalRegExpReplace = InternalRegExpReplace;
+  to.IsRegExp = IsRegExp;
   to.RegExpExec = DoRegExpExec;
-  to.RegExpExecNoTests = RegExpExecNoTests;
+  to.RegExpInitialize = RegExpInitialize;
   to.RegExpLastMatchInfo = RegExpLastMatchInfo;
+  to.RegExpSubclassExecJS = RegExpSubclassExecJS;
+  to.RegExpSubclassMatch = RegExpSubclassMatch;
+  to.RegExpSubclassReplace = RegExpSubclassReplace;
+  to.RegExpSubclassSearch = RegExpSubclassSearch;
+  to.RegExpSubclassSplit = RegExpSubclassSplit;
+  to.RegExpSubclassTest = RegExpSubclassTest;
   to.RegExpTest = RegExpTest;
 });
 
diff --git a/src/js/runtime.js b/src/js/runtime.js
index 7a61094..8e4f283 100644
--- a/src/js/runtime.js
+++ b/src/js/runtime.js
@@ -42,14 +42,6 @@
    ---------------------------------
 */
 
-function ConcatIterableToArray(target, iterable) {
-   var index = target.length;
-   for (var element of iterable) {
-     AddIndexedProperty(target, index++, element);
-   }
-   return target;
-}
-
 
 // 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
@@ -137,8 +129,4 @@
   to.SpeciesConstructor = SpeciesConstructor;
 });
 
-%InstallToContext([
-  "concat_iterable_to_array", ConcatIterableToArray,
-]);
-
 })
diff --git a/src/js/string-iterator.js b/src/js/string-iterator.js
index 3c331dd..af9af31 100644
--- a/src/js/string-iterator.js
+++ b/src/js/string-iterator.js
@@ -32,6 +32,7 @@
 
 // 21.1.5.1 CreateStringIterator Abstract Operation
 function CreateStringIterator(string) {
+  CHECK_OBJECT_COERCIBLE(string, 'String.prototype[Symbol.iterator]');
   var s = TO_STRING(string);
   var iterator = new StringIterator;
   SET_PRIVATE(iterator, stringIteratorIteratedStringSymbol, s);
diff --git a/src/js/string.js b/src/js/string.js
index a401978..0eb394e 100644
--- a/src/js/string.js
+++ b/src/js/string.js
@@ -15,12 +15,13 @@
 var GlobalString = global.String;
 var InternalArray = utils.InternalArray;
 var InternalPackedArray = utils.InternalPackedArray;
+var IsRegExp;
 var MakeRangeError;
 var MakeTypeError;
 var MaxSimple;
 var MinSimple;
+var RegExpInitialize;
 var matchSymbol = utils.ImportNow("match_symbol");
-var RegExpExecNoTests;
 var replaceSymbol = utils.ImportNow("replace_symbol");
 var searchSymbol = utils.ImportNow("search_symbol");
 var splitSymbol = utils.ImportNow("split_symbol");
@@ -28,11 +29,12 @@
 utils.Import(function(from) {
   ArrayIndexOf = from.ArrayIndexOf;
   ArrayJoin = from.ArrayJoin;
+  IsRegExp = from.IsRegExp;
   MakeRangeError = from.MakeRangeError;
   MakeTypeError = from.MakeTypeError;
   MaxSimple = from.MaxSimple;
   MinSimple = from.MinSimple;
-  RegExpExecNoTests = from.RegExpExecNoTests;
+  RegExpInitialize = from.RegExpInitialize;
 });
 
 //-------------------------------------------------------------------
@@ -159,9 +161,10 @@
 
   var subject = TO_STRING(this);
 
-  // Non-regexp argument.
-  var regexp = new GlobalRegExp(pattern);
-  return RegExpExecNoTests(regexp, subject, 0);
+  // Equivalent to RegExpCreate (ES#sec-regexpcreate)
+  var regexp = %_NewObject(GlobalRegExp, GlobalRegExp);
+  RegExpInitialize(regexp, pattern);
+  return regexp[matchSymbol](subject);
 }
 
 
@@ -355,7 +358,10 @@
   }
 
   var subject = TO_STRING(this);
-  var regexp = new GlobalRegExp(pattern);
+
+  // Equivalent to RegExpCreate (ES#sec-regexpcreate)
+  var regexp = %_NewObject(GlobalRegExp, GlobalRegExp);
+  RegExpInitialize(regexp, pattern);
   return %_Call(regexp[searchSymbol], regexp, subject);
 }
 
@@ -558,18 +564,6 @@
 }
 
 
-// ECMA-262, section 15.5.3.2
-function StringFromCharCode(_) {  // length == 1
-  "use strict";
-  var s = "";
-  var n = arguments.length;
-  for (var i = 0; i < n; ++i) {
-    s += %_StringCharFromCode(arguments[i] & 0xffff);
-  }
-  return s;
-}
-
-
 // ES6 draft, revision 26 (2014-07-18), section B.2.3.2.1
 function HtmlEscape(str) {
   return %_Call(StringReplace, TO_STRING(str), /"/g, "&quot;");
@@ -701,7 +695,7 @@
 
   var s = TO_STRING(this);
 
-  if (IS_REGEXP(searchString)) {
+  if (IsRegExp(searchString)) {
     throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.startsWith");
   }
 
@@ -727,7 +721,7 @@
 
   var s = TO_STRING(this);
 
-  if (IS_REGEXP(searchString)) {
+  if (IsRegExp(searchString)) {
     throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.endsWith");
   }
 
@@ -754,7 +748,7 @@
 
   var string = TO_STRING(this);
 
-  if (IS_REGEXP(searchString)) {
+  if (IsRegExp(searchString)) {
     throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.includes");
   }
 
@@ -860,7 +854,6 @@
 
 // Set up the non-enumerable functions on the String object.
 utils.InstallFunctions(GlobalString, DONT_ENUM, [
-  "fromCharCode", StringFromCharCode,
   "fromCodePoint", StringFromCodePoint,
   "raw", StringRaw
 ]);
diff --git a/src/js/symbol.js b/src/js/symbol.js
index ae54369..7365655 100644
--- a/src/js/symbol.js
+++ b/src/js/symbol.js
@@ -84,9 +84,7 @@
   // "search", searchSymbol,
   // "split, splitSymbol,
   "toPrimitive", toPrimitiveSymbol,
-  // TODO(dslomov, caitp): Currently defined in harmony-tostring.js ---
-  // Move here when shipping
-  // "toStringTag", toStringTagSymbol,
+  "toStringTag", toStringTagSymbol,
   "unscopables", unscopablesSymbol,
 ]);
 
diff --git a/src/js/typedarray.js b/src/js/typedarray.js
index 3d500a3..4fb174b 100644
--- a/src/js/typedarray.js
+++ b/src/js/typedarray.js
@@ -11,11 +11,15 @@
 // -------------------------------------------------------------------
 // Imports
 
-var ArrayFrom;
-var ArrayToString;
+var AddIndexedProperty;
+// array.js has to come before typedarray.js for this to work
+var ArrayToString = utils.ImportNow("ArrayToString");
 var ArrayValues;
+var GetIterator;
+var GetMethod;
 var GlobalArray = global.Array;
 var GlobalArrayBuffer = global.ArrayBuffer;
+var GlobalArrayBufferPrototype = GlobalArrayBuffer.prototype;
 var GlobalDataView = global.DataView;
 var GlobalObject = global.Object;
 var InternalArray = utils.InternalArray;
@@ -67,9 +71,10 @@
 TYPED_ARRAYS(DECLARE_GLOBALS)
 
 utils.Import(function(from) {
-  ArrayFrom = from.ArrayFrom;
-  ArrayToString = from.ArrayToString;
+  AddIndexedProperty = from.AddIndexedProperty;
   ArrayValues = from.ArrayValues;
+  GetIterator = from.GetIterator;
+  GetMethod = from.GetMethod;
   InnerArrayCopyWithin = from.InnerArrayCopyWithin;
   InnerArrayEvery = from.InnerArrayEvery;
   InnerArrayFill = from.InnerArrayFill;
@@ -118,7 +123,7 @@
   } else {
     var newTypedArray = new constructor(arg0, arg1, arg2);
   }
-  if (!%_IsTypedArray(newTypedArray)) throw MakeTypeError(kNotTypedArray);
+  if (!IS_TYPEDARRAY(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.
@@ -195,8 +200,7 @@
   }
 }
 
-function NAMEConstructByArrayLike(obj, arrayLike) {
-  var length = arrayLike.length;
+function NAMEConstructByArrayLike(obj, arrayLike, length) {
   var l = ToPositiveInteger(length, kInvalidTypedArrayLength);
 
   if (l > %_MaxSmi()) {
@@ -236,7 +240,23 @@
   for (var value of newIterable) {
     list.push(value);
   }
-  NAMEConstructByArrayLike(obj, list);
+  NAMEConstructByArrayLike(obj, list, list.length);
+}
+
+// ES#sec-typedarray-typedarray TypedArray ( typedArray )
+function NAMEConstructByTypedArray(obj, typedArray) {
+  // TODO(littledan): Throw on detached typedArray
+  var srcData = %TypedArrayGetBuffer(typedArray);
+  var length = %_TypedArrayGetLength(typedArray);
+  var byteLength = %_ArrayBufferViewGetByteLength(typedArray);
+  var newByteLength = length * ELEMENT_SIZE;
+  NAMEConstructByArrayLike(obj, typedArray, length);
+  var bufferConstructor = SpeciesConstructor(srcData, GlobalArrayBuffer);
+  var prototype = bufferConstructor.prototype;
+  // TODO(littledan): Use the right prototype based on bufferConstructor's realm
+  if (IS_RECEIVER(prototype) && prototype !== GlobalArrayBufferPrototype) {
+    %InternalSetPrototype(%TypedArrayGetBuffer(obj), prototype);
+  }
 }
 
 function NAMEConstructor(arg1, arg2, arg3) {
@@ -246,14 +266,12 @@
     } else if (IS_NUMBER(arg1) || IS_STRING(arg1) ||
                IS_BOOLEAN(arg1) || IS_UNDEFINED(arg1)) {
       NAMEConstructByLength(this, arg1);
+    } else if (IS_TYPEDARRAY(arg1)) {
+      NAMEConstructByTypedArray(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);
+        NAMEConstructByArrayLike(this, arg1, arg1.length);
       } else {
         NAMEConstructByIterable(this, arg1, iteratorFn);
       }
@@ -263,14 +281,6 @@
   }
 }
 
-// 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)) {
@@ -323,7 +333,7 @@
 %SetForceInlineFlag(TypedArraySubArray);
 
 function TypedArrayGetBuffer() {
-  if (!%_IsTypedArray(this)) {
+  if (!IS_TYPEDARRAY(this)) {
     throw MakeTypeError(kIncompatibleMethodReceiver,
                         "get TypedArray.prototype.buffer", this);
   }
@@ -332,7 +342,7 @@
 %SetForceInlineFlag(TypedArrayGetBuffer);
 
 function TypedArrayGetByteLength() {
-  if (!%_IsTypedArray(this)) {
+  if (!IS_TYPEDARRAY(this)) {
     throw MakeTypeError(kIncompatibleMethodReceiver,
                         "get TypedArray.prototype.byteLength", this);
   }
@@ -341,7 +351,7 @@
 %SetForceInlineFlag(TypedArrayGetByteLength);
 
 function TypedArrayGetByteOffset() {
-  if (!%_IsTypedArray(this)) {
+  if (!IS_TYPEDARRAY(this)) {
     throw MakeTypeError(kIncompatibleMethodReceiver,
                         "get TypedArray.prototype.byteOffset", this);
   }
@@ -350,7 +360,7 @@
 %SetForceInlineFlag(TypedArrayGetByteOffset);
 
 function TypedArrayGetLength() {
-  if (!%_IsTypedArray(this)) {
+  if (!IS_TYPEDARRAY(this)) {
     throw MakeTypeError(kIncompatibleMethodReceiver,
                         "get TypedArray.prototype.length", this);
   }
@@ -465,7 +475,7 @@
 %FunctionSetLength(TypedArraySet, 1);
 
 function TypedArrayGetToStringTag() {
-  if (!%_IsTypedArray(this)) return;
+  if (!IS_TYPEDARRAY(this)) return;
   var name = %_ClassOf(this);
   if (IS_UNDEFINED(name)) return;
   return name;
@@ -473,7 +483,7 @@
 
 
 function TypedArrayCopyWithin(target, start, end) {
-  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
 
   var length = %_TypedArrayGetLength(this);
 
@@ -485,7 +495,7 @@
 
 // ES6 draft 05-05-15, section 22.2.3.7
 function TypedArrayEvery(f, receiver) {
-  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
 
   var length = %_TypedArrayGetLength(this);
 
@@ -496,7 +506,7 @@
 
 // ES6 draft 08-24-14, section 22.2.3.12
 function TypedArrayForEach(f, receiver) {
-  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
 
   var length = %_TypedArrayGetLength(this);
 
@@ -507,7 +517,7 @@
 
 // ES6 draft 04-05-14 section 22.2.3.8
 function TypedArrayFill(value, start, end) {
-  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
 
   var length = %_TypedArrayGetLength(this);
 
@@ -518,7 +528,7 @@
 
 // ES6 draft 07-15-13, section 22.2.3.9
 function TypedArrayFilter(f, thisArg) {
-  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
 
   var length = %_TypedArrayGetLength(this);
   if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
@@ -536,7 +546,7 @@
 
 // ES6 draft 07-15-13, section 22.2.3.10
 function TypedArrayFind(predicate, thisArg) {
-  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
 
   var length = %_TypedArrayGetLength(this);
 
@@ -547,7 +557,7 @@
 
 // ES6 draft 07-15-13, section 22.2.3.11
 function TypedArrayFindIndex(predicate, thisArg) {
-  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
 
   var length = %_TypedArrayGetLength(this);
 
@@ -558,7 +568,7 @@
 
 // ES6 draft 05-18-15, section 22.2.3.21
 function TypedArrayReverse() {
-  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
 
   var length = %_TypedArrayGetLength(this);
 
@@ -586,7 +596,7 @@
 
 // ES6 draft 05-18-15, section 22.2.3.25
 function TypedArraySort(comparefn) {
-  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
 
   var length = %_TypedArrayGetLength(this);
 
@@ -600,7 +610,7 @@
 
 // ES6 section 22.2.3.13
 function TypedArrayIndexOf(element, index) {
-  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
 
   var length = %_TypedArrayGetLength(this);
   return InnerArrayIndexOf(this, element, index, length);
@@ -610,7 +620,7 @@
 
 // ES6 section 22.2.3.16
 function TypedArrayLastIndexOf(element, index) {
-  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
 
   var length = %_TypedArrayGetLength(this);
 
@@ -622,7 +632,7 @@
 
 // ES6 draft 07-15-13, section 22.2.3.18
 function TypedArrayMap(f, thisArg) {
-  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
 
   var length = %_TypedArrayGetLength(this);
   var result = TypedArraySpeciesCreate(this, length);
@@ -638,7 +648,7 @@
 
 // ES6 draft 05-05-15, section 22.2.3.24
 function TypedArraySome(f, receiver) {
-  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
 
   var length = %_TypedArrayGetLength(this);
 
@@ -649,7 +659,7 @@
 
 // ES6 section 22.2.3.27
 function TypedArrayToLocaleString() {
-  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
 
   var length = %_TypedArrayGetLength(this);
 
@@ -657,15 +667,9 @@
 }
 
 
-// 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);
+  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
 
   var length = %_TypedArrayGetLength(this);
 
@@ -675,7 +679,7 @@
 
 // ES6 draft 07-15-13, section 22.2.3.19
 function TypedArrayReduce(callback, current) {
-  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
 
   var length = %_TypedArrayGetLength(this);
   return InnerArrayReduce(callback, current, this, length,
@@ -686,7 +690,7 @@
 
 // ES6 draft 07-15-13, section 22.2.3.19
 function TypedArrayReduceRight(callback, current) {
-  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
 
   var length = %_TypedArrayGetLength(this);
   return InnerArrayReduceRight(callback, current, this, length,
@@ -696,7 +700,7 @@
 
 
 function TypedArraySlice(start, end) {
-  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
   var len = %_TypedArrayGetLength(this);
 
   var relativeStart = TO_INTEGER(start);
@@ -740,7 +744,7 @@
 
 // ES2016 draft, section 22.2.3.14
 function TypedArrayIncludes(searchElement, fromIndex) {
-  if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+  if (!IS_TYPEDARRAY(this)) throw MakeTypeError(kNotTypedArray);
 
   var length = %_TypedArrayGetLength(this);
 
@@ -760,14 +764,50 @@
 }
 
 
+// ES#sec-iterabletoarraylike Runtime Semantics: IterableToArrayLike( items )
+function IterableToArrayLike(items) {
+  var iterable = GetMethod(items, iteratorSymbol);
+  if (!IS_UNDEFINED(iterable)) {
+    var internal_array = new InternalArray();
+    var i = 0;
+    for (var value of
+         { [iteratorSymbol]() { return GetIterator(items, iterable) } }) {
+      internal_array[i] = value;
+      i++;
+    }
+    var array = [];
+    %MoveArrayContents(internal_array, array);
+    return array;
+  }
+  return TO_OBJECT(items);
+}
+
+
+// ES#sec-%typedarray%.from
+// %TypedArray%.from ( source [ , mapfn [ , thisArg ] ] )
 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);
+  if (!%IsConstructor(this)) throw MakeTypeError(kNotConstructor, this);
+  var mapping;
+  if (!IS_UNDEFINED(mapfn)) {
+    if (!IS_CALLABLE(mapfn)) throw MakeTypeError(kCalledNonCallable, this);
+    mapping = true;
+  } else {
+    mapping = false;
+  }
+  var arrayLike = IterableToArrayLike(source);
+  var length = TO_LENGTH(arrayLike.length);
+  var targetObject = TypedArrayCreate(this, length);
+  var value, mappedValue;
+  for (var i = 0; i < length; i++) {
+    value = arrayLike[i];
+    if (mapping) {
+      mappedValue = %_Call(mapfn, thisArg, value, i);
+    } else {
+      mappedValue = value;
+    }
+    targetObject[i] = mappedValue;
+  }
+  return targetObject;
 }
 %FunctionSetLength(TypedArrayFrom, 1);
 
@@ -785,7 +825,7 @@
 %FunctionSetPrototype(TypedArray, new GlobalObject());
 %AddNamedProperty(TypedArray.prototype,
                   "constructor", TypedArray, DONT_ENUM);
-utils.InstallFunctions(TypedArray, DONT_ENUM | DONT_DELETE | READ_ONLY, [
+utils.InstallFunctions(TypedArray, DONT_ENUM, [
   "from", TypedArrayFrom,
   "of", TypedArrayOf
 ]);
@@ -819,10 +859,12 @@
   "slice", TypedArraySlice,
   "some", TypedArraySome,
   "sort", TypedArraySort,
-  "toString", TypedArrayToString,
   "toLocaleString", TypedArrayToLocaleString
 ]);
 
+%AddNamedProperty(TypedArray.prototype, "toString", ArrayToString,
+                  DONT_ENUM);
+
 
 macro SETUP_TYPED_ARRAY(ARRAY_ID, NAME, ELEMENT_SIZE)
   %SetCode(GlobalNAME, NAMEConstructor);
@@ -838,9 +880,6 @@
   %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)
diff --git a/src/js/uri.js b/src/js/uri.js
index 712d7e6..dca83c9 100644
--- a/src/js/uri.js
+++ b/src/js/uri.js
@@ -15,7 +15,6 @@
 // Imports
 
 var GlobalObject = global.Object;
-var GlobalArray = global.Array;
 var InternalArray = utils.InternalArray;
 var MakeURIError;
 
@@ -76,7 +75,7 @@
   var x = (cc >> 12) & 0xF;
   var y = (cc >> 6) & 63;
   var z = cc & 63;
-  var octets = new GlobalArray(3);
+  var octets = new InternalArray(3);
   if (cc <= 0x007F) {
     octets[0] = cc;
   } else if (cc <= 0x07FF) {
@@ -96,7 +95,7 @@
   var x = cc1 & 3;
   var y = (cc2 >> 6) & 0xF;
   var z = cc2 & 63;
-  var octets = new GlobalArray(4);
+  var octets = new InternalArray(4);
   octets[0] = (u >> 2) + 240;
   octets[1] = (((u & 3) << 4) | w) + 128;
   octets[2] = ((x << 4) | y) + 128;
@@ -248,7 +247,7 @@
         var n = 0;
         while (((cc << ++n) & 0x80) != 0) { }
         if (n == 1 || n > 4) throw MakeURIError();
-        var octets = new GlobalArray(n);
+        var octets = new InternalArray(n);
         octets[0] = cc;
         if (k + 3 * (n - 1) >= uriLength) throw MakeURIError();
         for (var i = 1; i < n; i++) {
diff --git a/src/js/v8natives.js b/src/js/v8natives.js
index 5e1a825..5185c62 100644
--- a/src/js/v8natives.js
+++ b/src/js/v8natives.js
@@ -134,14 +134,6 @@
 }
 
 
-// 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;
@@ -581,11 +573,9 @@
         if (IsDataDescriptor(current) && IsDataDescriptor(desc)) {
           var currentIsWritable = current.isWritable();
           if (currentIsWritable != desc.isWritable()) {
-            if (!currentIsWritable || IS_STRONG(obj)) {
+            if (!currentIsWritable) {
               if (should_throw) {
-                throw currentIsWritable
-                    ? MakeTypeError(kStrongRedefineDisallowed, obj, p)
-                    : MakeTypeError(kRedefineDisallowed, p);
+                throw MakeTypeError(kRedefineDisallowed, p);
               } else {
                 return false;
               }
@@ -850,7 +840,6 @@
   "toString", ObjectToString,
   "toLocaleString", ObjectToLocaleString,
   "valueOf", ObjectValueOf,
-  "hasOwnProperty", ObjectHasOwnProperty,
   "isPrototypeOf", ObjectIsPrototypeOf,
   "propertyIsEnumerable", ObjectPropertyIsEnumerable,
   "__defineGetter__", ObjectDefineGetter,
@@ -1106,9 +1095,10 @@
   to.IsFinite = GlobalIsFinite;
   to.IsNaN = GlobalIsNaN;
   to.NumberIsNaN = NumberIsNaN;
+  to.NumberIsInteger = NumberIsInteger;
   to.ObjectDefineProperties = ObjectDefineProperties;
   to.ObjectDefineProperty = ObjectDefineProperty;
-  to.ObjectHasOwnProperty = ObjectHasOwnProperty;
+  to.ObjectHasOwnProperty = GlobalObject.prototype.hasOwnProperty;
 });
 
 %InstallToContext([