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 21namespace panda::ecmascript::tooling { 22bool 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 36bool 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 49std::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 72void 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 99void 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 133void 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 150void 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 191void DropframeManager::NewLexModifyRecordLevel() 192{ 193 modifiedLexVar_.push(std::vector<std::tuple<JSHandle<JSTaggedValue>, uint16_t, JSHandle<JSTaggedValue>>>()); 194} 195 196void 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 211uint32_t DropframeManager::GetLexModifyRecordLevel() 212{ 213 return modifiedLexVar_.size(); 214} 215 216std::vector<std::tuple<JSHandle<JSTaggedValue>, uint16_t, JSHandle<JSTaggedValue>>> 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 225void 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 240void 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 279void 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 286uint32_t DropframeManager::GetPromiseQueueSizeRecordOfTopFrame() 287{ 288 ASSERT(!promiseQueueSizeRecord_.empty()); 289 return promiseQueueSizeRecord_.top(); 290} 291 292void DropframeManager::PopPromiseQueueSizeRecord() 293{ 294 if (!promiseQueueSizeRecord_.empty()) { 295 promiseQueueSizeRecord_.pop(); 296 } 297} 298 299void DropframeManager::PushMethodInfo(std::tuple<JSPandaFile*, 300 panda_file::File::EntityId> methodInfo) 301{ 302 methodInfo_.push(methodInfo); 303} 304 305bool 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 317void DropframeManager::PopMethodInfo() 318{ 319 if (!methodInfo_.empty()) { 320 methodInfo_.pop(); 321 } 322} 323 324void DropframeManager::PushMethodType(MethodType methodType) 325{ 326 methodType_.push(methodType); 327} 328 329bool DropframeManager::CheckIsSendableMethod() 330{ 331 ASSERT(!methodType_.empty()); 332 return methodType_.top() == MethodType::SENDABLE_METHOD; 333} 334 335void DropframeManager::PopMethodType() 336{ 337 if (!methodType_.empty()) { 338 methodType_.pop(); 339 } 340} 341}