• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }