• 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/frames.h"
19 #include "ecmascript/global_env.h"
20 #include "ecmascript/global_handle_collection.h"
21 #include "ecmascript/interpreter/frame_handler.h"
22 #include "ecmascript/interpreter/interpreter-inl.h"
23 #include "ecmascript/jobs/micro_job_queue.h"
24 
25 namespace panda::ecmascript::tooling {
IsNewlexenvOpcode(BytecodeInstruction::Opcode op)26 bool DropframeManager::IsNewlexenvOpcode(BytecodeInstruction::Opcode op)
27 {
28     switch (op) {
29         case BytecodeInstruction::Opcode::NEWLEXENV_IMM8:
30         case BytecodeInstruction::Opcode::NEWLEXENVWITHNAME_IMM8_ID16:
31         case BytecodeInstruction::Opcode::WIDE_NEWLEXENV_PREF_IMM16:
32         case BytecodeInstruction::Opcode::WIDE_NEWLEXENVWITHNAME_PREF_IMM16_ID16:
33             return true;
34         default:
35             break;
36     }
37     return false;
38 }
39 
IsStlexvarOpcode(BytecodeInstruction::Opcode op)40 bool DropframeManager::IsStlexvarOpcode(BytecodeInstruction::Opcode op)
41 {
42     switch (op) {
43         case BytecodeInstruction::Opcode::STLEXVAR_IMM4_IMM4:
44         case BytecodeInstruction::Opcode::STLEXVAR_IMM8_IMM8:
45         case BytecodeInstruction::Opcode::WIDE_STLEXVAR_PREF_IMM16_IMM16:
46             return true;
47         default:
48             break;
49     }
50     return false;
51 }
52 
ReadStlexvarParams(const uint8_t * pc,BytecodeInstruction::Opcode op)53 std::pair<uint16_t, uint16_t> DropframeManager::ReadStlexvarParams(const uint8_t *pc, BytecodeInstruction::Opcode op)
54 {
55     uint16_t level = 0;
56     uint16_t slot = 0;
57     switch (op) {
58         case BytecodeInstruction::Opcode::STLEXVAR_IMM4_IMM4:
59             level = READ_INST_4_0();
60             slot = READ_INST_4_1();
61             break;
62         case BytecodeInstruction::Opcode::STLEXVAR_IMM8_IMM8:
63             level = READ_INST_8_0();
64             slot = READ_INST_8_1();
65             break;
66         case BytecodeInstruction::Opcode::WIDE_STLEXVAR_PREF_IMM16_IMM16:
67             level = READ_INST_16_1();
68             slot = READ_INST_16_3();
69             break;
70         default:
71             break;
72     }
73     return std::make_pair(level, slot);
74 }
75 
MethodEntry(JSThread * thread,JSHandle<Method> method,JSHandle<JSTaggedValue> envHandle)76 void DropframeManager::MethodEntry(JSThread *thread, JSHandle<Method> method, JSHandle<JSTaggedValue> envHandle)
77 {
78     std::set<std::pair<uint16_t, uint16_t>> modifiedLexVarPos;
79     NewLexModifyRecordLevel();
80     if (envHandle.GetTaggedValue().IsUndefinedOrNull()) {
81         return;
82     }
83     uint32_t codeSize = method->GetCodeSize();
84     uint16_t newEnvCount = 0;
85     auto bcIns = BytecodeInstruction(method->GetBytecodeArray());
86     auto bcInsLast = bcIns.JumpTo(codeSize);
87     while (bcIns.GetAddress() != bcInsLast.GetAddress()) {
88         BytecodeInstruction::Opcode op = bcIns.GetOpcode();
89         if (IsNewlexenvOpcode(op)) {
90             newEnvCount++;
91         } else if (IsStlexvarOpcode(op)) {
92             std::pair<uint16_t, uint16_t> lexVarPos = ReadStlexvarParams(bcIns.GetAddress(), op);
93             uint16_t level;
94             uint16_t slot;
95             std::tie(level, slot) = lexVarPos;
96             JSTaggedValue env = envHandle.GetTaggedValue();
97             for (uint16_t i = 0; ; i++) {
98                 if ((level < newEnvCount || i >= level - newEnvCount) &&
99                     slot < LexicalEnv::Cast(env.GetTaggedObject())->GetLength() - LexicalEnv::RESERVED_ENV_LENGTH &&
100                     !modifiedLexVarPos.count({i, slot})) {
101                     JSTaggedValue value = LexicalEnv::Cast(env.GetTaggedObject())->GetProperties(slot);
102                     EmplaceLexModifyRecord(thread, env, slot, value);
103                     modifiedLexVarPos.insert({i, slot});
104                 }
105                 if (i >= level) {
106                     break;
107                 }
108                 JSTaggedValue taggedParentEnv = LexicalEnv::Cast(env.GetTaggedObject())->GetParentEnv();
109                 if (taggedParentEnv.IsUndefined()) {
110                     break;
111                 }
112                 env = taggedParentEnv;
113             }
114         }
115         bcIns = bcIns.GetNext();
116     }
117     PushPromiseQueueSizeRecord(thread);
118 }
119 
MethodExit(JSThread * thread,JSHandle<Method> method)120 void DropframeManager::MethodExit(JSThread *thread, [[maybe_unused]] JSHandle<Method> method)
121 {
122     MergeLexModifyRecordOfTopFrame(thread);
123     PopPromiseQueueSizeRecord();
124 }
125 
DropLastFrame(JSThread * thread)126 void DropframeManager::DropLastFrame(JSThread *thread)
127 {
128     std::vector<std::tuple<JSHandle<JSTaggedValue>, uint16_t, JSHandle<JSTaggedValue>>> lexModifyRecord;
129     lexModifyRecord = GetLexModifyRecordOfTopFrame();
130     for (const auto &item : lexModifyRecord) {
131         JSHandle<JSTaggedValue> envHandle;
132         uint16_t slot;
133         JSHandle<JSTaggedValue> valueHandle;
134         std::tie(envHandle, slot, valueHandle) = item;
135         JSTaggedValue env = envHandle.GetTaggedValue();
136         ASSERT(slot < LexicalEnv::Cast(env.GetTaggedObject())->GetLength() - LexicalEnv::RESERVED_ENV_LENGTH);
137         LexicalEnv::Cast(env.GetTaggedObject())->SetProperties(thread, slot, valueHandle.GetTaggedValue());
138     }
139     RemoveLexModifyRecordOfTopFrame(thread);
140     PopPromiseQueueSizeRecord();
141 
142     FrameHandler frameHandler(thread);
143     bool isEntryFrameDropped = false;
144     while (frameHandler.HasFrame()) {
145         frameHandler.PrevJSFrame();
146         if (frameHandler.IsEntryFrame()) {
147             isEntryFrameDropped = true;
148             continue;
149         }
150         if (frameHandler.IsBuiltinFrame()) {
151             continue;
152         }
153         if (!thread->IsAsmInterpreter()) {
154             JSTaggedType *prevSp = frameHandler.GetSp();
155             thread->SetCurrentFrame(prevSp);
156         }
157         if (isEntryFrameDropped) {
158             thread->SetEntryFrameDroppedState();
159         }
160         thread->SetFrameDroppedState();
161         break;
162     }
163 }
164 
NewLexModifyRecordLevel()165 void DropframeManager::NewLexModifyRecordLevel()
166 {
167     modifiedLexVar_.push(std::vector<std::tuple<JSHandle<JSTaggedValue>, uint16_t, JSHandle<JSTaggedValue>>>());
168 }
169 
EmplaceLexModifyRecord(JSThread * thread,JSTaggedValue env,uint16_t slot,JSTaggedValue value)170 void DropframeManager::EmplaceLexModifyRecord(JSThread *thread, JSTaggedValue env, uint16_t slot, JSTaggedValue value)
171 {
172     GlobalHandleCollection globalHandleCollection(thread);
173     for (const auto &item : modifiedLexVar_.top()) {
174         JSHandle<JSTaggedValue> envHandleRecord = std::get<0>(item);
175         uint16_t slotRecord = std::get<1>(item);
176         if (envHandleRecord.GetTaggedType() == env.GetRawData() && slotRecord == slot) {
177             return;
178         }
179     }
180     JSHandle<JSTaggedValue> envHandle = globalHandleCollection.NewHandle<JSTaggedValue>(env.GetRawData());
181     JSHandle<JSTaggedValue> valueHandle = globalHandleCollection.NewHandle<JSTaggedValue>(value.GetRawData());
182     modifiedLexVar_.top().emplace_back(envHandle, slot, valueHandle);
183 }
184 
GetLexModifyRecordLevel()185 uint32_t DropframeManager::GetLexModifyRecordLevel()
186 {
187     return modifiedLexVar_.size();
188 }
189 
190 std::vector<std::tuple<JSHandle<JSTaggedValue>, uint16_t, JSHandle<JSTaggedValue>>>
GetLexModifyRecordOfTopFrame()191     DropframeManager::GetLexModifyRecordOfTopFrame()
192 {
193     if (modifiedLexVar_.empty()) {
194         return std::vector<std::tuple<JSHandle<JSTaggedValue>, uint16_t, JSHandle<JSTaggedValue>>>();
195     }
196     return modifiedLexVar_.top();
197 }
198 
RemoveLexModifyRecordOfTopFrame(JSThread * thread)199 void DropframeManager::RemoveLexModifyRecordOfTopFrame(JSThread *thread)
200 {
201     if (modifiedLexVar_.empty()) {
202         return;
203     }
204     GlobalHandleCollection globalHandleCollection(thread);
205     for (const auto &item : modifiedLexVar_.top()) {
206         JSHandle<JSTaggedValue> envHandle = std::get<0>(item);
207         JSHandle<JSTaggedValue> valueHandle = std::get<2>(item); // 2:get the third item of the tuple
208         globalHandleCollection.Dispose(envHandle);
209         globalHandleCollection.Dispose(valueHandle);
210     }
211     modifiedLexVar_.pop();
212 }
213 
MergeLexModifyRecordOfTopFrame(JSThread * thread)214 void DropframeManager::MergeLexModifyRecordOfTopFrame(JSThread *thread)
215 {
216     if (modifiedLexVar_.empty()) {
217         return;
218     }
219     GlobalHandleCollection globalHandleCollection(thread);
220     std::vector<std::tuple<JSHandle<JSTaggedValue>, uint16_t, JSHandle<JSTaggedValue>>> lexModifyRecord;
221     lexModifyRecord = modifiedLexVar_.top();
222     modifiedLexVar_.pop();
223     for (const auto &item : lexModifyRecord) {
224         JSHandle<JSTaggedValue> envHandle;
225         uint16_t slot;
226         JSHandle<JSTaggedValue> valueHandle;
227         std::tie(envHandle, slot, valueHandle) = item;
228         bool existRecord = false;
229         if (!modifiedLexVar_.empty()) {
230             for (const auto &itemLast : modifiedLexVar_.top()) {
231                 JSHandle<JSTaggedValue> envHandleRecord = std::get<0>(itemLast);
232                 uint16_t slotRecord = std::get<1>(itemLast);
233                 if (envHandleRecord.GetTaggedType() == envHandle.GetTaggedType() && slotRecord == slot) {
234                     existRecord = true;
235                     break;
236                 }
237             }
238         }
239         if (modifiedLexVar_.empty() || existRecord) {
240             globalHandleCollection.Dispose(envHandle);
241             globalHandleCollection.Dispose(valueHandle);
242         } else {
243             modifiedLexVar_.top().emplace_back(envHandle, slot, valueHandle);
244         }
245     }
246 }
247 
PushPromiseQueueSizeRecord(JSThread * thread)248 void DropframeManager::PushPromiseQueueSizeRecord(JSThread *thread)
249 {
250     EcmaContext *context = thread->GetCurrentEcmaContext();
251     uint32_t queueSize = job::MicroJobQueue::GetPromiseQueueSize(thread, context->GetMicroJobQueue());
252     promiseQueueSizeRecord_.push(queueSize);
253 }
254 
GetPromiseQueueSizeRecordOfTopFrame()255 uint32_t DropframeManager::GetPromiseQueueSizeRecordOfTopFrame()
256 {
257     ASSERT(!promiseQueueSizeRecord_.empty());
258     return promiseQueueSizeRecord_.top();
259 }
260 
PopPromiseQueueSizeRecord()261 void DropframeManager::PopPromiseQueueSizeRecord()
262 {
263     if (!promiseQueueSizeRecord_.empty()) {
264         promiseQueueSizeRecord_.pop();
265     }
266 }
267 }