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(thread);
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(thread);
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(thread, 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(thread);
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(thread);
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 uint32_t queueSize = job::MicroJobQueue::GetPromiseQueueSize(thread, thread->GetEcmaVM()->GetMicroJobQueue());
282 promiseQueueSizeRecord_.push(queueSize);
283 }
284
GetPromiseQueueSizeRecordOfTopFrame()285 uint32_t DropframeManager::GetPromiseQueueSizeRecordOfTopFrame()
286 {
287 ASSERT(!promiseQueueSizeRecord_.empty());
288 return promiseQueueSizeRecord_.top();
289 }
290
PopPromiseQueueSizeRecord()291 void DropframeManager::PopPromiseQueueSizeRecord()
292 {
293 if (!promiseQueueSizeRecord_.empty()) {
294 promiseQueueSizeRecord_.pop();
295 }
296 }
297
PushMethodInfo(std::tuple<JSPandaFile *,panda_file::File::EntityId> methodInfo)298 void DropframeManager::PushMethodInfo(std::tuple<JSPandaFile*,
299 panda_file::File::EntityId> methodInfo)
300 {
301 methodInfo_.push(methodInfo);
302 }
303
CheckExitMethodInfo(std::tuple<JSPandaFile *,panda_file::File::EntityId> methodInfo)304 bool DropframeManager::CheckExitMethodInfo(std::tuple<JSPandaFile*,
305 panda_file::File::EntityId> methodInfo)
306 {
307 if (methodInfo_.empty()) {
308 return false;
309 }
310 if (methodInfo == methodInfo_.top()) {
311 return true;
312 }
313 return false;
314 }
315
PopMethodInfo()316 void DropframeManager::PopMethodInfo()
317 {
318 if (!methodInfo_.empty()) {
319 methodInfo_.pop();
320 }
321 }
322
PushMethodType(MethodType methodType)323 void DropframeManager::PushMethodType(MethodType methodType)
324 {
325 methodType_.push(methodType);
326 }
327
CheckIsSendableMethod()328 bool DropframeManager::CheckIsSendableMethod()
329 {
330 ASSERT(!methodType_.empty());
331 return methodType_.top() == MethodType::SENDABLE_METHOD;
332 }
333
PopMethodType()334 void DropframeManager::PopMethodType()
335 {
336 if (!methodType_.empty()) {
337 methodType_.pop();
338 }
339 }
340 }