diff --git a/BUILD.gn b/BUILD.gn index 21855581b5..74e28dfd74 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -998,6 +998,7 @@ torque_files = [ "src/builtins/regexp-replace.tq", "src/builtins/regexp-search.tq", "src/builtins/regexp-source.tq", + "src/builtins/regexp-split.tq", "src/builtins/regexp-test.tq", "src/builtins/regexp.tq", "src/builtins/string.tq", diff --git a/src/builtins/builtins-definitions.h b/src/builtins/builtins-definitions.h index 1970d45493..2691cdfc9b 100644 --- a/src/builtins/builtins-definitions.h +++ b/src/builtins/builtins-definitions.h @@ -854,14 +854,11 @@ namespace internal { CPP(RegExpPrototypeToString) \ CPP(RegExpRightContextGetter) \ \ - /* ES #sec-regexp.prototype-@@split */ \ - TFJ(RegExpPrototypeSplit, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \ /* RegExp helpers */ \ TFS(RegExpExecAtom, kRegExp, kString, kLastIndex, kMatchInfo) \ TFS(RegExpExecInternal, kRegExp, kString, kLastIndex, kMatchInfo) \ ASM(RegExpInterpreterTrampoline, CCall) \ TFS(RegExpPrototypeExecSlow, kReceiver, kString) \ - TFS(RegExpSplit, kRegExp, kString, kLimit) \ \ /* RegExp String Iterator */ \ /* https://tc39.github.io/proposal-string-matchall/ */ \ diff --git a/src/builtins/builtins-regexp-gen.cc b/src/builtins/builtins-regexp-gen.cc index 921a43df55..35a409cb91 100644 --- a/src/builtins/builtins-regexp-gen.cc +++ b/src/builtins/builtins-regexp-gen.cc @@ -2042,10 +2042,9 @@ TF_BUILTIN(RegExpPrototypeMatchAll, RegExpMatchAllAssembler) { // Generates the fast path for @@split. {regexp} is an unmodified, non-sticky // JSRegExp, {string} is a String, and {limit} is a Smi. -void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(TNode context, - TNode regexp, - TNode string, - TNode const limit) { +TNode RegExpBuiltinsAssembler::RegExpPrototypeSplitBody( + TNode context, TNode regexp, TNode string, + TNode const limit) { CSA_ASSERT(this, IsFastRegExpPermissive(context, regexp)); CSA_ASSERT(this, Word32BinaryNot(FastFlagGetter(regexp, JSRegExp::kSticky))); @@ -2059,6 +2058,8 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(TNode context, TNode array_map = LoadJSArrayElementsMap(kind, native_context); Label return_empty_array(this, Label::kDeferred); + TVARIABLE(JSArray, var_result); + Label done(this); // If limit is zero, return an empty array. { @@ -2092,13 +2093,13 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(TNode context, { TNode length = SmiConstant(1); TNode capacity = IntPtrConstant(1); - TNode result = AllocateJSArray(kind, array_map, capacity, - length, allocation_site, mode); + var_result = AllocateJSArray(kind, array_map, capacity, length, + allocation_site, mode); - TNode fixed_array = CAST(LoadElements(result)); + TNode fixed_array = CAST(LoadElements(var_result.value())); UnsafeStoreFixedArrayElement(fixed_array, 0, string); - Return(result); + Goto(&done); } } @@ -2262,110 +2263,21 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(TNode context, BIND(&out); { - TNode const result = array.ToJSArray(context); - Return(result); + var_result = array.ToJSArray(context); + Goto(&done); } BIND(&return_empty_array); { TNode length = SmiZero(); TNode capacity = IntPtrZero(); - TNode result = AllocateJSArray(kind, array_map, capacity, length, - allocation_site, mode); - Return(result); - } -} - -// Helper that skips a few initial checks. -TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) { - TNode regexp = CAST(Parameter(Descriptor::kRegExp)); - TNode string = CAST(Parameter(Descriptor::kString)); - TNode maybe_limit = CAST(Parameter(Descriptor::kLimit)); - TNode context = CAST(Parameter(Descriptor::kContext)); - - CSA_ASSERT_BRANCH(this, [&](Label* ok, Label* not_ok) { - BranchIfFastRegExp_Strict(context, regexp, ok, not_ok); - }); - - // Verify {maybe_limit}. - - VARIABLE(var_limit, MachineRepresentation::kTagged, maybe_limit); - Label if_limitissmimax(this), runtime(this, Label::kDeferred); - - { - Label next(this); - - GotoIf(IsUndefined(maybe_limit), &if_limitissmimax); - Branch(TaggedIsPositiveSmi(maybe_limit), &next, &runtime); - - // We need to be extra-strict and require the given limit to be either - // undefined or a positive smi. We can't call ToUint32(maybe_limit) since - // that might move us onto the slow path, resulting in ordering spec - // violations (see https://crbug.com/801171). - - 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(&next); - } - - BIND(&next); + var_result = AllocateJSArray(kind, array_map, capacity, length, + allocation_site, mode); + Goto(&done); } - // Due to specific shortcuts we take on the fast path (specifically, we don't - // allocate a new regexp instance as specced), we need to ensure that the - // given regexp is non-sticky to avoid invalid results. See crbug.com/v8/6706. - - GotoIf(FastFlagGetter(regexp, JSRegExp::kSticky), &runtime); - - // We're good to go on the fast path, which is inlined here. - - RegExpPrototypeSplitBody(context, regexp, string, CAST(var_limit.value())); - - BIND(&runtime); - Return(CallRuntime(Runtime::kRegExpSplit, context, regexp, string, - var_limit.value())); -} - -// ES#sec-regexp.prototype-@@split -// RegExp.prototype [ @@split ] ( string, limit ) -TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) { - const int kStringArg = 0; - const int kLimitArg = 1; - - TNode argc = - ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)); - CodeStubArguments args(this, argc); - - TNode maybe_receiver = args.GetReceiver(); - TNode maybe_string = args.GetOptionalArgumentValue(kStringArg); - TNode maybe_limit = args.GetOptionalArgumentValue(kLimitArg); - TNode context = CAST(Parameter(Descriptor::kContext)); - - // Ensure {maybe_receiver} is a JSReceiver. - ThrowIfNotJSReceiver(context, maybe_receiver, - MessageTemplate::kIncompatibleMethodReceiver, - "RegExp.prototype.@@split"); - TNode receiver = CAST(maybe_receiver); - - // Convert {maybe_string} to a String. - TNode string = ToString_Inline(context, maybe_string); - - // Strict: Reads the flags property. - // TODO(jgruber): Handle slow flag accesses on the fast path and make this - // permissive. - Label stub(this), runtime(this, Label::kDeferred); - BranchIfFastRegExp_Strict(context, receiver, &stub, &runtime); - - BIND(&stub); - args.PopAndReturn(CallBuiltin(Builtins::kRegExpSplit, context, receiver, - string, maybe_limit)); - - BIND(&runtime); - args.PopAndReturn(CallRuntime(Runtime::kRegExpSplit, context, receiver, - string, maybe_limit)); + BIND(&done); + return var_result.value(); } class RegExpStringIteratorAssembler : public RegExpBuiltinsAssembler { diff --git a/src/builtins/builtins-regexp-gen.h b/src/builtins/builtins-regexp-gen.h index a307b68b65..f90793705e 100644 --- a/src/builtins/builtins-regexp-gen.h +++ b/src/builtins/builtins-regexp-gen.h @@ -187,9 +187,10 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler { TNode const string, const bool is_fastpath); - void RegExpPrototypeSplitBody(TNode context, TNode regexp, - TNode const string, - TNode const limit); + TNode RegExpPrototypeSplitBody(TNode context, + TNode regexp, + TNode const string, + TNode const limit); }; class RegExpMatchAllAssembler : public RegExpBuiltinsAssembler { diff --git a/src/builtins/regexp-split.tq b/src/builtins/regexp-split.tq new file mode 100644 index 0000000000..8a9a30a7e9 --- /dev/null +++ b/src/builtins/regexp-split.tq @@ -0,0 +1,72 @@ +// Copyright 2019 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. + +#include 'src/builtins/builtins-regexp-gen.h' + +namespace runtime { + extern transitioning runtime + RegExpSplit(implicit context: Context)(JSReceiver, String, Object): JSAny; +} // namespace runtime + +namespace regexp { + + const kMaxValueSmi: constexpr int31 + generates 'Smi::kMaxValue'; + + extern transitioning macro RegExpBuiltinsAssembler::RegExpPrototypeSplitBody( + implicit context: Context)(JSRegExp, String, Smi): JSArray; + + // Helper that skips a few initial checks. + transitioning builtin + RegExpSplit(implicit context: Context)( + regexp: FastJSRegExp, string: String, limit: JSAny): JSAny { + let sanitizedLimit: Smi; + + // We need to be extra-strict and require the given limit to be either + // undefined or a positive smi. We can't call ToUint32(maybe_limit) since + // that might move us onto the slow path, resulting in ordering spec + // violations (see https://crbug.com/801171). + + if (limit == Undefined) { + // TODO(jgruber): In this case, we can probably avoid generation of limit + // checks in Generate_RegExpPrototypeSplitBody. + sanitizedLimit = SmiConstant(kMaxValueSmi); + } else if (!TaggedIsPositiveSmi(limit)) { + return runtime::RegExpSplit(regexp, string, limit); + } else { + sanitizedLimit = UnsafeCast(limit); + } + + // Due to specific shortcuts we take on the fast path (specifically, we + // don't allocate a new regexp instance as specced), we need to ensure that + // the given regexp is non-sticky to avoid invalid results. See + // crbug.com/v8/6706. + + if (FastFlagGetter(regexp, kSticky)) { + return runtime::RegExpSplit(regexp, string, sanitizedLimit); + } + + // We're good to go on the fast path, which is inlined here. + return RegExpPrototypeSplitBody(regexp, string, sanitizedLimit); + } + + // ES#sec-regexp.prototype-@@split + // RegExp.prototype [ @@split ] ( string, limit ) + transitioning javascript builtin RegExpPrototypeSplit( + js-implicit context: Context, receiver: JSAny)(...arguments): JSAny { + ThrowIfNotJSReceiver( + receiver, kIncompatibleMethodReceiver, 'RegExp.prototype.@@split'); + const receiver = UnsafeCast(receiver); + const string: String = ToString_Inline(context, arguments[0]); + const limit = arguments[1]; + + // Strict: Reads the flags property. + // TODO(jgruber): Handle slow flag accesses on the fast path and make this + // permissive. + const fastRegExp = Cast(receiver) + otherwise return runtime::RegExpSplit(receiver, string, limit); + return RegExpSplit(fastRegExp, string, limit); + } + +}