| Index: src/builtins/builtins-regexp.cc
|
| diff --git a/src/builtins/builtins-regexp.cc b/src/builtins/builtins-regexp.cc
|
| index b907a1bf45dd0559bbf92279e69cb6704121e94f..95979c91e1908023617c155bc685afa9e0b5934f 100644
|
| --- a/src/builtins/builtins-regexp.cc
|
| +++ b/src/builtins/builtins-regexp.cc
|
| @@ -422,9 +422,10 @@ Node* RegExpBuiltinsAssembler::IsInitialRegExpMap(Node* context, Node* map) {
|
| // We use a fairly coarse granularity for this and simply check whether both
|
| // the regexp itself is unmodified (i.e. its map has not changed) and its
|
| // prototype is unmodified.
|
| -void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* context, Node* map,
|
| - Label* if_isunmodified,
|
| - Label* if_ismodified) {
|
| +void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* const context,
|
| + Node* const map,
|
| + Label* const if_isunmodified,
|
| + Label* const if_ismodified) {
|
| Node* const native_context = LoadNativeContext(context);
|
| Node* const regexp_fun =
|
| LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
|
| @@ -446,6 +447,25 @@ void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* context, Node* map,
|
| Branch(proto_has_initialmap, if_isunmodified, if_ismodified);
|
| }
|
|
|
| +Node* RegExpBuiltinsAssembler::IsFastRegExpMap(Node* const context,
|
| + Node* const map) {
|
| + Label yup(this), nope(this), out(this);
|
| + Variable var_result(this, MachineRepresentation::kWord32);
|
| +
|
| + BranchIfFastRegExp(context, map, &yup, &nope);
|
| +
|
| + Bind(&yup);
|
| + var_result.Bind(Int32Constant(1));
|
| + Goto(&out);
|
| +
|
| + Bind(&nope);
|
| + var_result.Bind(Int32Constant(0));
|
| + Goto(&out);
|
| +
|
| + Bind(&out);
|
| + return var_result.value();
|
| +}
|
| +
|
| void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* context, Node* map,
|
| Label* if_isunmodified,
|
| Label* if_ismodified) {
|
| @@ -2029,6 +2049,51 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context,
|
| }
|
| }
|
|
|
| +// Helper that skips a few initial checks.
|
| +TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) {
|
| + typedef RegExpSplitDescriptor Descriptor;
|
| +
|
| + Node* const regexp = Parameter(Descriptor::kReceiver);
|
| + Node* const string = Parameter(Descriptor::kString);
|
| + Node* const maybe_limit = Parameter(Descriptor::kLimit);
|
| + Node* const context = Parameter(Descriptor::kContext);
|
| +
|
| + CSA_ASSERT(this, IsFastRegExpMap(context, LoadMap(regexp)));
|
| + CSA_ASSERT(this, IsString(string));
|
| +
|
| + // TODO(jgruber): Even if map checks send us to the fast path, we still need
|
| + // to verify the constructor property and jump to the slow path if it has
|
| + // been changed.
|
| +
|
| + // Convert {maybe_limit} to a uint32, capping at the maximal smi value.
|
| + Variable var_limit(this, MachineRepresentation::kTagged);
|
| + Label if_limitissmimax(this), limit_done(this);
|
| +
|
| + GotoIf(IsUndefined(maybe_limit), &if_limitissmimax);
|
| +
|
| + {
|
| + Node* const limit = ToUint32(context, maybe_limit);
|
| + GotoUnless(TaggedIsSmi(limit), &if_limitissmimax);
|
| +
|
| + var_limit.Bind(limit);
|
| + Goto(&limit_done);
|
| + }
|
| +
|
| + Bind(&if_limitissmimax);
|
| + {
|
| + // TODO(jgruber): In this case, we can probably avoid generation of limit
|
| + // checks in Generate_RegExpPrototypeSplitBody.
|
| + var_limit.Bind(SmiConstant(Smi::kMaxValue));
|
| + Goto(&limit_done);
|
| + }
|
| +
|
| + Bind(&limit_done);
|
| + {
|
| + Node* const limit = var_limit.value();
|
| + RegExpPrototypeSplitBody(context, regexp, string, limit);
|
| + }
|
| +}
|
| +
|
| // ES#sec-regexp.prototype-@@split
|
| // RegExp.prototype [ @@split ] ( string, limit )
|
| TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) {
|
| @@ -2046,51 +2111,16 @@ TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) {
|
| // Convert {maybe_string} to a String.
|
| Node* const string = ToString(context, maybe_string);
|
|
|
| - Label fast_path(this), slow_path(this);
|
| - BranchIfFastRegExp(context, map, &fast_path, &slow_path);
|
| -
|
| - Bind(&fast_path);
|
| - {
|
| - // TODO(jgruber): Even if map checks send us to the fast path, we still need
|
| - // to verify the constructor property and jump to the slow path if it has
|
| - // been changed.
|
| -
|
| - // Convert {maybe_limit} to a uint32, capping at the maximal smi value.
|
| - Variable var_limit(this, MachineRepresentation::kTagged);
|
| - Label if_limitissmimax(this), limit_done(this);
|
| -
|
| - GotoIf(IsUndefined(maybe_limit), &if_limitissmimax);
|
| -
|
| - {
|
| - Node* const limit = ToUint32(context, maybe_limit);
|
| - GotoUnless(TaggedIsSmi(limit), &if_limitissmimax);
|
| -
|
| - var_limit.Bind(limit);
|
| - Goto(&limit_done);
|
| - }
|
| -
|
| - Bind(&if_limitissmimax);
|
| - {
|
| - // TODO(jgruber): In this case, we can probably generation of limit checks
|
| - // in Generate_RegExpPrototypeSplitBody.
|
| - Node* const smi_max = SmiConstant(Smi::kMaxValue);
|
| - var_limit.Bind(smi_max);
|
| - Goto(&limit_done);
|
| - }
|
| + Label stub(this), runtime(this, Label::kDeferred);
|
| + BranchIfFastRegExp(context, map, &stub, &runtime);
|
|
|
| - Bind(&limit_done);
|
| - {
|
| - Node* const limit = var_limit.value();
|
| - RegExpPrototypeSplitBody(context, receiver, string, limit);
|
| - }
|
| - }
|
| + Bind(&stub);
|
| + Callable split_callable = CodeFactory::RegExpSplit(isolate());
|
| + Return(CallStub(split_callable, context, receiver, string, maybe_limit));
|
|
|
| - Bind(&slow_path);
|
| - {
|
| - Node* const result = CallRuntime(Runtime::kRegExpSplit, context, receiver,
|
| - string, maybe_limit);
|
| - Return(result);
|
| - }
|
| + Bind(&runtime);
|
| + Return(CallRuntime(Runtime::kRegExpSplit, context, receiver, string,
|
| + maybe_limit));
|
| }
|
|
|
| Node* RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath(
|
| @@ -2406,50 +2436,37 @@ Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath(
|
| return var_result.value();
|
| }
|
|
|
| -// ES#sec-regexp.prototype-@@replace
|
| -// RegExp.prototype [ @@replace ] ( string, replaceValue )
|
| -TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) {
|
| - Node* const maybe_receiver = Parameter(0);
|
| - Node* const maybe_string = Parameter(1);
|
| - Node* const replace_value = Parameter(2);
|
| - Node* const context = Parameter(5);
|
| -
|
| - // Ensure {maybe_receiver} is a JSReceiver.
|
| - Node* const map = ThrowIfNotJSReceiver(
|
| - context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver,
|
| - "RegExp.prototype.@@replace");
|
| - Node* const receiver = maybe_receiver;
|
| +// Helper that skips a few initial checks.
|
| +TF_BUILTIN(RegExpReplace, RegExpBuiltinsAssembler) {
|
| + typedef RegExpReplaceDescriptor Descriptor;
|
|
|
| - // Convert {maybe_string} to a String.
|
| - Callable tostring_callable = CodeFactory::ToString(isolate());
|
| - Node* const string = CallStub(tostring_callable, context, maybe_string);
|
| + Node* const regexp = Parameter(Descriptor::kReceiver);
|
| + Node* const string = Parameter(Descriptor::kString);
|
| + Node* const replace_value = Parameter(Descriptor::kReplaceValue);
|
| + Node* const context = Parameter(Descriptor::kContext);
|
|
|
| - // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance?
|
| - Label checkreplacecallable(this), runtime(this, Label::kDeferred),
|
| - fastpath(this);
|
| - BranchIfFastRegExp(context, map, &checkreplacecallable, &runtime);
|
| + CSA_ASSERT(this, IsFastRegExpMap(context, LoadMap(regexp)));
|
| + CSA_ASSERT(this, IsString(string));
|
|
|
| - Bind(&checkreplacecallable);
|
| - Node* const regexp = receiver;
|
| + Label checkreplacestring(this), if_iscallable(this),
|
| + runtime(this, Label::kDeferred);
|
|
|
| // 2. Is {replace_value} callable?
|
| - Label checkreplacestring(this), if_iscallable(this);
|
| GotoIf(TaggedIsSmi(replace_value), &checkreplacestring);
|
| -
|
| - Node* const replace_value_map = LoadMap(replace_value);
|
| - Branch(IsCallableMap(replace_value_map), &if_iscallable, &checkreplacestring);
|
| + Branch(IsCallableMap(LoadMap(replace_value)), &if_iscallable,
|
| + &checkreplacestring);
|
|
|
| // 3. Does ToString({replace_value}) contain '$'?
|
| Bind(&checkreplacestring);
|
| {
|
| + Callable tostring_callable = CodeFactory::ToString(isolate());
|
| Node* const replace_string =
|
| CallStub(tostring_callable, context, replace_value);
|
|
|
| Node* const dollar_char = Int32Constant('$');
|
| - Node* const smi_minusone = SmiConstant(Smi::FromInt(-1));
|
| GotoUnless(SmiEqual(StringIndexOfChar(context, replace_string, dollar_char,
|
| SmiConstant(0)),
|
| - smi_minusone),
|
| + SmiConstant(-1)),
|
| &runtime);
|
|
|
| Return(
|
| @@ -2459,35 +2476,56 @@ TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) {
|
| // {regexp} is unmodified and {replace_value} is callable.
|
| Bind(&if_iscallable);
|
| {
|
| - Node* const replace_callable = replace_value;
|
| + Node* const replace_fn = replace_value;
|
|
|
| // Check if the {regexp} is global.
|
| Label if_isglobal(this), if_isnotglobal(this);
|
| +
|
| Node* const is_global = FastFlagGetter(regexp, JSRegExp::kGlobal);
|
| Branch(is_global, &if_isglobal, &if_isnotglobal);
|
|
|
| Bind(&if_isglobal);
|
| - {
|
| - Node* const result = ReplaceGlobalCallableFastPath(
|
| - context, regexp, string, replace_callable);
|
| - Return(result);
|
| - }
|
| + Return(ReplaceGlobalCallableFastPath(context, regexp, string, replace_fn));
|
|
|
| Bind(&if_isnotglobal);
|
| - {
|
| - Node* const result =
|
| - CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction,
|
| - context, string, regexp, replace_callable);
|
| - Return(result);
|
| - }
|
| + Return(CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction,
|
| + context, string, regexp, replace_fn));
|
| }
|
|
|
| Bind(&runtime);
|
| - {
|
| - Node* const result = CallRuntime(Runtime::kRegExpReplace, context, receiver,
|
| - string, replace_value);
|
| - Return(result);
|
| - }
|
| + Return(CallRuntime(Runtime::kRegExpReplace, context, regexp, string,
|
| + replace_value));
|
| +}
|
| +
|
| +// ES#sec-regexp.prototype-@@replace
|
| +// RegExp.prototype [ @@replace ] ( string, replaceValue )
|
| +TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) {
|
| + Node* const maybe_receiver = Parameter(0);
|
| + Node* const maybe_string = Parameter(1);
|
| + Node* const replace_value = Parameter(2);
|
| + Node* const context = Parameter(5);
|
| +
|
| + // Ensure {maybe_receiver} is a JSReceiver.
|
| + Node* const map = ThrowIfNotJSReceiver(
|
| + context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver,
|
| + "RegExp.prototype.@@replace");
|
| + Node* const receiver = maybe_receiver;
|
| +
|
| + // Convert {maybe_string} to a String.
|
| + Callable tostring_callable = CodeFactory::ToString(isolate());
|
| + Node* const string = CallStub(tostring_callable, context, maybe_string);
|
| +
|
| + // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance?
|
| + Label stub(this), runtime(this, Label::kDeferred);
|
| + BranchIfFastRegExp(context, map, &stub, &runtime);
|
| +
|
| + Bind(&stub);
|
| + Callable replace_callable = CodeFactory::RegExpReplace(isolate());
|
| + Return(CallStub(replace_callable, context, receiver, string, replace_value));
|
| +
|
| + Bind(&runtime);
|
| + Return(CallRuntime(Runtime::kRegExpReplace, context, receiver, string,
|
| + replace_value));
|
| }
|
|
|
| // Simple string matching functionality for internal use which does not modify
|
|
|