• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 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 
16 #ifndef ECMASCRIPT_BUILTINS_BUILTINS_REGEXP_INL_H
17 #define ECMASCRIPT_BUILTINS_BUILTINS_REGEXP_INL_H
18 
19 #include "ecmascript/global_env.h"
20 #include "ecmascript/js_regexp.h"
21 #include "ecmascript/js_array.h"
22 
23 namespace panda::ecmascript::builtins {
24 
25 /* static */
26 template <int N>
GetCapture(JSThread * thread)27 JSTaggedValue RegExpGlobalResult::GetCapture(JSThread *thread)
28 {
29     JSHandle<JSTaggedValue> table = JSHandle<JSTaggedValue>(thread, RegExpExecResultCache::GetGlobalTable(thread));
30     JSHandle<RegExpGlobalResult> globalTable = JSHandle<RegExpGlobalResult>::Cast(table);
31     JSTaggedValue res = globalTable->Get(thread, CAPTURE_START_INDEX + N - 1);
32     int captureNum = globalTable->GetTotalCaptureCounts().GetInt();
33     if (res.IsHole() && (N < captureNum)) {
34         int startIndex = globalTable->GetStartOfCaptureIndex(N).GetInt();
35         int endIndex = globalTable->GetEndOfCaptureIndex(N).GetInt();
36         int len = endIndex - startIndex;
37         if (len < 0) {
38             res = JSTaggedValue::Undefined();
39         } else {
40             res = JSTaggedValue(EcmaStringAccessor::FastSubString(thread->GetEcmaVM(),
41                 JSHandle<EcmaString>(thread, EcmaString::Cast(globalTable->GetInputString(thread))),
42                 static_cast<uint32_t>(startIndex), static_cast<uint32_t>(len)));
43         }
44         globalTable->Set(thread, CAPTURE_START_INDEX + N - 1, res);
45     } else if (res.IsHole()) {
46         res = thread->GetEcmaVM()->GetFactory()->GetEmptyString().GetTaggedValue();
47         globalTable->Set(thread, CAPTURE_START_INDEX + N - 1, res);
48     }
49     return res;
50 }
51 
52 template <RBMode mode>
FindCachedResult(JSThread * thread,const JSHandle<JSTaggedValue> input,CacheType type,const JSHandle<JSTaggedValue> regexp,JSTaggedValue lastIndexInput,JSHandle<JSTaggedValue> extend,bool isIntermediateResult)53 JSTaggedValue RegExpExecResultCache::FindCachedResult(JSThread *thread,
54                                                       const JSHandle<JSTaggedValue> input, CacheType type,
55                                                       const JSHandle<JSTaggedValue> regexp,
56                                                       JSTaggedValue lastIndexInput, JSHandle<JSTaggedValue> extend,
57                                                       bool isIntermediateResult)
58 {
59     JSHandle<JSRegExp> regexpObj(regexp);
60     JSTaggedValue pattern = regexpObj->GetOriginalSource<mode>(thread);
61     JSTaggedValue flags = regexpObj->GetOriginalFlags<mode>(thread);
62     JSTaggedValue inputValue = input.GetTaggedValue();
63     JSTaggedValue extendValue = extend.GetTaggedValue();
64     if (!pattern.IsString() || !flags.IsInt() || !input->IsString() || !lastIndexInput.IsInt()) {
65         return JSTaggedValue::Undefined();
66     }
67     uint32_t hash = pattern.GetKeyHashCode(thread) + static_cast<uint32_t>(flags.GetInt()) +
68                     input->GetKeyHashCode(thread) + static_cast<uint32_t>(lastIndexInput.GetInt());
69     uint32_t entry = hash & static_cast<uint32_t>(GetCacheLength() - 1);
70     if (!Match<mode>(thread, entry, pattern, flags, inputValue, lastIndexInput, extendValue, type)) {
71         uint32_t entry2 = (entry + 1) & static_cast<uint32_t>(GetCacheLength() - 1);
72         if (!Match<mode>(thread, entry2, pattern, flags, inputValue, lastIndexInput, extendValue, type)) {
73             return JSTaggedValue::Undefined();
74         }
75         entry = entry2;
76     }
77     ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
78         static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(UINT32_MAX));
79     uint32_t index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
80     // update cached value if input value is changed
81     JSTaggedValue cachedStr = Get<mode>(thread, index + INPUT_STRING_INDEX);
82     if (!cachedStr.IsUndefined() && cachedStr != inputValue) {
83         Set(thread, index + INPUT_STRING_INDEX, inputValue);
84     }
85     JSTaggedValue result;
86     switch (type) {
87         case REPLACE_TYPE:
88             result = Get<mode>(thread, index + RESULT_REPLACE_INDEX);
89             break;
90         case SPLIT_TYPE:
91             result = Get<mode>(thread, index + RESULT_SPLIT_INDEX);
92             break;
93         case MATCH_TYPE:
94             result = Get<mode>(thread, index + RESULT_MATCH_INDEX);
95             break;
96         case EXEC_TYPE:
97             result = Get<mode>(thread, index + RESULT_EXEC_INDEX);
98             break;
99         case INTERMEDIATE_REPLACE_TYPE:
100             result = Get<mode>(thread, index + RESULT_INTERMEDIATE_REPLACE_INDEX);
101             break;
102         case TEST_TYPE:
103             result = Get<mode>(thread, index + RESULT_TEST_INDEX);
104             break;
105         case SEARCH_TYPE:
106             result = Get<mode>(thread, index + RESULT_SEARCH_INDEX);
107             break;
108         default:
109             LOG_ECMA(FATAL) << "this branch is unreachable";
110             UNREACHABLE();
111             break;
112     }
113     SetLastMatchGlobalTableIndex(thread, index);
114     SetUseLastMatch(thread, true);
115     SetNeedUpdateGlobal(thread, true);
116     SetHitCount(thread, GetHitCount() + 1);
117     if (type != SEARCH_TYPE && type != SPLIT_TYPE) {
118         BuiltinsRegExp::SetLastIndex(thread, regexp, Get<RBMode::FAST_NO_RB>(thread, index + LAST_INDEX_INDEX), true);
119     }
120     if (!isIntermediateResult && result.IsJSArray()) {
121         JSHandle<JSArray> resultHandle(thread, JSArray::Cast(result));
122         JSHandle<JSArray> copyArray = thread->GetEcmaVM()->GetFactory()->CloneArrayLiteral(resultHandle);
123         return copyArray.GetTaggedValue();
124     }
125     return result;
126 }
127 
128 template <RBMode mode>
Match(JSThread * thread,int entry,JSTaggedValue & pattern,JSTaggedValue & flags,JSTaggedValue & input,JSTaggedValue & lastIndexInputValue,JSTaggedValue & extend,CacheType type)129 bool RegExpExecResultCache::Match(JSThread *thread, int entry, JSTaggedValue &pattern, JSTaggedValue &flags,
130                                   JSTaggedValue &input, JSTaggedValue &lastIndexInputValue, JSTaggedValue &extend,
131                                   CacheType type)
132 {
133     ASSERT((static_cast<size_t>(CACHE_TABLE_HEADER_SIZE) +
134             static_cast<size_t>(entry) * static_cast<size_t>(ENTRY_SIZE)) <= static_cast<size_t>(INT_MAX));
135     int index = CACHE_TABLE_HEADER_SIZE + entry * ENTRY_SIZE;
136 
137     JSTaggedValue typeKey = Get<mode>(thread, index + RESULT_REPLACE_INDEX + type);
138     if (typeKey.IsUndefined()) {
139         return false;
140     }
141 
142     JSTaggedValue keyPattern = Get<mode>(thread, index + PATTERN_INDEX);
143     if (keyPattern.IsUndefined()) {
144         return false;
145     }
146 
147     uint8_t flagsBits = static_cast<uint8_t>(flags.GetInt());
148     JSTaggedValue keyFlags = Get<RBMode::FAST_NO_RB>(thread, index + FLAG_INDEX);
149     uint8_t keyFlagsBits = static_cast<uint8_t>(keyFlags.GetInt());
150     if (flagsBits != keyFlagsBits) {
151         return false;
152     }
153 
154     uint32_t lastIndexInputInt = static_cast<uint32_t>(lastIndexInputValue.GetInt());
155     JSTaggedValue keyLastIndexInput = Get<RBMode::FAST_NO_RB>(thread, index + LAST_INDEX_INPUT_INDEX);
156     uint32_t keyLastIndexInputInt = static_cast<uint32_t>(keyLastIndexInput.GetInt());
157     if (lastIndexInputInt != keyLastIndexInputInt) {
158         return false;
159     }
160 
161     JSTaggedValue keyInput = Get<mode>(thread, index + INPUT_STRING_INDEX);
162     JSTaggedValue keyExtend = Get<mode>(thread, index + EXTEND_INDEX);
163     EcmaString *patternStr = EcmaString::Cast(pattern.GetTaggedObject());
164     EcmaString *inputStr = EcmaString::Cast(input.GetTaggedObject());
165     EcmaString *keyPatternStr = EcmaString::Cast(keyPattern.GetTaggedObject());
166     EcmaString *keyInputStr = EcmaString::Cast(keyInput.GetTaggedObject());
167     bool extendEqual = false;
168     if (extend.IsString() && keyExtend.IsString()) {
169         EcmaString *extendStr = EcmaString::Cast(extend.GetTaggedObject());
170         EcmaString *keyExtendStr = EcmaString::Cast(keyExtend.GetTaggedObject());
171         extendEqual = EcmaStringAccessor::StringsAreEqual<mode>(thread, extendStr, keyExtendStr);
172     } else if (extend.IsUndefined() && keyExtend.IsUndefined()) {
173         extendEqual = true;
174     } else {
175         return false;
176     }
177     return extendEqual &&
178            EcmaStringAccessor::StringsAreEqual<mode>(thread, patternStr, keyPatternStr) &&
179            EcmaStringAccessor::StringsAreEqual<mode>(thread, inputStr, keyInputStr);
180 }
181 }  // namespace panda::ecmascript::builtins
182 #endif  // ECMASCRIPT_BUILTINS_BUILTINS_REGEXP_INL_H
183