1 /** 2 * Copyright (c) 2021-2024 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 #ifndef PANDA_PLUGINS_ETS_RUNTIME_TS2ETS_ETS_TYPE_VISITOR_H_ 17 #define PANDA_PLUGINS_ETS_RUNTIME_TS2ETS_ETS_TYPE_VISITOR_H_ 18 19 #include "libpandabase/macros.h" 20 #include "libpandafile/proto_data_accessor-inl.h" 21 #include "runtime/include/runtime.h" 22 #include "runtime/mem/heap_manager.h" 23 24 namespace ark::ets::interop::js { 25 26 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 27 #define TYPEVIS_PRIM_TYPES_LIST(V) \ 28 V(U1) \ 29 V(I8) \ 30 V(U16) \ 31 V(I16) \ 32 V(I32) \ 33 V(I64) \ 34 V(F32) \ 35 V(F64) 36 37 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 38 #define TYPEVIS_RAISE_ERROR(val) \ 39 do { \ 40 Error() = (val); \ 41 return; \ 42 } while (0) 43 44 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 45 #define TYPEVIS_ABRUPT_ON_ERROR() \ 46 do { \ 47 if (UNLIKELY(!!Error())) \ 48 return; \ 49 } while (0) 50 51 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 52 #define TYPEVIS_CHECK_ERROR(expr, val) \ 53 do { \ 54 if (UNLIKELY(!(expr))) \ 55 TYPEVIS_RAISE_ERROR(val); \ 56 } while (0) 57 58 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 59 #define TYPEVIS_CHECK_FORWARD_ERROR(err) \ 60 do { \ 61 auto &_e = (err); \ 62 TYPEVIS_CHECK_ERROR(!_e, std::move(_e)); \ 63 } while (0) 64 65 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 66 #define TYPEVIS_NAPI_CHECK(expr) TYPEVIS_CHECK_ERROR((expr) == napi_ok, #expr) 67 68 class EtsTypeVisitor { 69 public: 70 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 71 #define DEF_VIS(name) virtual void Visit##name() = 0; TYPEVIS_PRIM_TYPES_LIST(DEF_VIS)72 TYPEVIS_PRIM_TYPES_LIST(DEF_VIS) 73 #undef DEF_VIS 74 75 virtual void VisitPrimitive(const ark::panda_file::Type type) 76 { 77 switch (type.GetId()) { 78 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 79 #define DELEGATE(name) \ 80 case ark::panda_file::Type::TypeId::name: { \ 81 return Visit##name(); \ 82 } 83 TYPEVIS_PRIM_TYPES_LIST(DELEGATE) 84 #undef DELEGATE 85 86 default: 87 TYPEVIS_RAISE_ERROR("bad primitive type"); 88 } 89 } 90 91 virtual void VisitString(ark::Class *klass) = 0; 92 virtual void VisitArray(ark::Class *klass) = 0; 93 94 virtual void VisitFieldPrimitive(ark::Field const *field, ark::panda_file::Type type) = 0; 95 virtual void VisitFieldReference(ark::Field const *field, ark::Class *klass) = 0; 96 VisitField(ark::Field const * field)97 virtual void VisitField(ark::Field const *field) 98 { 99 auto type = field->GetType(); 100 if (type.IsPrimitive()) { 101 return VisitFieldPrimitive(field, type); 102 } 103 return VisitFieldReference(field, field->ResolveTypeClass()); 104 } 105 VisitObject(ark::Class * klass)106 virtual void VisitObject(ark::Class *klass) 107 { 108 auto fields = klass->GetInstanceFields(); 109 for (auto const &f : fields) { 110 VisitField(&f); 111 } 112 InStatic() = true; 113 auto sfields = klass->GetStaticFields(); 114 for (auto const &f : sfields) { 115 VisitField(&f); 116 } 117 InStatic() = false; 118 TYPEVIS_ABRUPT_ON_ERROR(); 119 } 120 VisitReference(ark::Class * klass)121 virtual void VisitReference(ark::Class *klass) 122 { 123 if (klass->IsStringClass()) { 124 return VisitString(klass); 125 } 126 if (klass->IsArrayClass()) { 127 return VisitArray(klass); 128 } 129 return VisitObject(klass); 130 } 131 VisitClass(ark::Class * klass)132 virtual void VisitClass(ark::Class *klass) 133 { 134 if (klass->IsPrimitive()) { 135 return VisitPrimitive(klass->GetType()); 136 } 137 return VisitReference(klass); 138 } 139 Error()140 std::optional<std::string> &Error() 141 { 142 return error_; 143 } 144 InStatic()145 bool &InStatic() 146 { 147 return inStatic_; 148 } 149 150 private: 151 std::optional<std::string> error_; 152 bool inStatic_ = false; 153 }; 154 155 class EtsMethodVisitor { 156 public: EtsMethodVisitor(ark::Method * method)157 explicit EtsMethodVisitor(ark::Method *method) : method_(method) {} 158 VisitMethod()159 virtual void VisitMethod() 160 { 161 refArgIdx_ = 0; 162 auto excludeThis = static_cast<uint32_t>(!method_->IsStatic()); 163 auto numArgs = method_->GetNumArgs() - excludeThis; 164 VisitReturn(); 165 TYPEVIS_ABRUPT_ON_ERROR(); 166 for (uint32_t i = 0; i < numArgs; ++i) { 167 VisitArgument(i); 168 TYPEVIS_ABRUPT_ON_ERROR(); 169 } 170 } 171 VisitArgs()172 virtual void VisitArgs() 173 { 174 refArgIdx_ = 0; 175 auto excludeThis = static_cast<uint32_t>(!method_->IsStatic()); 176 auto numArgs = method_->GetNumArgs() - excludeThis; 177 if (!method_->GetReturnType().IsPrimitive()) { 178 refArgIdx_++; 179 } 180 for (uint32_t i = 0; i < numArgs; ++i) { 181 VisitArgument(i); 182 TYPEVIS_ABRUPT_ON_ERROR(); 183 } 184 } 185 VisitReturn()186 virtual void VisitReturn() 187 { 188 refArgIdx_ = 0; 189 VisitReturnImpl(); 190 } 191 Error()192 std::optional<std::string> &Error() 193 { 194 return error_; 195 } 196 GetMethod()197 auto GetMethod() const 198 { 199 return method_; 200 } 201 202 protected: 203 virtual void VisitReturn(ark::panda_file::Type type) = 0; 204 virtual void VisitReturn(ark::Class *klass) = 0; 205 virtual void VisitArgument(uint32_t idx, ark::panda_file::Type type) = 0; 206 virtual void VisitArgument(uint32_t idx, ark::Class *klass) = 0; 207 208 private: VisitReturnImpl()209 void VisitReturnImpl() 210 { 211 panda_file::Type type = method_->GetReturnType(); 212 if (type.IsPrimitive()) { 213 return VisitReturn(type); 214 } 215 return VisitReturn(ResolveRefClass()); 216 } 217 VisitArgument(uint32_t argIdx)218 void VisitArgument(uint32_t argIdx) 219 { 220 auto excludeThis = static_cast<uint32_t>(!method_->IsStatic()); 221 panda_file::Type type = method_->GetArgType(argIdx + excludeThis); 222 if (type.IsPrimitive()) { 223 return VisitArgument(argIdx, type); 224 } 225 return VisitArgument(argIdx, ResolveRefClass()); 226 } 227 ResolveRefClass()228 ark::Class *ResolveRefClass() 229 { 230 auto pf = method_->GetPandaFile(); 231 panda_file::MethodDataAccessor mda(*pf, method_->GetFileId()); 232 panda_file::ProtoDataAccessor pda(*pf, mda.GetProtoId()); 233 auto classLinker = ark::Runtime::GetCurrent()->GetClassLinker(); 234 auto ctx = method_->GetClass()->GetLoadContext(); 235 236 auto klassId = pda.GetReferenceType(refArgIdx_++); 237 auto klass = classLinker->GetClass(*pf, klassId, ctx); 238 return klass; 239 } 240 241 ark::Method *method_ {}; 242 uint32_t refArgIdx_ {}; 243 std::optional<std::string> error_; 244 }; 245 246 class EtsConvertorRef { 247 public: 248 using ObjRoot = ark::ObjectHeader **; 249 using ValVariant = std::variant<ark::Value, ObjRoot>; 250 251 EtsConvertorRef() = default; EtsConvertorRef(ValVariant * dataPtr)252 explicit EtsConvertorRef(ValVariant *dataPtr) 253 { 254 u_.dataPtr = dataPtr; // NOLINT(cppcoreguidelines-pro-type-union-access) 255 } EtsConvertorRef(ObjRoot obj,size_t offs)256 EtsConvertorRef(ObjRoot obj, size_t offs) : isField_(true) 257 { 258 u_.field.obj = obj; // NOLINT(cppcoreguidelines-pro-type-union-access) 259 u_.field.offs = offs; // NOLINT(cppcoreguidelines-pro-type-union-access) 260 } 261 262 template <typename T> LoadPrimitive()263 T LoadPrimitive() const 264 { 265 if (isField_) { 266 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 267 return (*u_.field.obj)->GetFieldPrimitive<T>(u_.field.offs); 268 } 269 return std::get<ark::Value>(*u_.dataPtr).GetAs<T>(); // NOLINT(cppcoreguidelines-pro-type-union-access) 270 } 271 LoadReference()272 ark::ObjectHeader *LoadReference() const 273 { 274 if (isField_) { 275 return (*u_.field.obj)->GetFieldObject(u_.field.offs); // NOLINT(cppcoreguidelines-pro-type-union-access) 276 } 277 return *std::get<ObjRoot>(*u_.dataPtr); // NOLINT(cppcoreguidelines-pro-type-union-access) 278 } 279 280 template <typename T> StorePrimitive(T val)281 void StorePrimitive(T val) 282 { 283 if (isField_) { 284 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 285 (*u_.field.obj)->SetFieldPrimitive<T>(u_.field.offs, val); 286 } else { 287 *u_.dataPtr = ark::Value(val); // NOLINT(cppcoreguidelines-pro-type-union-access) 288 } 289 } 290 StoreReference(ObjRoot val)291 void StoreReference(ObjRoot val) 292 { 293 if (isField_) { 294 (*u_.field.obj)->SetFieldObject(u_.field.offs, *val); // NOLINT(cppcoreguidelines-pro-type-union-access) 295 } else { 296 *u_.dataPtr = val; // NOLINT(cppcoreguidelines-pro-type-union-access) 297 } 298 } 299 300 private: 301 union USlot { 302 struct FieldSlot { // field slot 303 ObjRoot obj = nullptr; 304 size_t offs = 0; 305 }; 306 307 FieldSlot field; 308 ValVariant *dataPtr = nullptr; // handle or primitive slot 309 USlot()310 USlot() {} // NOLINT(modernize-use-equals-default) 311 }; 312 313 USlot u_ {}; 314 bool isField_ = false; 315 }; 316 317 } // namespace ark::ets::interop::js 318 319 #endif // !PANDA_PLUGINS_ETS_RUNTIME_TS2ETS_ETS_TYPE_VISITOR_H_ 320