1 /** 2 * Copyright (c) 2023-2025 Huawei Device Co., Ltd. 3 * Copyright (c) 2023-2025 Huawei Device Co., Ltd. 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_CLASS_WRAPPER_H_ 18 #define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_CLASS_WRAPPER_H_ 19 20 #include "libpandabase/macros.h" 21 #include "plugins/ets/runtime/ets_utils.h" 22 #include "plugins/ets/runtime/interop_js/ets_proxy/ets_field_wrapper.h" 23 #include "plugins/ets/runtime/interop_js/ets_proxy/ets_method_wrapper.h" 24 #include "plugins/ets/runtime/interop_js/ets_proxy/ets_object_reference.h" 25 #include "plugins/ets/runtime/interop_js/js_proxy/js_proxy.h" 26 #include "plugins/ets/runtime/interop_js/js_refconvert.h" 27 #include "plugins/ets/runtime/interop_js/interop_common.h" 28 29 #include <node_api.h> 30 #include <vector> 31 32 namespace ark::ets { 33 class EtsClass; 34 } // namespace ark::ets 35 36 namespace ark::ets::interop::js::ets_proxy { 37 38 using EtsClassWrappersCache = WrappersCache<EtsClass *, EtsClassWrapper>; 39 40 class EtsClassWrapper { 41 public: 42 // clang-format off 43 static constexpr auto FIELD_ATTR = static_cast<napi_property_attributes>(napi_writable | napi_enumerable); 44 static constexpr auto METHOD_ATTR = napi_default; 45 // NOLINTBEGIN(hicpp-signed-bitwise) 46 static constexpr auto STATIC_FIELD_ATTR = static_cast<napi_property_attributes>(napi_writable | napi_enumerable |\ 47 napi_static); 48 // NOLINTEND(hicpp-signed-bitwise) 49 static constexpr auto STATIC_METHOD_ATTR = napi_static; 50 // clang-format on 51 52 using OverloadsMap = 53 std::unordered_multimap<uint8_t const *, std::pair<char const *, uint32_t>, utf::Mutf8Hash, utf::Mutf8Equal>; 54 55 static std::unique_ptr<EtsClassWrapper> Create(InteropCtx *ctx, EtsClass *etsClass, 56 const char *jsBuiltinName = nullptr, 57 const OverloadsMap *overloads = nullptr); 58 59 static EtsClassWrapper *Get(InteropCtx *ctx, EtsClass *etsClass); 60 61 static std::unique_ptr<JSRefConvert> CreateJSRefConvertEtsProxy(InteropCtx *ctx, Class *klass); 62 static std::unique_ptr<JSRefConvert> CreateJSRefConvertJSProxy(InteropCtx *ctx, Class *klass); 63 static std::unique_ptr<JSRefConvert> CreateJSRefConvertEtsInterface(InteropCtx *ctx, Class *klass); 64 IsEtsGlobalClass()65 bool IsEtsGlobalClass() const 66 { 67 return IsEtsGlobalClassName(etsClass_->GetDescriptor()); 68 } 69 GetEtsClass()70 EtsClass *GetEtsClass() 71 { 72 return etsClass_; 73 } 74 75 EtsMethodSet *GetMethod(const std::string &name) const; 76 GetJsCtor(napi_env env)77 napi_value GetJsCtor(napi_env env) const 78 { 79 return GetReferenceValue(env, jsCtorRef_); 80 } 81 HasBuiltin()82 bool HasBuiltin() const 83 { 84 return jsBuiltinCtorRef_ != nullptr; 85 } 86 GetBuiltin(napi_env env)87 napi_value GetBuiltin(napi_env env) const 88 { 89 ASSERT(HasBuiltin()); 90 return GetReferenceValue(env, jsBuiltinCtorRef_); 91 } 92 SetJSBuiltinMatcher(std::function<EtsObject * (InteropCtx *,napi_value,bool)> && matcher)93 void SetJSBuiltinMatcher(std::function<EtsObject *(InteropCtx *, napi_value, bool)> &&matcher) 94 { 95 jsBuiltinMatcher_ = std::move(matcher); 96 } 97 98 napi_value Wrap(InteropCtx *ctx, EtsObject *etsObject); 99 EtsObject *Unwrap(InteropCtx *ctx, napi_value jsValue); 100 101 EtsObject *UnwrapEtsProxy(InteropCtx *ctx, napi_value jsValue); 102 EtsObject *CreateJSBuiltinProxy(InteropCtx *ctx, napi_value jsValue); 103 104 ~EtsClassWrapper() = default; 105 106 private: EtsClassWrapper(EtsClass * etsCls)107 explicit EtsClassWrapper(EtsClass *etsCls) : etsClass_(etsCls) {} 108 NO_COPY_SEMANTIC(EtsClassWrapper); 109 NO_MOVE_SEMANTIC(EtsClassWrapper); 110 111 bool SetupHierarchy(InteropCtx *ctx, const char *jsBuiltinName); 112 113 using PropsMap = 114 std::unordered_map<uint8_t const *, std::variant<EtsMethodSet *, Field *>, utf::Mutf8Hash, utf::Mutf8Equal>; 115 using GetterSetterPropsMap = std::unordered_map<std::string, napi_property_descriptor>; 116 using FieldsVec = std::vector<Field *>; 117 using MethodsVec = std::vector<EtsMethodSet *>; 118 119 std::pair<FieldsVec, MethodsVec> CalculateProperties(const OverloadsMap *overloads); 120 void SetBaseWrapperMethods(napi_env env, const EtsClassWrapper::MethodsVec &methods); 121 void UpdatePropsWithBaseClasses(PropsMap *props); 122 void CollectConstructors(PropsMap *props); 123 void CollectClassMethods(PropsMap *props, const OverloadsMap *overloads); 124 bool HasOverloadsMethod(const OverloadsMap *overloads, Method *m); 125 std::pair<FieldsVec, MethodsVec> CalculateFieldsAndMethods(const PropsMap &props); 126 std::vector<napi_property_descriptor> BuildJSProperties(napi_env &env, Span<Field *> fields, 127 Span<EtsMethodSet *> methods); 128 129 static napi_value GetGlobalSymbolIterator(napi_env &env); 130 131 EtsClassWrapper *LookupBaseWrapper(EtsClass *klass); 132 void BuildGetterSetterFieldProperties(GetterSetterPropsMap &propMap, EtsMethodSet *method); 133 void SetUpMimicHandler(napi_env env); 134 static napi_value CreateProxy(napi_env env, napi_value jsCtor, EtsClassWrapper *thisWrapper); 135 static napi_value MimicGetHandler(napi_env env, napi_callback_info info); 136 static napi_value MimicSetHandler(napi_env env, napi_callback_info info); 137 138 static napi_value JSCtorCallback(napi_env env, napi_callback_info cinfo); 139 bool CreateAndWrap(napi_env env, napi_value jsNewtarget, napi_value jsThis, Span<napi_value> jsArgs); 140 141 static void ThrowJSErrorNotAssignable(napi_env env, EtsClass *fromKlass, EtsClass *toKlass); 142 GetFields()143 Span<EtsFieldWrapper> GetFields() 144 { 145 return Span<EtsFieldWrapper>(etsFieldWrappers_.get(), numFields_); 146 } 147 GetMethods()148 Span<LazyEtsMethodWrapperLink> GetMethods() 149 { 150 return Span<LazyEtsMethodWrapperLink>(etsMethodWrappers_.get(), numMethods_); 151 } 152 153 EtsClass *const etsClass_ {}; 154 EtsClassWrapper *baseWrapper_ {}; 155 156 LazyEtsMethodWrapperLink etsCtorLink_ {}; 157 napi_ref jsCtorRef_ {}; 158 159 // For built-in classes 160 napi_ref jsBuiltinCtorRef_ {}; 161 std::function<EtsObject *(InteropCtx *, napi_value, bool)> jsBuiltinMatcher_; 162 163 js_proxy::JSProxy *jsproxyWrapper_ {}; 164 165 // NOTE(vpukhov): allocate inplace to reduce memory consumption 166 std::unique_ptr<LazyEtsMethodWrapperLink[]> etsMethodWrappers_; // NOLINT(modernize-avoid-c-arrays) 167 std::unique_ptr<EtsFieldWrapper[]> etsFieldWrappers_; // NOLINT(modernize-avoid-c-arrays) 168 std::vector<std::unique_ptr<EtsMethodSet>> etsMethods_; 169 std::vector<std::unique_ptr<EtsFieldWrapper>> getterSetterFieldWrappers_; 170 uint32_t numMethods_ {}; 171 uint32_t numFields_ {}; 172 173 bool needProxy_ = false; 174 napi_ref jsProxyCtorRef_ {}; 175 napi_ref jsProxyHandlerRef_ {}; 176 177 static constexpr const char *INTERFACE_ITERABLE_NAME = "escompat.IterableIterator"; 178 }; 179 180 } // namespace ark::ets::interop::js::ets_proxy 181 182 #endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_ETS_CLASS_WRAPPER_H_ 183