1 /**
2 * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include <cstdint>
16 #include <iterator>
17 #include "ets_class_root.h"
18 #include "ets_vm.h"
19 #include "include/mem/panda_containers.h"
20 #include "macros.h"
21 #include "mem/vm_handle.h"
22 #include "plugins/ets/runtime/regexp/regexp_executor.h"
23 #include "plugins/ets/runtime/types/ets_array.h"
24 #include "plugins/ets/runtime/types/ets_string.h"
25 #include "plugins/ets/runtime/types/ets_field.h"
26 #include "runtime/regexp/ecmascript/regexp_parser.h"
27 #include "runtime/include/mem/panda_string.h"
28 #include "runtime/handle_scope-inl.h"
29 #include "types/ets_object.h"
30 #include "types/ets_primitives.h"
31 #include <array>
32 #include "plugins/ets/runtime/regexp/regexp.h"
33
34 namespace ark::ets::intrinsics {
35 using RegExpParser = ark::RegExpParser;
36 using RegExpExecutor = ark::ets::RegExpExecutor;
37 using Array = ark::coretypes::Array;
38
39 namespace {
40
41 constexpr const int LAST_PAREN_FIELDS_COUNT = 10;
42
43 constexpr const char *GROUP_NAMES_FIELD_NAME = "groupNames";
44 constexpr const char *LAST_INDEX_FIELD_NAME = "lastIndex";
45 constexpr const char *FLAGS_FIELD_NAME = "flags_";
46 constexpr std::array<const char *, LAST_PAREN_FIELDS_COUNT> PAREN_FIELD_NAMES = {"", "$1_", "$2_", "$3_", "$4_",
47 "$5_", "$6_", "$7_", "$8_", "$9_"};
48 constexpr const char *LAST_MATCH_FIELD_NAME = "lastMatch_";
49 constexpr const char *INPUT_STATIC_FIELD_NAME = "input_";
50 constexpr const char *LAST_PAREN_FIELD_NAME = "lastParen_";
51 constexpr const char *LEFT_CONTEXT_FIELD_NAME = "leftContext_";
52 constexpr const char *RIGHT_CONTEXT_FIELD_NAME = "rightContext_";
53
54 constexpr const char *RESULT_CLASS_NAME = "Lescompat/RegExpExecArray;";
55 constexpr const char *INDEX_FIELD_NAME = "index";
56 constexpr const char *INPUT_FIELD_NAME = "input";
57 constexpr const char *INDICES_FIELD_NAME = "indices";
58 constexpr const char *RESULT_FIELD_NAME = "result_";
59 constexpr const char *IS_CORRECT_FIELD_NAME = "isCorrect";
60 constexpr const char *GROUPS_FIELD_NAME = "groupsRaw_";
61
62 constexpr const auto REG_EXP_PARSER_EXT_FLAG_EXT_UNICODE = (1U << 7U);
63
64 constexpr const uint32_t INDICES_DIMENSIONS_NUM = 2;
65
CastToBitMask(EtsString * checkStr)66 uint32_t CastToBitMask(EtsString *checkStr)
67 {
68 uint32_t flagsBits = 0;
69 uint32_t flagsBitsTemp = 0;
70 for (int i = 0; i < checkStr->GetLength(); i++) {
71 switch (checkStr->At(i)) {
72 case 'd':
73 flagsBitsTemp = RegExpParser::FLAG_HASINDICES;
74 break;
75 case 'g':
76 flagsBitsTemp = RegExpParser::FLAG_GLOBAL;
77 break;
78 case 'i':
79 flagsBitsTemp = RegExpParser::FLAG_IGNORECASE;
80 break;
81 case 'm':
82 flagsBitsTemp = RegExpParser::FLAG_MULTILINE;
83 break;
84 case 's':
85 flagsBitsTemp = RegExpParser::FLAG_DOTALL;
86 break;
87 case 'u':
88 flagsBitsTemp = RegExpParser::FLAG_UTF16;
89 break;
90 case 'y':
91 flagsBitsTemp = RegExpParser::FLAG_STICKY;
92 break;
93 case 'v':
94 flagsBitsTemp = REG_EXP_PARSER_EXT_FLAG_EXT_UNICODE;
95 break;
96 default: {
97 auto *thread = ManagedThread::GetCurrent();
98 auto ctx = PandaEtsVM::GetCurrent()->GetLanguageContext();
99 std::string message = "invalid regular expression flags";
100 ark::ThrowException(ctx, thread, utf::CStringAsMutf8("Lstd/core/IllegalArgumentException;"),
101 utf::CStringAsMutf8(message.c_str()));
102 return 0;
103 }
104 }
105 if ((flagsBits & flagsBitsTemp) != 0) {
106 auto *thread = ManagedThread::GetCurrent();
107 auto ctx = PandaEtsVM::GetCurrent()->GetLanguageContext();
108 std::string message = "invalid regular expression flags";
109 ark::ThrowException(ctx, thread, utf::CStringAsMutf8("Lstd/core/IllegalArgumentException;"),
110 utf::CStringAsMutf8(message.c_str()));
111 return 0;
112 }
113 flagsBits |= flagsBitsTemp;
114 }
115 return flagsBits;
116 }
117
118 struct ExecuteOptions {
119 EtsInt lastIndex;
120 bool hasIndices;
121 bool hasSlashU;
122 };
123
124 } // namespace
125
SetFlags(EtsObject * regexpObject,EtsString * checkStr)126 void SetFlags(EtsObject *regexpObject, EtsString *checkStr)
127 {
128 auto *coroutine = EtsCoroutine::GetCurrent();
129 [[maybe_unused]] HandleScope<ObjectHeader *> scope(coroutine);
130 VMHandle<EtsObject> regexp(coroutine, regexpObject->GetCoreType());
131
132 auto *regexpClass = regexp->GetClass();
133 EtsField *flagsField = regexpClass->GetDeclaredFieldIDByName(FLAGS_FIELD_NAME);
134
135 auto flags = checkStr->GetMutf8();
136 std::sort(flags.begin(), flags.end());
137 VMHandle<EtsString> newFlags(coroutine, EtsString::CreateFromMUtf8(flags.c_str())->GetCoreType());
138 regexp->SetFieldObject(flagsField, newFlags->AsObject());
139 }
140
SetGroupNames(EtsObject * regexpObject,const RegExpParser & parser)141 void SetGroupNames(EtsObject *regexpObject, const RegExpParser &parser)
142 {
143 auto *coroutine = EtsCoroutine::GetCurrent();
144 [[maybe_unused]] HandleScope<ObjectHeader *> scope(coroutine);
145 VMHandle<EtsObject> regexp(coroutine, regexpObject->GetCoreType());
146 auto *regexpClass = regexp->GetClass();
147
148 auto *classLinker = PandaEtsVM::GetCurrent()->GetClassLinker();
149 auto *stringClass = classLinker->GetClassRoot(EtsClassRoot::STRING);
150
151 auto groupName = parser.GetGroupNames();
152 EtsObjectArray *etsGroupNames = EtsObjectArray::Create(stringClass, groupName.size());
153 VMHandle<EtsObjectArray> arrHandle(coroutine, etsGroupNames->GetCoreType());
154
155 EtsField *groupNamesField = regexpClass->GetDeclaredFieldIDByName(GROUP_NAMES_FIELD_NAME);
156 for (size_t i = 0; i < groupName.size(); ++i) {
157 VMHandle<EtsString> str(coroutine,
158 EtsString::CreateFromMUtf8(groupName[i].c_str(), groupName[i].size())->GetCoreType());
159 arrHandle.GetPtr()->Set(i, str->AsObject());
160 }
161 regexp.GetPtr()->SetFieldObject(groupNamesField, arrHandle.GetPtr()->AsObject());
162 }
163
EscompatRegExpCompile(EtsObject * regexpObj)164 extern "C" EtsObject *EscompatRegExpCompile(EtsObject *regexpObj)
165 {
166 // NOTE(kparshukov): remove #20259
167 auto *coroutine = EtsCoroutine::GetCurrent();
168 [[maybe_unused]] HandleScope<ObjectHeader *> scope(coroutine);
169
170 VMHandle<EtsObject> regexp(coroutine, regexpObj->GetCoreType());
171 return regexp.GetPtr();
172 }
173
EscompatRegExpParse(EtsString * pattern)174 extern "C" EtsString *EscompatRegExpParse(EtsString *pattern)
175 {
176 RegExpParser parse = RegExpParser();
177 auto patternstr = ark::PandaStringToStd(pattern->GetUtf8());
178 parse.Init(const_cast<char *>(reinterpret_cast<const char *>(patternstr.c_str())), patternstr.length(), 0);
179 parse.Parse();
180 if (parse.IsError()) {
181 auto errormsg = ark::PandaStringToStd(parse.GetErrorMsg());
182 return EtsString::CreateFromMUtf8(errormsg.c_str(), errormsg.length());
183 }
184 return nullptr;
185 }
186
SetSuccessfulMatchLegacyProperties(EtsClass * type,const EtsObject * regexpExecArrayObj,EtsString * inputStrObj,uint32_t index)187 void SetSuccessfulMatchLegacyProperties(EtsClass *type, const EtsObject *regexpExecArrayObj, EtsString *inputStrObj,
188 uint32_t index)
189 {
190 auto *coroutine = EtsCoroutine::GetCurrent();
191 [[maybe_unused]] HandleScope<ObjectHeader *> scope(coroutine);
192
193 EtsClass *resultClass = regexpExecArrayObj->GetClass();
194 auto *resultField = resultClass->GetDeclaredFieldIDByName(RESULT_FIELD_NAME);
195 VMHandle<EtsObjectArray> matches(coroutine, regexpExecArrayObj->GetFieldObject(resultField)->GetCoreType());
196 ASSERT(matches->GetLength() != 0);
197
198 VMHandle<EtsString> inputStr(coroutine, inputStrObj->GetCoreType());
199 VMHandle<EtsString> emptyString(coroutine, EtsString::CreateNewEmptyString()->GetCoreType());
200
201 EtsField *lastMatchField = type->GetStaticFieldIDByName(LAST_MATCH_FIELD_NAME);
202 type->SetStaticFieldObject(lastMatchField, matches->Get(0U));
203 for (size_t i = 1; i < LAST_PAREN_FIELDS_COUNT; ++i) {
204 EtsField *parenField = type->GetStaticFieldIDByName(PAREN_FIELD_NAMES[i]);
205 if (i < matches->GetLength()) {
206 type->SetStaticFieldObject(parenField, matches->Get(i));
207 } else {
208 type->SetStaticFieldObject(parenField, emptyString->AsObject());
209 }
210 }
211
212 EtsField *inputField = type->GetStaticFieldIDByName(INPUT_STATIC_FIELD_NAME);
213 type->SetStaticFieldObject(inputField, inputStr->AsObject());
214
215 EtsField *lastParenField = type->GetStaticFieldIDByName(LAST_PAREN_FIELD_NAME);
216 if (matches->GetLength() > 1) {
217 type->SetStaticFieldObject(lastParenField, matches->Get(matches->GetLength() - 1U));
218 } else {
219 type->SetStaticFieldObject(lastParenField, emptyString->AsObject());
220 }
221
222 EtsField *leftContextField = type->GetStaticFieldIDByName(LEFT_CONTEXT_FIELD_NAME);
223 EtsString *prefix = EtsString::FastSubString(inputStr.GetPtr(), 0, index);
224 type->SetStaticFieldObject(leftContextField, prefix->AsObject());
225
226 EtsField *rightContextField = type->GetStaticFieldIDByName(RIGHT_CONTEXT_FIELD_NAME);
227 auto suffixBegin = index + EtsString::FromEtsObject(matches->Get(0U))->GetLength();
228 EtsString *suffix = EtsString::FastSubString(inputStr.GetPtr(), suffixBegin, inputStr->GetLength() - suffixBegin);
229 type->SetStaticFieldObject(rightContextField, suffix->AsObject());
230 }
231
SetUnsuccessfulMatchLegacyProperties(EtsClass * type)232 void SetUnsuccessfulMatchLegacyProperties(EtsClass *type)
233 {
234 auto *coroutine = EtsCoroutine::GetCurrent();
235 [[maybe_unused]] HandleScope<ObjectHeader *> scope(coroutine);
236 VMHandle<EtsString> emptyString(coroutine, EtsString::CreateNewEmptyString()->GetCoreType());
237
238 {
239 EtsField *lastMatchField = type->GetStaticFieldIDByName(LAST_MATCH_FIELD_NAME);
240 type->SetStaticFieldObject(lastMatchField, emptyString->AsObject());
241 }
242 for (size_t i = 1; i < LAST_PAREN_FIELDS_COUNT; ++i) {
243 EtsField *lastParenField = type->GetStaticFieldIDByName(PAREN_FIELD_NAMES[i]);
244 type->SetStaticFieldObject(lastParenField, emptyString->AsObject());
245 }
246 }
247
ExtractString(EtsString * from,const bool asUtf16)248 PandaVector<uint8_t> ExtractString(EtsString *from, const bool asUtf16)
249 {
250 auto *coroutine = EtsCoroutine::GetCurrent();
251 [[maybe_unused]] HandleScope<ObjectHeader *> scope(coroutine);
252 VMHandle<EtsString> inputStr(coroutine, from->GetCoreType());
253 PandaVector<uint8_t> result;
254 if (asUtf16) {
255 const auto size = inputStr.GetPtr()->GetLength();
256 PandaVector<uint16_t> u16Buffer(size);
257 inputStr.GetPtr()->CopyDataUtf16(u16Buffer.data(), size);
258 auto strBuffer = reinterpret_cast<uint8_t *>(u16Buffer.data());
259 const auto newSize = size * 2;
260 for (int i = 0; i < newSize; i++) {
261 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
262 result.push_back(strBuffer[i]);
263 }
264 result.emplace_back(0U);
265 result.emplace_back(0U);
266 } else {
267 const size_t vecLen = inputStr.GetPtr()->GetLength();
268 result.resize(vecLen);
269 const auto data = inputStr.GetPtr()->GetDataMUtf8();
270 for (size_t i = 0U; i < vecLen; i++) {
271 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
272 result[i] = data[i];
273 }
274 result.emplace_back(0U);
275 }
276 return result;
277 }
278
Execute(EtsString * pattern,EtsString * flags,EtsString * inputStrObj,ExecuteOptions options)279 RegExpExecResult Execute(EtsString *pattern, EtsString *flags, EtsString *inputStrObj, ExecuteOptions options)
280 {
281 auto *coroutine = EtsCoroutine::GetCurrent();
282 [[maybe_unused]] HandleScope<ObjectHeader *> scope(coroutine);
283 VMHandle<EtsString> patternHandle(coroutine, pattern->GetCoreType());
284 VMHandle<EtsString> flagsHandle(coroutine, flags->GetCoreType());
285 VMHandle<EtsString> inputStr(coroutine, inputStrObj->GetCoreType());
286
287 const auto stringLength = inputStr.GetPtr()->GetLength();
288 const bool isUtf16Str = inputStr->IsUtf16();
289 const bool isUtf16Pattern = patternHandle->IsUtf16();
290 auto re = EtsRegExp();
291 re.SetFlags(flagsHandle.GetPtr());
292
293 const bool isUtf16 = options.hasSlashU || isUtf16Str || isUtf16Pattern || re.IsUtf16();
294
295 auto str = ExtractString(inputStr.GetPtr(), isUtf16);
296 auto patternStr = ExtractString(patternHandle.GetPtr(), isUtf16);
297 auto compiled = re.Compile(patternStr, isUtf16, patternHandle.GetPtr()->GetLength());
298 if (!compiled) {
299 RegExpExecResult badResult;
300 badResult.isSuccess = false;
301 return badResult;
302 }
303
304 auto result = re.Execute(str, stringLength, options.lastIndex);
305 re.Destroy();
306 if (!options.hasIndices) {
307 result.indices.clear();
308 }
309 return result;
310 }
311
SetResultField(EtsObject * regexpExecArrayObj,const PandaVector<std::pair<bool,PandaString>> & matches,bool isWide)312 void SetResultField(EtsObject *regexpExecArrayObj, const PandaVector<std::pair<bool, PandaString>> &matches,
313 bool isWide)
314 {
315 auto *coroutine = EtsCoroutine::GetCurrent();
316 [[maybe_unused]] HandleScope<ObjectHeader *> scope(coroutine);
317 VMHandle<EtsObject> regexpExecArray(coroutine, regexpExecArrayObj->GetCoreType());
318 EtsClass *resultClass = regexpExecArray->GetClass();
319
320 auto *classLinker = PandaEtsVM::GetCurrent()->GetClassLinker();
321 auto *stringClass = classLinker->GetClassRoot(EtsClassRoot::STRING);
322
323 auto *resultField = resultClass->GetDeclaredFieldIDByName(RESULT_FIELD_NAME);
324 VMHandle<EtsObjectArray> resultArray(coroutine, EtsObjectArray::Create(stringClass, matches.size())->GetCoreType());
325 VMHandle<EtsString> match;
326 for (size_t i = 0; i < matches.size(); ++i) {
327 if (isWide) {
328 match = VMHandle<EtsString>(
329 coroutine, EtsString::CreateFromUtf16(reinterpret_cast<const ets_char *>(matches[i].second.c_str()),
330 matches[i].second.length() / RegExpExecutor::WIDE_CHAR_SIZE)
331 ->GetCoreType());
332 } else {
333 match = VMHandle<EtsString>(
334 coroutine, EtsString::CreateFromUtf8(reinterpret_cast<const char *>(matches[i].second.c_str()),
335 matches[i].second.length())
336 ->GetCoreType());
337 }
338 resultArray->Set(i, match->AsObject());
339 }
340 regexpExecArray->SetFieldObject(resultField, resultArray->AsObject());
341 }
342
SetIndicesField(EtsObject * regexpExecArrayObj,const PandaVector<std::pair<uint32_t,uint32_t>> & indices,bool hasIndices)343 void SetIndicesField(EtsObject *regexpExecArrayObj, const PandaVector<std::pair<uint32_t, uint32_t>> &indices,
344 bool hasIndices)
345 {
346 auto *coroutine = EtsCoroutine::GetCurrent();
347 [[maybe_unused]] HandleScope<ObjectHeader *> scope(coroutine);
348 VMHandle<EtsObject> regexpExecArray(coroutine, regexpExecArrayObj->GetCoreType());
349 auto *classLinker = PandaEtsVM::GetCurrent()->GetClassLinker();
350
351 EtsClass *resultClass = regexpExecArray->GetClass();
352 auto *indicesField = resultClass->GetDeclaredFieldIDByName(INDICES_FIELD_NAME);
353
354 if (!hasIndices) {
355 VMHandle<EtsDoubleArray> defaultVal(coroutine, EtsDoubleArray::Create(0)->GetCoreType());
356 regexpExecArray->SetFieldObject(indicesField, defaultVal->AsObject());
357 return;
358 }
359
360 VMHandle<EtsObjectArray> indicesArray(
361 coroutine,
362 EtsObjectArray::Create(classLinker->GetClassRoot(EtsClassRoot::DOUBLE_ARRAY), indices.size())->GetCoreType());
363 for (size_t i = 0; i < indices.size(); ++i) {
364 VMHandle<EtsDoubleArray> index(coroutine, EtsDoubleArray::Create(INDICES_DIMENSIONS_NUM)->GetCoreType());
365 index->Set(0, static_cast<EtsDouble>(indices[i].first));
366 index->Set(1, static_cast<EtsDouble>(indices[i].second));
367 indicesArray->Set(i, index->AsObject());
368 }
369 regexpExecArray->SetFieldObject(indicesField, indicesArray->AsObject());
370 }
371
SetIsCorrectField(EtsObject * regexpExecArrayObj,bool value)372 void SetIsCorrectField(EtsObject *regexpExecArrayObj, bool value)
373 {
374 auto *coroutine = EtsCoroutine::GetCurrent();
375 [[maybe_unused]] HandleScope<ObjectHeader *> scope(coroutine);
376 VMHandle<EtsObject> regexpExecArray(coroutine, regexpExecArrayObj->GetCoreType());
377 EtsClass *resultClass = regexpExecArray->GetClass();
378 auto *resultCorrectField = resultClass->GetDeclaredFieldIDByName(IS_CORRECT_FIELD_NAME);
379
380 regexpExecArray->SetFieldPrimitive<EtsBoolean>(resultCorrectField, ToEtsBoolean(value));
381 }
382
SetInputField(EtsObject * regexpExecArrayObj,EtsString * inputStrObj)383 void SetInputField(EtsObject *regexpExecArrayObj, EtsString *inputStrObj)
384 {
385 auto *coroutine = EtsCoroutine::GetCurrent();
386 [[maybe_unused]] HandleScope<ObjectHeader *> scope(coroutine);
387 VMHandle<EtsString> inputStr(coroutine, inputStrObj->GetCoreType());
388 VMHandle<EtsObject> regexpExecArray(coroutine, regexpExecArrayObj->GetCoreType());
389 EtsClass *resultClass = regexpExecArray->GetClass();
390 auto *inputField = resultClass->GetDeclaredFieldIDByName(INPUT_FIELD_NAME);
391
392 regexpExecArray->SetFieldObject(inputField, inputStr->AsObject());
393 }
394
SetIndexField(EtsObject * regexpExecArrayObj,uint32_t index)395 void SetIndexField(EtsObject *regexpExecArrayObj, uint32_t index)
396 {
397 auto *coroutine = EtsCoroutine::GetCurrent();
398 [[maybe_unused]] HandleScope<ObjectHeader *> scope(coroutine);
399 VMHandle<EtsObject> regexpExecArray(coroutine, regexpExecArrayObj->GetCoreType());
400 EtsClass *resultClass = regexpExecArray->GetClass();
401
402 auto *indexField = resultClass->GetDeclaredFieldIDByName(INDEX_FIELD_NAME);
403 regexpExecArray->SetFieldPrimitive<EtsDouble>(indexField, static_cast<EtsDouble>(index));
404 }
405
SetLastIndexField(EtsObject * regexp,EtsField * lastIndexField,bool global,bool sticky,EtsDouble value)406 void SetLastIndexField(EtsObject *regexp, EtsField *lastIndexField, bool global, bool sticky, EtsDouble value)
407 {
408 if (!global && !sticky) {
409 return;
410 }
411 regexp->SetFieldPrimitive<EtsDouble>(lastIndexField, value);
412 }
413
SetGroupsField(EtsObject * regexpExecArrayObj,const std::map<PandaString,std::pair<int32_t,int32_t>> & groups)414 void SetGroupsField(EtsObject *regexpExecArrayObj, const std::map<PandaString, std::pair<int32_t, int32_t>> &groups)
415 {
416 auto *coroutine = EtsCoroutine::GetCurrent();
417 [[maybe_unused]] HandleScope<ObjectHeader *> scope(coroutine);
418 VMHandle<EtsObject> regexpExecArray(coroutine, regexpExecArrayObj->GetCoreType());
419 EtsClass *resultClass = regexpExecArray->GetClass();
420 auto *groupsField = resultClass->GetDeclaredFieldIDByName(GROUPS_FIELD_NAME);
421 PandaString data;
422 for (const auto &[key, value] : groups) {
423 data += key + "," + ToPandaString(value.first) + "," + ToPandaString(value.second) + ";";
424 }
425
426 EtsString *groupsStr = EtsString::CreateFromMUtf8(data.c_str(), data.size());
427 regexpExecArray->SetFieldObject(groupsField, groupsStr->AsObject());
428 }
429
EscompatRegExpExec(EtsObject * obj,EtsString * patternStr,EtsString * flagsStr,EtsString * str,EtsBoolean hasSlashU)430 extern "C" EtsObject *EscompatRegExpExec(EtsObject *obj, EtsString *patternStr, EtsString *flagsStr, EtsString *str,
431 EtsBoolean hasSlashU)
432 {
433 auto *coroutine = EtsCoroutine::GetCurrent();
434 [[maybe_unused]] HandleScope<ObjectHeader *> scope(coroutine);
435
436 auto *classLinker = PandaEtsVM::GetCurrent()->GetClassLinker();
437
438 VMHandle<EtsObject> regexp(coroutine, obj->GetCoreType());
439 VMHandle<EtsString> pattern(coroutine, patternStr->GetCoreType());
440 VMHandle<EtsString> flags(coroutine, flagsStr->GetCoreType());
441 VMHandle<EtsString> strHandle(coroutine, str->GetCoreType());
442 auto *regexpResultArrayClass = classLinker->GetClass(RESULT_CLASS_NAME);
443 VMHandle<EtsObject> regexpExecArrayObject(coroutine, EtsObject::Create(regexpResultArrayClass)->GetCoreType());
444
445 auto *regexpClass = regexp->GetClass();
446
447 EtsField *lastIndexField = regexpClass->GetDeclaredFieldIDByName(LAST_INDEX_FIELD_NAME);
448 auto lastIdx = static_cast<int32_t>(regexp.GetPtr()->GetFieldPrimitive<EtsDouble>(lastIndexField));
449 auto flagsBits = static_cast<uint8_t>(CastToBitMask(flags.GetPtr()));
450
451 bool global = (flagsBits & RegExpParser::FLAG_GLOBAL) > 0;
452 bool sticky = (flagsBits & RegExpParser::FLAG_STICKY) > 0;
453 bool hasIndices = (flagsBits & RegExpParser::FLAG_HASINDICES) > 0;
454 if ((!global && !sticky) || lastIdx < 0) {
455 lastIdx = 0;
456 }
457 EtsInt strLen = strHandle->GetLength();
458 if (lastIdx > strLen) {
459 SetLastIndexField(regexp.GetPtr(), lastIndexField, global, sticky, 0.0);
460 SetUnsuccessfulMatchLegacyProperties(regexpClass);
461 SetIsCorrectField(regexpExecArrayObject.GetPtr(), false);
462 return regexpExecArrayObject.GetPtr();
463 }
464
465 auto execResult = Execute(pattern.GetPtr(), flags.GetPtr(), strHandle.GetPtr(),
466 ExecuteOptions {lastIdx, hasIndices, static_cast<bool>(hasSlashU)});
467 if (!execResult.isSuccess) {
468 SetLastIndexField(regexp.GetPtr(), lastIndexField, global, sticky, 0.0);
469 SetUnsuccessfulMatchLegacyProperties(regexpClass);
470 SetIsCorrectField(regexpExecArrayObject.GetPtr(), false);
471 return regexpExecArrayObject.GetPtr();
472 }
473
474 SetLastIndexField(regexp.GetPtr(), lastIndexField, global, sticky, static_cast<EtsDouble>(execResult.endIndex));
475 SetIsCorrectField(regexpExecArrayObject.GetPtr(), true);
476 SetIndexField(regexpExecArrayObject.GetPtr(), execResult.index);
477 SetInputField(regexpExecArrayObject.GetPtr(), strHandle.GetPtr());
478 SetResultField(regexpExecArrayObject.GetPtr(), execResult.captures, execResult.isWide);
479 SetSuccessfulMatchLegacyProperties(regexpClass, regexpExecArrayObject.GetPtr(), strHandle.GetPtr(),
480 execResult.index);
481 SetIndicesField(regexpExecArrayObject.GetPtr(), execResult.indices, hasIndices);
482 SetGroupsField(regexpExecArrayObject.GetPtr(), execResult.namedGroups);
483 return regexpExecArrayObject.GetPtr();
484 }
485 } // namespace ark::ets::intrinsics
486