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