1 /*
2 * Copyright (c) 2023 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 #include "ecmascript/debugger/dropframe_manager.h"
17
18 #include "ecmascript/interpreter/interpreter-inl.h"
19 #include "ecmascript/jobs/micro_job_queue.h"
20
21 namespace panda::ecmascript::tooling {
IsNewlexenvOpcode(BytecodeInstruction::Opcode op)22 bool DropframeManager::IsNewlexenvOpcode(BytecodeInstruction::Opcode op)
23 {
24 switch (op) {
25 case BytecodeInstruction::Opcode::NEWLEXENV_IMM8:
26 case BytecodeInstruction::Opcode::NEWLEXENVWITHNAME_IMM8_ID16:
27 case BytecodeInstruction::Opcode::WIDE_NEWLEXENV_PREF_IMM16:
28 case BytecodeInstruction::Opcode::WIDE_NEWLEXENVWITHNAME_PREF_IMM16_ID16:
29 return true;
30 default:
31 break;
32 }
33 return false;
34 }
35
IsStlexvarOpcode(BytecodeInstruction::Opcode op)36 bool DropframeManager::IsStlexvarOpcode(BytecodeInstruction::Opcode op)
37 {
38 switch (op) {
39 case BytecodeInstruction::Opcode::STLEXVAR_IMM4_IMM4:
40 case BytecodeInstruction::Opcode::STLEXVAR_IMM8_IMM8:
41 case BytecodeInstruction::Opcode::WIDE_STLEXVAR_PREF_IMM16_IMM16:
42 return true;
43 default:
44 break;
45 }
46 return false;
47 }
48
ReadStlexvarParams(const uint8_t * pc,BytecodeInstruction::Opcode op)49 std::pair<uint16_t, uint16_t> DropframeManager::ReadStlexvarParams(const uint8_t *pc, BytecodeInstruction::Opcode op)
50 {
51 uint16_t level = 0;
52 uint16_t slot = 0;
53 switch (op) {
54 case BytecodeInstruction::Opcode::STLEXVAR_IMM4_IMM4:
55 level = READ_INST_4_0();
56 slot = READ_INST_4_1();
57 break;
58 case BytecodeInstruction::Opcode::STLEXVAR_IMM8_IMM8:
59 level = READ_INST_8_0();
60 slot = READ_INST_8_1();
61 break;
62 case BytecodeInstruction::Opcode::WIDE_STLEXVAR_PREF_IMM16_IMM16:
63 level = READ_INST_16_1();
64 slot = READ_INST_16_3();
65 break;
66 default:
67 break;
68 }
69 return std::make_pair(level, slot);
70 }
71
MethodEntry(JSThread * thread,JSHandle<Method> method,JSHandle<JSTaggedValue> envHandle)72 void DropframeManager::MethodEntry(JSThread *thread, JSHandle<Method> method, JSHandle<JSTaggedValue> envHandle)
73 {
74 std::set<std::pair<uint16_t, uint16_t>> modifiedLexVarPos;
75 const JSPandaFile* methodJsPandaFile = method->GetJSPandaFile();
76 panda_file::File::EntityId methodId = method->GetMethodId();
77 PushMethodInfo(std::make_tuple(const_cast<JSPandaFile *>(methodJsPandaFile), methodId));
78 if (method->IsSendableMethod()) {
79 PushMethodType(MethodType::SENDABLE_METHOD);
80 return;
81 }
82 NewLexModifyRecordLevel();
83 PushPromiseQueueSizeRecord(thread);
84 if (!envHandle->IsLexicalEnv()) {
85 PushMethodType(MethodType::OTHER_METHOD);
86 return;
87 }
88 PushMethodType(MethodType::NORMAL_METHOD);
89 uint32_t codeSize = method->GetCodeSize();
90 uint16_t newEnvCount = 0;
91 auto bcIns = BytecodeInstruction(method->GetBytecodeArray());
92 auto bcInsLast = bcIns.JumpTo(codeSize);
93 while (bcIns.GetAddress() != bcInsLast.GetAddress()) {
94 AddLexPropertiesToRecord(thread, bcIns, newEnvCount, modifiedLexVarPos, envHandle);
95 bcIns = bcIns.GetNext();
96 }
97 }
98
AddLexPropertiesToRecord(JSThread * thread,BytecodeInstruction & bcIns,uint16_t & newEnvCount,std::set<std::pair<uint16_t,uint16_t>> & modifiedLexVarPos,JSHandle<JSTaggedValue> envHandle)99 void DropframeManager::AddLexPropertiesToRecord(JSThread *thread, BytecodeInstruction &bcIns, uint16_t &newEnvCount,
100 std::set<std::pair<uint16_t, uint16_t>> &modifiedLexVarPos, JSHandle<JSTaggedValue> envHandle)
101 {
102 BytecodeInstruction::Opcode op = bcIns.GetOpcode();
103 if (IsNewlexenvOpcode(op)) {
104 newEnvCount++;
105 return;
106 }
107 if (IsStlexvarOpcode(op)) {
108 std::pair<uint16_t, uint16_t> lexVarPos = ReadStlexvarParams(bcIns.GetAddress(), op);
109 uint16_t level;
110 uint16_t slot;
111 std::tie(level, slot) = lexVarPos;
112 JSTaggedValue env = envHandle.GetTaggedValue();
113 for (uint16_t i = 0; ; i++) {
114 if ((level < newEnvCount || i >= level - newEnvCount) &&
115 slot < LexicalEnv::Cast(env.GetTaggedObject())->GetLength() - LexicalEnv::RESERVED_ENV_LENGTH &&
116 !modifiedLexVarPos.count({i, slot})) {
117 JSTaggedValue value = LexicalEnv::Cast(env.GetTaggedObject())->GetProperties(slot);
118 EmplaceLexModifyRecord(thread, env, slot, value);
119 modifiedLexVarPos.insert({i, slot});
120 }
121 if (i >= level) {
122 break;
123 }
124 JSTaggedValue taggedParentEnv = LexicalEnv::Cast(env.GetTaggedObject())->GetParentEnv();
125 if (!taggedParentEnv.IsLexicalEnv()) {
126 break;
127 }
128 env = taggedParentEnv;
129 }
130 }
131 }
132
MethodExit(JSThread * thread,JSHandle<Method> method)133 void DropframeManager::MethodExit(JSThread *thread, [[maybe_unused]] JSHandle<Method> method)
134 {
135 const JSPandaFile* methodJsPandaFile = method->GetJSPandaFile();
136 panda_file::File::EntityId methodId = method->GetMethodId();
137 if (!CheckExitMethodInfo(std::make_tuple(const_cast<JSPandaFile *>(methodJsPandaFile), methodId))) {
138 return;
139 }
140 PopMethodInfo();
141 if (CheckIsSendableMethod()) {
142 PopMethodType();
143 return;
144 }
145 PopMethodType();
146 MergeLexModifyRecordOfTopFrame(thread);
147 PopPromiseQueueSizeRecord();
148 }
149
DropLastFrame(JSThread * thread)150 void DropframeManager::DropLastFrame(JSThread *thread)
151 {
152 std::vector<std::tuple<JSHandle<JSTaggedValue>, uint16_t, JSHandle<JSTaggedValue>>> lexModifyRecord;
153 lexModifyRecord = GetLexModifyRecordOfTopFrame();
154 for (const auto &item : lexModifyRecord) {
155 JSHandle<JSTaggedValue> envHandle;
156 uint16_t slot;
157 JSHandle<JSTaggedValue> valueHandle;
158 std::tie(envHandle, slot, valueHandle) = item;
159 JSTaggedValue env = envHandle.GetTaggedValue();
160 ASSERT(slot < LexicalEnv::Cast(env.GetTaggedObject())->GetLength() - LexicalEnv::RESERVED_ENV_LENGTH);
161 LexicalEnv::Cast(env.GetTaggedObject())->SetProperties(thread, slot, valueHandle.GetTaggedValue());
162 }
163 PopMethodInfo();
164 PopMethodType();
165 RemoveLexModifyRecordOfTopFrame(thread);
166 PopPromiseQueueSizeRecord();
167
168 FrameHandler frameHandler(thread);
169 bool isEntryFrameDropped = false;
170 while (frameHandler.HasFrame()) {
171 frameHandler.PrevJSFrame();
172 if (frameHandler.IsEntryFrame()) {
173 isEntryFrameDropped = true;
174 continue;
175 }
176 if (frameHandler.IsBuiltinFrame()) {
177 continue;
178 }
179 if (!thread->IsAsmInterpreter()) {
180 JSTaggedType *prevSp = frameHandler.GetSp();
181 thread->SetCurrentFrame(prevSp);
182 }
183 if (isEntryFrameDropped) {
184 thread->SetEntryFrameDroppedState();
185 }
186 thread->SetFrameDroppedState();
187 break;
188 }
189 }
190
NewLexModifyRecordLevel()191 void DropframeManager::NewLexModifyRecordLevel()
192 {
193 modifiedLexVar_.push(std::vector<std::tuple<JSHandle<JSTaggedValue>, uint16_t, JSHandle<JSTaggedValue>>>());
194 }
195
EmplaceLexModifyRecord(JSThread * thread,JSTaggedValue env,uint16_t slot,JSTaggedValue value)196 void DropframeManager::EmplaceLexModifyRecord(JSThread *thread, JSTaggedValue env, uint16_t slot, JSTaggedValue value)
197 {
198 GlobalHandleCollection globalHandleCollection(thread);
199 for (const auto &item : modifiedLexVar_.top()) {
200 JSHandle<JSTaggedValue> envHandleRecord = std::get<0>(item);
201 uint16_t slotRecord = std::get<1>(item);
202 if (envHandleRecord.GetTaggedType() == env.GetRawData() && slotRecord == slot) {
203 return;
204 }
205 }
206 JSHandle<JSTaggedValue> envHandle = globalHandleCollection.NewHandle<JSTaggedValue>(env.GetRawData());
207 JSHandle<JSTaggedValue> valueHandle = globalHandleCollection.NewHandle<JSTaggedValue>(value.GetRawData());
208 modifiedLexVar_.top().emplace_back(envHandle, slot, valueHandle);
209 }
210
GetLexModifyRecordLevel()211 uint32_t DropframeManager::GetLexModifyRecordLevel()
212 {
213 return modifiedLexVar_.size();
214 }
215
216 std::vector<std::tuple<JSHandle<JSTaggedValue>, uint16_t, JSHandle<JSTaggedValue>>>
GetLexModifyRecordOfTopFrame()217 DropframeManager::GetLexModifyRecordOfTopFrame()
218 {
219 if (modifiedLexVar_.empty()) {
220 return std::vector<std::tuple<JSHandle<JSTaggedValue>, uint16_t, JSHandle<JSTaggedValue>>>();
221 }
222 return modifiedLexVar_.top();
223 }
224
RemoveLexModifyRecordOfTopFrame(JSThread * thread)225 void DropframeManager::RemoveLexModifyRecordOfTopFrame(JSThread *thread)
226 {
227 if (modifiedLexVar_.empty()) {
228 return;
229 }
230 GlobalHandleCollection globalHandleCollection(thread);
231 for (const auto &item : modifiedLexVar_.top()) {
232 JSHandle<JSTaggedValue> envHandle = std::get<0>(item);
233 JSHandle<JSTaggedValue> valueHandle = std::get<2>(item); // 2:get the third item of the tuple
234 globalHandleCollection.Dispose(envHandle);
235 globalHandleCollection.Dispose(valueHandle);
236 }
237 modifiedLexVar_.pop();
238 }
239
MergeLexModifyRecordOfTopFrame(JSThread * thread)240 void DropframeManager::MergeLexModifyRecordOfTopFrame(JSThread *thread)
241 {
242 if (modifiedLexVar_.empty()) {
243 return;
244 }
245 GlobalHandleCollection globalHandleCollection(thread);
246 std::vector<std::tuple<JSHandle<JSTaggedValue>, uint16_t, JSHandle<JSTaggedValue>>> lexModifyRecord;
247 lexModifyRecord = modifiedLexVar_.top();
248 modifiedLexVar_.pop();
249 if (!modifiedLexVar_.empty() && modifiedLexVar_.top().empty()) {
250 modifiedLexVar_.pop();
251 modifiedLexVar_.push(lexModifyRecord);
252 return;
253 }
254 for (const auto &item : lexModifyRecord) {
255 JSHandle<JSTaggedValue> envHandle;
256 uint16_t slot;
257 JSHandle<JSTaggedValue> valueHandle;
258 std::tie(envHandle, slot, valueHandle) = item;
259 bool existRecord = false;
260 if (!modifiedLexVar_.empty()) {
261 for (const auto &itemLast : modifiedLexVar_.top()) {
262 JSHandle<JSTaggedValue> envHandleRecord = std::get<0>(itemLast);
263 uint16_t slotRecord = std::get<1>(itemLast);
264 if (envHandleRecord.GetTaggedType() == envHandle.GetTaggedType() && slotRecord == slot) {
265 existRecord = true;
266 break;
267 }
268 }
269 }
270 if (modifiedLexVar_.empty() || existRecord) {
271 globalHandleCollection.Dispose(envHandle);
272 globalHandleCollection.Dispose(valueHandle);
273 } else {
274 modifiedLexVar_.top().emplace_back(envHandle, slot, valueHandle);
275 }
276 }
277 }
278
PushPromiseQueueSizeRecord(JSThread * thread)279 void DropframeManager::PushPromiseQueueSizeRecord(JSThread *thread)
280 {
281 EcmaContext *context = thread->GetCurrentEcmaContext();
282 uint32_t queueSize = job::MicroJobQueue::GetPromiseQueueSize(thread, context->GetMicroJobQueue());
283 promiseQueueSizeRecord_.push(queueSize);
284 }
285
GetPromiseQueueSizeRecordOfTopFrame()286 uint32_t DropframeManager::GetPromiseQueueSizeRecordOfTopFrame()
287 {
288 ASSERT(!promiseQueueSizeRecord_.empty());
289 return promiseQueueSizeRecord_.top();
290 }
291
PopPromiseQueueSizeRecord()292 void DropframeManager::PopPromiseQueueSizeRecord()
293 {
294 if (!promiseQueueSizeRecord_.empty()) {
295 promiseQueueSizeRecord_.pop();
296 }
297 }
298
PushMethodInfo(std::tuple<JSPandaFile *,panda_file::File::EntityId> methodInfo)299 void DropframeManager::PushMethodInfo(std::tuple<JSPandaFile*,
300 panda_file::File::EntityId> methodInfo)
301 {
302 methodInfo_.push(methodInfo);
303 }
304
CheckExitMethodInfo(std::tuple<JSPandaFile *,panda_file::File::EntityId> methodInfo)305 bool DropframeManager::CheckExitMethodInfo(std::tuple<JSPandaFile*,
306 panda_file::File::EntityId> methodInfo)
307 {
308 if (methodInfo_.empty()) {
309 return false;
310 }
311 if (methodInfo == methodInfo_.top()) {
312 return true;
313 }
314 return false;
315 }
316
PopMethodInfo()317 void DropframeManager::PopMethodInfo()
318 {
319 if (!methodInfo_.empty()) {
320 methodInfo_.pop();
321 }
322 }
323
PushMethodType(MethodType methodType)324 void DropframeManager::PushMethodType(MethodType methodType)
325 {
326 methodType_.push(methodType);
327 }
328
CheckIsSendableMethod()329 bool DropframeManager::CheckIsSendableMethod()
330 {
331 ASSERT(!methodType_.empty());
332 return methodType_.top() == MethodType::SENDABLE_METHOD;
333 }
334
PopMethodType()335 void DropframeManager::PopMethodType()
336 {
337 if (!methodType_.empty()) {
338 methodType_.pop();
339 }
340 }
341 }