Commit 92a571e5 authored by littledan's avatar littledan Committed by Commit bot

Add ES2015 RegExp full subclassing semantics behind a flag

This patch implements ES2015 RegExp subclassing semantics, namely the
hardest part where RegExp.prototype.exec and certain flag getters can
be overridden in order to provide different behavior. This change is
hidden behind a new flag, --harmony-regexp-exec. The flag guards the
behavior by installing entirely different implementations of the
methods which follow the new semantics.

Preliminary performance tests show a 3-4x regression in the Octane
RegExp benchmark. The new code doesn't call out into several fast
paths that the old code supported, so this is expected.

The patch is tested mostly by test262, where most RegExp tests are fixed,
with the exception of deliberate spec violations for web compatibility,
and for the 'sticky' flag, which is not dynamically read by this patch
in all cases but rather statically compiled into the RegExp. The latter
will require a follow-on patch to implement. A small additional set of
tests verifies one particular case, mostly to check whether the flag
mechanism works.

R=adamk,yangguo@chromium.org
LOG=Y
BUG=v8:4602

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

Cr-Commit-Position: refs/heads/master@{#35068}
parent 51342423
......@@ -305,6 +305,7 @@ action("js2c_experimental") {
"src/js/generator.js",
"src/js/harmony-atomics.js",
"src/js/harmony-regexp.js",
"src/js/harmony-regexp-exec.js",
"src/js/harmony-object-observe.js",
"src/js/harmony-sharedarraybuffer.js",
"src/js/harmony-simd.js",
......
......@@ -2363,6 +2363,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexps)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_unicode_regexps)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_do_expressions)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_iterator_close)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexp_exec)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexp_lookbehind)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexp_property)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_function_name)
......@@ -2942,6 +2943,8 @@ bool Genesis::InstallExperimentalNatives() {
static const char* harmony_simd_natives[] = {"native harmony-simd.js",
nullptr};
static const char* harmony_do_expressions_natives[] = {nullptr};
static const char* harmony_regexp_exec_natives[] = {
"native harmony-regexp-exec.js", nullptr};
static const char* harmony_regexp_subclass_natives[] = {nullptr};
static const char* harmony_regexp_lookbehind_natives[] = {nullptr};
static const char* harmony_instanceof_natives[] = {nullptr};
......
......@@ -199,6 +199,7 @@ DEFINE_IMPLICATION(es_staging, move_object_start)
V(harmony_sharedarraybuffer, "harmony sharedarraybuffer") \
V(harmony_simd, "harmony simd") \
V(harmony_do_expressions, "harmony do-expressions") \
V(harmony_regexp_exec, "harmony RegExp exec override behavior") \
V(harmony_regexp_property, "harmony unicode regexp property classes") \
V(harmony_string_padding, "harmony String-padding methods")
......
// 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);
})
......@@ -36,6 +36,7 @@ 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");
......@@ -70,17 +71,6 @@ utils.Import(function(from) {
// Utilities for definitions
function OverrideFunction(object, name, f) {
%CheckIsBootstrapping();
ObjectDefineProperty(object, name, { value: f,
writeable: true,
configurable: true,
enumerable: false });
%FunctionSetName(f, name);
%FunctionRemovePrototype(f);
%SetNativeFlag(f);
}
function InstallFunction(object, name, func) {
InstallFunctions(object, DONT_ENUM, [name, func]);
}
......
......@@ -126,6 +126,18 @@ function InstallGetterSetter(object, name, getter, setter, attributes) {
}
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
......@@ -187,6 +199,12 @@ function PostNatives(utils) {
"PromiseChain",
"PromiseDeferred",
"PromiseResolved",
"RegExpSubclassExecJS",
"RegExpSubclassMatch",
"RegExpSubclassReplace",
"RegExpSubclassSearch",
"RegExpSubclassSplit",
"RegExpSubclassTest",
"SetIterator",
"SetIteratorNext",
"SetValues",
......@@ -206,6 +224,10 @@ function PostNatives(utils) {
"to_string_tag_symbol",
"object_to_string",
"species_symbol",
"match_symbol",
"replace_symbol",
"search_symbol",
"split_symbol",
];
var filtered_exports = {};
......@@ -284,6 +306,7 @@ utils.InstallConstants = InstallConstants;
utils.InstallFunctions = InstallFunctions;
utils.InstallGetter = InstallGetter;
utils.InstallGetterSetter = InstallGetterSetter;
utils.OverrideFunction = OverrideFunction;
utils.SetUpLockedPrototype = SetUpLockedPrototype;
utils.PostNatives = PostNatives;
utils.PostExperimentals = PostExperimentals;
......
......@@ -11,21 +11,30 @@
// -------------------------------------------------------------------
// 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;
});
// -------------------------------------------------------------------
......@@ -46,6 +55,7 @@ var RegExpLastMatchInfo = new InternalPackedArray(
// -------------------------------------------------------------------
// ES#sec-isregexp IsRegExp ( argument )
function IsRegExp(o) {
if (!IS_RECEIVER(o)) return false;
var is_regexp = o[matchSymbol];
......@@ -54,7 +64,8 @@ function IsRegExp(o) {
}
// 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);
......@@ -72,6 +83,8 @@ function PatternFlags(pattern) {
}
// ES#sec-regexp-pattern-flags
// RegExp ( pattern, flags )
function RegExpConstructor(pattern, flags) {
var newtarget = new.target;
var pattern_is_regexp = IsRegExp(pattern);
......@@ -101,6 +114,7 @@ function RegExpConstructor(pattern, flags) {
}
// ES#sec-regexp.prototype.compile RegExp.prototype.compile (pattern, flags)
function RegExpCompileJS(pattern, flags) {
if (!IS_REGEXP(this)) {
throw MakeTypeError(kIncompatibleMethodReceiver,
......@@ -165,6 +179,54 @@ function RegExpExecNoTests(regexp, string, start) {
}
// 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(this.global);
var sticky = TO_BOOLEAN(this.sticky);
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,
......@@ -204,10 +266,25 @@ function RegExpExecJS(string) {
}
// ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S )
function RegExpSubclassExec(regexp, string) {
var exec = regexp.exec;
if (IS_CALLABLE(exec)) {
var result = %_Call(exec, regexp, string);
if (!IS_OBJECT(result) && !IS_NULL(result)) {
throw MakeTypeError(kInvalidRegExpExecResult);
}
return result;
}
return %_Call(RegExpExecJS, regexp, string);
}
// 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
......@@ -261,6 +338,19 @@ function RegExpTest(string) {
}
}
// ES#sec-regexp.prototype.test RegExp.prototype.test ( S )
function RegExpSubclassTest(string) {
if (!IS_OBJECT(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;
......@@ -308,7 +398,8 @@ function AtSurrogatePair(subject, index) {
}
// 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)) {
......@@ -382,9 +473,69 @@ function RegExpSplit(string, limit) {
}
// 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);
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);
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);
......@@ -398,7 +549,38 @@ function RegExpMatch(string) {
}
// ES6 21.2.5.8.
// ES#sec-regexp.prototype-@@match
// RegExp.prototype [ @@match ] ( string )
function RegExpSubclassMatch(string) {
if (!IS_OBJECT(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 = [];
var n = 0;
var result;
while (true) {
result = RegExpSubclassExec(this, string);
if (IS_NULL(result)) {
if (n === 0) return null;
return array;
}
var matchStr = TO_STRING(result[0]);
%AddElement(array, n, matchStr);
if (matchStr === "") SetAdvancedStringIndex(this, string, unicode);
n++;
}
}
%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
......@@ -525,7 +707,6 @@ function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) {
function RegExpReplace(string, replace) {
// TODO(littledan): allow non-regexp receivers.
if (!IS_REGEXP(this)) {
throw MakeTypeError(kIncompatibleMethodReceiver,
"RegExp.prototype.@@replace", this);
......@@ -567,9 +748,189 @@ function RegExpReplace(string, replace) {
}
// 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_OBJECT(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 = this.global;
if (global) {
var unicode = this.unicode;
this.lastIndex = 0;
}
var results = new InternalArray();
var result, replacement;
while (true) {
result = RegExpSubclassExec(this, string);
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);
......@@ -580,6 +941,24 @@ function RegExpSearch(string) {
}
// ES#sec-regexp.prototype-@@search
// RegExp.prototype [ @@search ] ( string )
function RegExpSubclassSearch(string) {
if (!IS_OBJECT(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
......@@ -780,6 +1159,12 @@ for (var i = 1; i < 10; ++i) {
utils.Export(function(to) {
to.RegExpExec = DoRegExpExec;
to.RegExpLastMatchInfo = RegExpLastMatchInfo;
to.RegExpSubclassExecJS = RegExpSubclassExecJS;
to.RegExpSubclassMatch = RegExpSubclassMatch;
to.RegExpSubclassReplace = RegExpSubclassReplace;
to.RegExpSubclassSearch = RegExpSubclassSearch;
to.RegExpSubclassSplit = RegExpSubclassSplit;
to.RegExpSubclassTest = RegExpSubclassTest;
to.RegExpTest = RegExpTest;
to.IsRegExp = IsRegExp;
});
......
......@@ -136,6 +136,8 @@ class CallSite {
"Function has non-object prototype '%' in instanceof check") \
T(InvalidArgument, "invalid_argument") \
T(InvalidInOperatorUse, "Cannot use 'in' operator to search for '%' in %") \
T(InvalidRegExpExecResult, \
"RegExp exec method returned something other than an Object or null") \
T(InvalidSimdOperation, "% is not a valid type for this SIMD operation.") \
T(IteratorResultNotAnObject, "Iterator result % is not an object") \
T(IteratorValueNotAnObject, "Iterator value % is not an entry object") \
......
......@@ -119,7 +119,7 @@ bytecodes: [
B(TestEqualStrict), R(12),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), U16(137),
B(Wide), B(LdaSmi), U16(138),
B(Star), R(12),
B(LdaConstant), U8(8),
B(Star), R(13),
......@@ -302,7 +302,7 @@ bytecodes: [
B(TestEqualStrict), R(13),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), U16(137),
B(Wide), B(LdaSmi), U16(138),
B(Star), R(13),
B(LdaConstant), U8(8),
B(Star), R(14),
......@@ -499,7 +499,7 @@ bytecodes: [
B(TestEqualStrict), R(12),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), U16(137),
B(Wide), B(LdaSmi), U16(138),
B(Star), R(12),
B(LdaConstant), U8(8),
B(Star), R(13),
......@@ -686,7 +686,7 @@ bytecodes: [
B(TestEqualStrict), R(11),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), U16(137),
B(Wide), B(LdaSmi), U16(138),
B(Star), R(11),
B(LdaConstant), U8(10),
B(Star), R(12),
......
// 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.
// Flags: --harmony-regexp-exec
class MyError extends Error { }
RegExp.prototype.exec = () => { throw new MyError() };
assertThrows(() => "foo".match(/bar/), MyError);
// 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.
// Flags: --no-harmony-regexp-exec
class MyError extends Error { }
RegExp.prototype.exec = () => { throw new MyError() };
assertEquals(null, "foo".match(/bar/));
......@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --no-harmony-regexp-exec
// Regexp shouldn't use String.prototype.slice()
var s = new String("foo");
assertEquals("f", s.slice(0,1));
......
......@@ -2081,6 +2081,7 @@
'../../src/js/generator.js',
'../../src/js/harmony-atomics.js',
'../../src/js/harmony-regexp.js',
'../../src/js/harmony-regexp-exec.js',
'../../src/js/harmony-object-observe.js',
'../../src/js/harmony-sharedarraybuffer.js',
'../../src/js/harmony-simd.js',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment