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;
......
This diff is collapsed.
......@@ -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