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/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;
 });