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 }