[builtins] Port RegExp Split to Torque
Bug: v8:8976 Change-Id: I1ffc6637e26ee217750d099d758fd67ed2130131 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1796316 Commit-Queue: Z Nguyen-Huu <duongn@microsoft.com> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Cr-Commit-Position: refs/heads/master@{#63733}
This commit is contained in:
parent
7b4f4b2e9f
commit
ee0581c332
1
BUILD.gn
1
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",
|
||||
|
@ -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/ */ \
|
||||
|
@ -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> context,
|
||||
TNode<JSRegExp> regexp,
|
||||
TNode<String> string,
|
||||
TNode<Smi> const limit) {
|
||||
TNode<JSArray> RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(
|
||||
TNode<Context> context, TNode<JSRegExp> regexp, TNode<String> string,
|
||||
TNode<Smi> 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> context,
|
||||
TNode<Map> 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> context,
|
||||
{
|
||||
TNode<Smi> length = SmiConstant(1);
|
||||
TNode<IntPtrT> capacity = IntPtrConstant(1);
|
||||
TNode<JSArray> result = AllocateJSArray(kind, array_map, capacity,
|
||||
length, allocation_site, mode);
|
||||
var_result = AllocateJSArray(kind, array_map, capacity, length,
|
||||
allocation_site, mode);
|
||||
|
||||
TNode<FixedArray> fixed_array = CAST(LoadElements(result));
|
||||
TNode<FixedArray> 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> context,
|
||||
|
||||
BIND(&out);
|
||||
{
|
||||
TNode<JSArray> const result = array.ToJSArray(context);
|
||||
Return(result);
|
||||
var_result = array.ToJSArray(context);
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&return_empty_array);
|
||||
{
|
||||
TNode<Smi> length = SmiZero();
|
||||
TNode<IntPtrT> capacity = IntPtrZero();
|
||||
TNode<JSArray> result = AllocateJSArray(kind, array_map, capacity, length,
|
||||
allocation_site, mode);
|
||||
Return(result);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper that skips a few initial checks.
|
||||
TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) {
|
||||
TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kRegExp));
|
||||
TNode<String> string = CAST(Parameter(Descriptor::kString));
|
||||
TNode<Object> maybe_limit = CAST(Parameter(Descriptor::kLimit));
|
||||
TNode<Context> 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<IntPtrT> argc =
|
||||
ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
|
||||
CodeStubArguments args(this, argc);
|
||||
|
||||
TNode<Object> maybe_receiver = args.GetReceiver();
|
||||
TNode<Object> maybe_string = args.GetOptionalArgumentValue(kStringArg);
|
||||
TNode<Object> maybe_limit = args.GetOptionalArgumentValue(kLimitArg);
|
||||
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
|
||||
|
||||
// Ensure {maybe_receiver} is a JSReceiver.
|
||||
ThrowIfNotJSReceiver(context, maybe_receiver,
|
||||
MessageTemplate::kIncompatibleMethodReceiver,
|
||||
"RegExp.prototype.@@split");
|
||||
TNode<JSReceiver> receiver = CAST(maybe_receiver);
|
||||
|
||||
// Convert {maybe_string} to a String.
|
||||
TNode<String> 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 {
|
||||
|
@ -187,9 +187,10 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
|
||||
TNode<String> const string,
|
||||
const bool is_fastpath);
|
||||
|
||||
void RegExpPrototypeSplitBody(TNode<Context> context, TNode<JSRegExp> regexp,
|
||||
TNode<String> const string,
|
||||
TNode<Smi> const limit);
|
||||
TNode<JSArray> RegExpPrototypeSplitBody(TNode<Context> context,
|
||||
TNode<JSRegExp> regexp,
|
||||
TNode<String> const string,
|
||||
TNode<Smi> const limit);
|
||||
};
|
||||
|
||||
class RegExpMatchAllAssembler : public RegExpBuiltinsAssembler {
|
||||
|
72
src/builtins/regexp-split.tq
Normal file
72
src/builtins/regexp-split.tq
Normal file
@ -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<Smi>(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<JSReceiver>(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<FastJSRegExp>(receiver)
|
||||
otherwise return runtime::RegExpSplit(receiver, string, limit);
|
||||
return RegExpSplit(fastRegExp, string, limit);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user