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