mirror of
https://gitee.com/openharmony/arkcompiler_ets_runtime
synced 2024-11-23 10:09:54 +00:00
!9430 RegExp GetFlags IR优化
Merge pull request !9430 from jiangmengyang/getflags
This commit is contained in:
commit
f9ad35e03c
@ -2056,7 +2056,8 @@ void Builtins::InitializeRegExp(const JSHandle<GlobalEnv> &env)
|
||||
FunctionLength::ZERO);
|
||||
JSHandle<JSFunction>(execFunc)->SetLexicalEnv(thread_, env);
|
||||
|
||||
JSHandle<JSTaggedValue> flagsGetter = CreateGetter(env, RegExp::GetFlags, "flags", FunctionLength::ZERO);
|
||||
JSHandle<JSTaggedValue> flagsGetter = CreateGetter(env, RegExp::GetFlags, "flags", FunctionLength::ZERO,
|
||||
kungfu::BuiltinsStubCSigns::RegExpGetFlags);
|
||||
JSHandle<JSTaggedValue> flagsKey(globalConstants->GetHandledFlagsString());
|
||||
SetGetter(regPrototype, flagsKey, flagsGetter);
|
||||
JSHandle<JSFunction>(flagsGetter)->SetLexicalEnv(thread_, env);
|
||||
@ -2887,16 +2888,19 @@ void Builtins::SetStringTagSymbol(const JSHandle<GlobalEnv> &env, const JSHandle
|
||||
}
|
||||
|
||||
JSHandle<JSTaggedValue> Builtins::CreateGetter(const JSHandle<GlobalEnv> &env, EcmaEntrypoint func,
|
||||
std::string_view name, int length) const
|
||||
std::string_view name, int length,
|
||||
kungfu::BuiltinsStubCSigns::ID builtinId) const
|
||||
{
|
||||
JSHandle<JSTaggedValue> funcName(factory_->NewFromUtf8ReadOnly(name));
|
||||
return CreateGetter(env, func, funcName, length);
|
||||
return CreateGetter(env, func, funcName, length, builtinId);
|
||||
}
|
||||
|
||||
JSHandle<JSTaggedValue> Builtins::CreateGetter(const JSHandle<GlobalEnv> &env, EcmaEntrypoint func,
|
||||
JSHandle<JSTaggedValue> key, int length) const
|
||||
JSHandle<JSTaggedValue> key, int length,
|
||||
kungfu::BuiltinsStubCSigns::ID builtinId) const
|
||||
{
|
||||
JSHandle<JSFunction> function = factory_->NewJSFunction(env, reinterpret_cast<void *>(func));
|
||||
JSHandle<JSFunction> function = factory_->NewJSFunction(env, reinterpret_cast<void *>(func),
|
||||
FunctionKind::GETTER_FUNCTION, builtinId);
|
||||
JSFunction::SetFunctionLength(thread_, function, JSTaggedValue(length));
|
||||
JSHandle<JSTaggedValue> prefix = thread_->GlobalConstants()->GetHandledGetString();
|
||||
JSFunction::SetFunctionName(thread_, JSHandle<JSFunctionBase>(function), key, prefix);
|
||||
|
@ -336,10 +336,16 @@ private:
|
||||
|
||||
void SetStringTagSymbol(const JSHandle<GlobalEnv> &env, const JSHandle<JSObject> &obj,
|
||||
std::string_view key) const;
|
||||
|
||||
JSHandle<JSTaggedValue> CreateGetter(const JSHandle<GlobalEnv> &env, EcmaEntrypoint func,
|
||||
std::string_view name, int length) const;
|
||||
std::string_view name, int length,
|
||||
kungfu::BuiltinsStubCSigns::ID builtinId =
|
||||
kungfu::BuiltinsStubCSigns::INVALID) const;
|
||||
|
||||
JSHandle<JSTaggedValue> CreateGetter(const JSHandle<GlobalEnv> &env, EcmaEntrypoint func,
|
||||
JSHandle<JSTaggedValue> key, int length) const;
|
||||
JSHandle<JSTaggedValue> key, int length,
|
||||
kungfu::BuiltinsStubCSigns::ID builtinId =
|
||||
kungfu::BuiltinsStubCSigns::INVALID) const;
|
||||
|
||||
void SetConstant(const JSHandle<JSObject> &obj, std::string_view key, JSTaggedValue value) const;
|
||||
|
||||
|
@ -118,6 +118,7 @@ libark_jsoptimizer_sources = [
|
||||
"builtins/builtins_object_stub_builder.cpp",
|
||||
"builtins/builtins_proxy_stub_builder.cpp",
|
||||
"builtins/builtins_reflect_stub_builder.cpp",
|
||||
"builtins/builtins_regexp_stub_builder.cpp",
|
||||
"builtins/builtins_string_stub_builder.cpp",
|
||||
"builtins/builtins_stubs.cpp",
|
||||
"builtins/builtins_typedarray_stub_builder.cpp",
|
||||
|
@ -48,7 +48,8 @@ namespace panda::ecmascript::kungfu {
|
||||
BUILTINS_WITH_TYPEDARRAY_STUB_BUILDER(V) \
|
||||
BUILTINS_WITH_DATAVIEW_STUB_BUILDER(K) \
|
||||
BUILTINS_WITH_REFLECT_STUB_BUILDER(T) \
|
||||
BUILTINS_WITH_COLLATOR_STUB_BUILDER(V)
|
||||
BUILTINS_WITH_COLLATOR_STUB_BUILDER(V) \
|
||||
BUILTINS_WITH_REGEXP_STUB_BUILDER(V)
|
||||
|
||||
#define BUILTINS_WITH_STRING_STUB_BUILDER(V) \
|
||||
V(CharAt, String, Hole()) \
|
||||
@ -162,6 +163,9 @@ namespace panda::ecmascript::kungfu {
|
||||
#define BUILTINS_WITH_COLLATOR_STUB_BUILDER(V) \
|
||||
V(ResolvedOptions, Collator, Undefined())
|
||||
|
||||
#define BUILTINS_WITH_REGEXP_STUB_BUILDER(V) \
|
||||
V(GetFlags, RegExp, Undefined())
|
||||
|
||||
#define BUILTINS_WITH_TYPEDARRAY_STUB_BUILDER(V) \
|
||||
V(Reverse, TypedArray, Undefined()) \
|
||||
V(LastIndexOf, TypedArray, Undefined()) \
|
||||
|
165
ecmascript/compiler/builtins/builtins_regexp_stub_builder.cpp
Normal file
165
ecmascript/compiler/builtins/builtins_regexp_stub_builder.cpp
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ecmascript/compiler/builtins/builtins_regexp_stub_builder.h"
|
||||
#include "ecmascript/compiler/new_object_stub_builder.h"
|
||||
#include "ecmascript/regexp/regexp_parser.h"
|
||||
namespace panda::ecmascript::kungfu {
|
||||
|
||||
void BuiltinsRegExpStubBuilder::GetFlags(GateRef glue, GateRef thisValue,
|
||||
[[maybe_unused]] GateRef numArgs, Variable* result, Label* exit, Label *slowPath)
|
||||
{
|
||||
auto env = GetEnvironment();
|
||||
Label isEcmaObject(env);
|
||||
BRANCH(IsEcmaObject(thisValue), &isEcmaObject, slowPath);
|
||||
Bind(&isEcmaObject);
|
||||
Label fastRegExpPath(env);
|
||||
GateRef fastRegExp = CallRuntime(glue, RTSTUB_ID(IsFastRegExp), { thisValue });
|
||||
BRANCH(TaggedIsTrue(fastRegExp), slowPath, &fastRegExpPath);
|
||||
Bind(&fastRegExpPath);
|
||||
{
|
||||
Label hasException(env);
|
||||
DEFVARIABLE(bitFlagsStr, VariableType::INT32(), Int32(0));
|
||||
GateRef globalResult = TryToFastGetProperty(glue, thisValue, ConstantIndex::GLOBAL_STRING_INDEX);
|
||||
Label notHasException(env);
|
||||
BRANCH(HasPendingException(glue), &hasException, ¬HasException);
|
||||
Bind(¬HasException);
|
||||
Label globalKey(env);
|
||||
Label notGlobalKey(env);
|
||||
BRANCH(TaggedIsTrue(globalResult), &globalKey, ¬GlobalKey);
|
||||
Bind(&globalKey);
|
||||
{
|
||||
bitFlagsStr = Int32Or(*bitFlagsStr, Int32(RegExpParser::FLAG_GLOBAL));
|
||||
Jump(¬GlobalKey);
|
||||
}
|
||||
Bind(¬GlobalKey);
|
||||
GateRef ignoreCaseResult = TryToFastGetProperty(glue, thisValue, ConstantIndex::IGNORE_CASE_INDEX);
|
||||
Label notHasException1(env);
|
||||
BRANCH(HasPendingException(glue), &hasException, ¬HasException1);
|
||||
Bind(¬HasException1);
|
||||
Label ignoreCaseKey(env);
|
||||
Label notIgnoreCaseKey(env);
|
||||
BRANCH(TaggedIsTrue(ignoreCaseResult), &ignoreCaseKey, ¬IgnoreCaseKey);
|
||||
Bind(&ignoreCaseKey);
|
||||
{
|
||||
bitFlagsStr = Int32Or(*bitFlagsStr, Int32(RegExpParser::FLAG_IGNORECASE));
|
||||
Jump(¬IgnoreCaseKey);
|
||||
}
|
||||
Bind(¬IgnoreCaseKey);
|
||||
GateRef multilineResult = TryToFastGetProperty(glue, thisValue, ConstantIndex::MULTILINE_INDEX);
|
||||
Label notHasException2(env);
|
||||
BRANCH(HasPendingException(glue), &hasException, ¬HasException2);
|
||||
Bind(¬HasException2);
|
||||
Label multilineKey(env);
|
||||
Label notMultilineKey(env);
|
||||
BRANCH(TaggedIsTrue(multilineResult), &multilineKey, ¬MultilineKey);
|
||||
Bind(&multilineKey);
|
||||
{
|
||||
bitFlagsStr = Int32Or(*bitFlagsStr, Int32(RegExpParser::FLAG_MULTILINE));
|
||||
Jump(¬MultilineKey);
|
||||
}
|
||||
Bind(¬MultilineKey);
|
||||
GateRef dotAllResult = TryToFastGetProperty(glue, thisValue, ConstantIndex::DOT_ALL_INDEX);
|
||||
Label notHasException3(env);
|
||||
BRANCH(HasPendingException(glue), &hasException, ¬HasException3);
|
||||
Bind(¬HasException3);
|
||||
Label dotAllKey(env);
|
||||
Label notDotAllKey(env);
|
||||
BRANCH(TaggedIsTrue(dotAllResult), &dotAllKey, ¬DotAllKey);
|
||||
Bind(&dotAllKey);
|
||||
{
|
||||
bitFlagsStr = Int32Or(*bitFlagsStr, Int32(RegExpParser::FLAG_DOTALL));
|
||||
Jump(¬DotAllKey);
|
||||
}
|
||||
Bind(¬DotAllKey);
|
||||
GateRef unicodeResult = TryToFastGetProperty(glue, thisValue, ConstantIndex::UNICODE_INDEX);
|
||||
Label notHasException4(env);
|
||||
BRANCH(HasPendingException(glue), &hasException, ¬HasException4);
|
||||
Bind(¬HasException4);
|
||||
Label unicodeKey(env);
|
||||
Label notUnicodeKey(env);
|
||||
BRANCH(TaggedIsTrue(unicodeResult), &unicodeKey, ¬UnicodeKey);
|
||||
Bind(&unicodeKey);
|
||||
{
|
||||
bitFlagsStr = Int32Or(*bitFlagsStr, Int32(RegExpParser::FLAG_UTF16));
|
||||
Jump(¬UnicodeKey);
|
||||
}
|
||||
Bind(¬UnicodeKey);
|
||||
GateRef stickyResult = TryToFastGetProperty(glue, thisValue, ConstantIndex::STICKY_INDEX);
|
||||
Label notHasException5(env);
|
||||
BRANCH(HasPendingException(glue), &hasException, ¬HasException5);
|
||||
Bind(¬HasException5);
|
||||
Label stickyKey(env);
|
||||
Label notStickyKey(env);
|
||||
BRANCH(TaggedIsTrue(stickyResult), &stickyKey, ¬StickyKey);
|
||||
Bind(&stickyKey);
|
||||
{
|
||||
bitFlagsStr = Int32Or(*bitFlagsStr, Int32(RegExpParser::FLAG_STICKY));
|
||||
Jump(¬StickyKey);
|
||||
}
|
||||
Bind(¬StickyKey);
|
||||
GateRef indicesResult = TryToFastGetProperty(glue, thisValue, ConstantIndex::HAS_INDICES_INDEX);
|
||||
Label notHasException6(env);
|
||||
BRANCH(HasPendingException(glue), &hasException, ¬HasException6);
|
||||
Bind(¬HasException6);
|
||||
Label hasIndicesKey(env);
|
||||
Label notHasIndicesKey(env);
|
||||
BRANCH(TaggedIsTrue(indicesResult), &hasIndicesKey, ¬HasIndicesKey);
|
||||
Bind(&hasIndicesKey);
|
||||
{
|
||||
bitFlagsStr = Int32Or(*bitFlagsStr, Int32(RegExpParser::FLAG_HASINDICES));
|
||||
Jump(¬HasIndicesKey);
|
||||
}
|
||||
Bind(¬HasIndicesKey);
|
||||
GateRef flagsStr = CallRuntime(glue, RTSTUB_ID(GetAllFlagsInternal), { IntToTaggedInt(*bitFlagsStr)});
|
||||
result->WriteVariable(flagsStr);
|
||||
Jump(exit);
|
||||
Bind(&hasException);
|
||||
{
|
||||
result->WriteVariable(GetGlobalConstantValue(VariableType::JS_POINTER(),
|
||||
glue, ConstantIndex::EMPTY_STRING_OBJECT_INDEX));
|
||||
Jump(exit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GateRef BuiltinsRegExpStubBuilder::TryToFastGetProperty(GateRef glue, GateRef thisValue, ConstantIndex constIndex)
|
||||
{
|
||||
auto env = GetEnvironment();
|
||||
Label entry(env);
|
||||
Label exit(env);
|
||||
env->SubCfgEntry(&entry);
|
||||
DEFVARIABLE(result, VariableType::JS_ANY(), TaggedFalse());
|
||||
GateRef key = GetGlobalConstantValue(VariableType::JS_POINTER(), glue, constIndex);
|
||||
GateRef property = FastGetPropertyByName(glue, thisValue, key, ProfileOperation());
|
||||
Label hasException(env);
|
||||
Label notHasException(env);
|
||||
BRANCH(HasPendingException(glue), &hasException, ¬HasException);
|
||||
Bind(&hasException);
|
||||
{
|
||||
result = Exception();
|
||||
Jump(&exit);
|
||||
}
|
||||
Bind(¬HasException);
|
||||
{
|
||||
result = FastToBoolean(property);
|
||||
Jump(&exit);
|
||||
}
|
||||
Bind(&exit);
|
||||
auto res = *result;
|
||||
env->SubCfgExit();
|
||||
return res;
|
||||
}
|
||||
} // namespace panda::ecmascript::kungfu
|
41
ecmascript/compiler/builtins/builtins_regexp_stub_builder.h
Normal file
41
ecmascript/compiler/builtins/builtins_regexp_stub_builder.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Huawei Device Co., Ltd.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ECMASCRIPT_COMPILER_BUILTINS_REGEXP_STUB_BUILDER_H
|
||||
#define ECMASCRIPT_COMPILER_BUILTINS_REGEXP_STUB_BUILDER_H
|
||||
#include "ecmascript/compiler/builtins/builtins_stubs.h"
|
||||
|
||||
namespace panda::ecmascript::kungfu {
|
||||
|
||||
class BuiltinsRegExpStubBuilder : public BuiltinsStubBuilder {
|
||||
public:
|
||||
explicit BuiltinsRegExpStubBuilder(StubBuilder *parent)
|
||||
: BuiltinsStubBuilder(parent) {}
|
||||
BuiltinsRegExpStubBuilder(CallSignature *callSignature, Environment *env)
|
||||
: BuiltinsStubBuilder(callSignature, env) {}
|
||||
explicit BuiltinsRegExpStubBuilder(Environment* env): BuiltinsStubBuilder(env) {}
|
||||
~BuiltinsRegExpStubBuilder() override = default;
|
||||
NO_MOVE_SEMANTIC(BuiltinsRegExpStubBuilder);
|
||||
NO_COPY_SEMANTIC(BuiltinsRegExpStubBuilder);
|
||||
void GenerateCircuit() override {}
|
||||
GateRef TryToFastGetProperty(GateRef glue, GateRef thisValue, ConstantIndex constIndex);
|
||||
|
||||
#define DECLARE_BUILTINS_REGEXP_STUB_BUILDER(method, ...) \
|
||||
void method(GateRef glue, GateRef thisValue, GateRef numArgs, Variable *res, Label *exit, Label *slowPath);
|
||||
BUILTINS_WITH_REGEXP_STUB_BUILDER(DECLARE_BUILTINS_REGEXP_STUB_BUILDER)
|
||||
#undef DECLARE_BUILTINS_REGEXP_STUB_BUILDER
|
||||
};
|
||||
} // namespace panda::ecmascript::kungfu
|
||||
#endif // ECMASCRIPT_COMPILER_BUILTINS_REGEXP_STUB_BUILDER_H
|
@ -21,6 +21,7 @@
|
||||
#include "ecmascript/compiler/builtins/builtins_dataview_stub_builder.h"
|
||||
#include "ecmascript/compiler/builtins/builtins_function_stub_builder.h"
|
||||
#include "ecmascript/compiler/builtins/builtins_string_stub_builder.h"
|
||||
#include "ecmascript/compiler/builtins/builtins_regexp_stub_builder.h"
|
||||
#include "ecmascript/compiler/builtins/builtins_number_stub_builder.h"
|
||||
#include "ecmascript/compiler/builtins/builtins_proxy_stub_builder.h"
|
||||
#include "ecmascript/compiler/builtins/builtins_reflect_stub_builder.h"
|
||||
|
@ -540,6 +540,7 @@ class ObjectFactory;
|
||||
V(EthioaaString, ETHIOAA_INDEX, "ethioaa") \
|
||||
V(StickyString, STICKY_INDEX, "sticky") \
|
||||
V(HasIndicesString, HAS_INDICES_INDEX, "hasIndices") \
|
||||
V(DotAllString, DOT_ALL_INDEX, "dotAll") \
|
||||
V(MultilineString, MULTILINE_INDEX, "multiline") \
|
||||
V(IgnoreCaseString, IGNORE_CASE_INDEX, "ignoreCase") \
|
||||
V(IndicesString, INDICES_INDEX, "indices") \
|
||||
|
@ -479,7 +479,9 @@ namespace panda::ecmascript {
|
||||
V(SuperCallForwardAllArgs) \
|
||||
V(OptSuperCallForwardAllArgs) \
|
||||
V(GetCollationValueFromIcuCollator) \
|
||||
V(DecodeURIComponent)
|
||||
V(DecodeURIComponent) \
|
||||
V(IsFastRegExp) \
|
||||
V(GetAllFlagsInternal)
|
||||
|
||||
|
||||
#define RUNTIME_STUB_LIST(V) \
|
||||
|
@ -4050,6 +4050,61 @@ DEF_RUNTIME_STUBS(GetCollationValueFromIcuCollator)
|
||||
return thread->GlobalConstants()->GetDefaultString().GetRawData();
|
||||
}
|
||||
|
||||
DEF_RUNTIME_STUBS(IsFastRegExp)
|
||||
{
|
||||
RUNTIME_STUBS_HEADER(IsFastRegExp);
|
||||
JSHandle<JSObject> thisValue = GetHArg<JSObject>(argv, argc, 0); // 0: means the zeroth parameter
|
||||
JSHandle<JSTaggedValue> thisObjVal(thisValue);
|
||||
bool result = builtins::BuiltinsRegExp::IsFastRegExp(thread, thisObjVal);
|
||||
return JSTaggedValue(result).GetRawData();
|
||||
}
|
||||
|
||||
DEF_RUNTIME_STUBS(GetAllFlagsInternal)
|
||||
{
|
||||
RUNTIME_STUBS_HEADER(GetAllFlagsInternal);
|
||||
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
|
||||
uint8_t *flagsStr = new uint8_t[RegExpParser::FLAG_NUM + 1]; // FLAG_NUM flags + '\0'
|
||||
if (flagsStr == nullptr) {
|
||||
LOG_ECMA(FATAL) << "BuiltinsRegExp::GetAllFlagsInternal:flagsStr is nullptr";
|
||||
}
|
||||
size_t flagsLen = 0;
|
||||
JSHandle<JSTaggedValue> value = GetHArg<JSTaggedValue>(argv, argc, 0); // 0: means the zeroth parameter
|
||||
int64 bigFlagsStr = value->GetInt();
|
||||
if (bigFlagsStr & RegExpParser::FLAG_HASINDICES) {
|
||||
flagsStr[flagsLen] = 'd';
|
||||
flagsLen++;
|
||||
}
|
||||
if (bigFlagsStr & RegExpParser::FLAG_GLOBAL) {
|
||||
flagsStr[flagsLen] = 'g';
|
||||
flagsLen++;
|
||||
}
|
||||
if (bigFlagsStr & RegExpParser::FLAG_IGNORECASE) {
|
||||
flagsStr[flagsLen] = 'i';
|
||||
flagsLen++;
|
||||
}
|
||||
if (bigFlagsStr & RegExpParser::FLAG_MULTILINE) {
|
||||
flagsStr[flagsLen] = 'm';
|
||||
flagsLen++;
|
||||
}
|
||||
if (bigFlagsStr & RegExpParser::FLAG_DOTALL) {
|
||||
flagsStr[flagsLen] = 's';
|
||||
flagsLen++;
|
||||
}
|
||||
if (bigFlagsStr & RegExpParser::FLAG_UTF16) {
|
||||
flagsStr[flagsLen] = 'u';
|
||||
flagsLen++;
|
||||
}
|
||||
if (bigFlagsStr & RegExpParser::FLAG_STICKY) {
|
||||
flagsStr[flagsLen] = 'y';
|
||||
flagsLen++;
|
||||
}
|
||||
|
||||
flagsStr[flagsLen] = '\0';
|
||||
JSHandle<EcmaString> flagsString = factory->NewFromUtf8(flagsStr, flagsLen);
|
||||
delete[] flagsStr;
|
||||
return flagsString.GetTaggedValue().GetRawData();
|
||||
}
|
||||
|
||||
void RuntimeStubs::Initialize(JSThread *thread)
|
||||
{
|
||||
#define DEF_RUNTIME_STUB(name) kungfu::RuntimeStubCSigns::ID_##name
|
||||
|
@ -163,3 +163,4 @@ true
|
||||
true
|
||||
true
|
||||
true
|
||||
dgimsuy
|
||||
|
@ -672,3 +672,8 @@ pattern = /\d$/gm;
|
||||
result = string.match(pattern);
|
||||
print(1 == result.length);
|
||||
print('9' == result[0]);
|
||||
|
||||
// test getFlags
|
||||
const res = /abc/giymsud;
|
||||
res.lastIndex = -1;
|
||||
print(res.flags);
|
||||
|
Loading…
Reference in New Issue
Block a user