• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2023-2025 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 "include/class.h"
17 #include "include/mem/panda_containers.h"
18 #include "include/method.h"
19 #include "plugins/ets/runtime/interop_js/js_proxy/js_proxy.h"
20 #include "plugins/ets/runtime/interop_js/interop_context.h"
21 
22 #include "plugins/ets/runtime/types/ets_object.h"
23 
24 namespace ark::ets::interop::js::js_proxy {
25 
26 extern "C" void CallJSProxyBridge(Method *method, ...);
27 extern "C" void CallJSFunctionBridge(Method *method, ...);
28 
29 using MethodMap = std::unordered_map<uint8_t const *, Method *, utf::Mutf8Hash, utf::Mutf8Equal>;
30 
31 // Create JSProxy class descriptor that will respond to IsProxyClass
32 // NOLINTNEXTLINE(modernize-avoid-c-arrays)
MakeProxyDescriptor(const uint8_t * descriptorP)33 static std::unique_ptr<uint8_t[]> MakeProxyDescriptor(const uint8_t *descriptorP)
34 {
35     Span<const uint8_t> descriptor(descriptorP, utf::Mutf8Size(descriptorP));
36 
37     ASSERT(descriptor.size() > 2U);
38     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
39     ASSERT(descriptor[0] == 'L');
40     ASSERT(descriptor[descriptor.size() - 1] == ';');
41 
42     size_t proxyDescriptorSize = descriptor.size() + 3U;  // + $$\0
43     // NOLINTNEXTLINE(modernize-avoid-c-arrays)
44     auto proxyDescriptorData = std::make_unique<uint8_t[]>(proxyDescriptorSize);
45     Span<uint8_t> proxyDescriptor(proxyDescriptorData.get(), proxyDescriptorSize);
46 
47     proxyDescriptor[0] = 'L';
48     proxyDescriptor[1] = '$';
49     std::copy_n(&descriptor[1], descriptor.size() - 2U, &proxyDescriptor[2U]);
50     proxyDescriptor[proxyDescriptor.size() - 3U] = '$';
51     proxyDescriptor[proxyDescriptor.size() - 2U] = ';';
52     proxyDescriptor[proxyDescriptor.size() - 1U] = '\0';
53 
54     return proxyDescriptorData;
55 }
56 
GetAllInterfaceMethod(Class * interfaceCls,PandaVector<Method * > & methodPtrs,PandaSet<Class * > & methodPSet)57 static void GetAllInterfaceMethod(Class *interfaceCls, PandaVector<Method *> &methodPtrs, PandaSet<Class *> &methodPSet)
58 {
59     if (methodPSet.count(interfaceCls) != 0) {
60         return;
61     }
62     methodPSet.insert(interfaceCls);
63     auto methods = interfaceCls->GetMethods();
64     for (auto &method : methods) {
65         methodPtrs.push_back(&method);
66     }
67     for (auto *itf : interfaceCls->GetInterfaces()) {
68         GetAllInterfaceMethod(itf, methodPtrs, methodPSet);
69     }
70 }
71 
GetInterfaceMethodDistinct(Class * interfaceCls,MethodMap & methodMap,PandaSet<Class * > & interfaceSet)72 static void GetInterfaceMethodDistinct(Class *interfaceCls, MethodMap &methodMap, PandaSet<Class *> &interfaceSet)
73 {
74     if (interfaceSet.count(interfaceCls) != 0) {
75         return;
76     }
77     interfaceSet.insert(interfaceCls);
78     auto methods = interfaceCls->GetMethods();
79     for (auto &method : methods) {
80         methodMap.insert({method.GetName().data, &method});
81     }
82     for (auto *itf : interfaceCls->GetInterfaces()) {
83         GetInterfaceMethodDistinct(itf, methodMap, interfaceSet);
84     }
85 }
86 
InitProxyMethod(Class * cls,Method * src,Method * proxy,void * entryPoint)87 static void InitProxyMethod(Class *cls, Method *src, Method *proxy, void *entryPoint)
88 {
89     new (proxy) Method(src);
90 
91     proxy->SetAccessFlags((src->GetAccessFlags() & ~(ACC_ABSTRACT | ACC_DEFAULT_INTERFACE_METHOD)) | ACC_FINAL);
92     proxy->SetClass(cls);
93     proxy->SetCompiledEntryPoint(entryPoint);
94 }
95 
96 /*
97  * BuildProxyMethods
98  */
BuildProxyMethods(Class * cls,Span<Method * > targetMethods,void * entryPoint)99 static Span<Method> BuildProxyMethods(Class *cls, Span<Method *> targetMethods, void *entryPoint)
100 {
101     ClassLinker *classLinker = Runtime::GetCurrent()->GetClassLinker();
102 
103     PandaVector<size_t> targetMethodsIdx;
104 
105     for (size_t i = 0; i < targetMethods.size(); ++i) {
106         auto m = targetMethods[i];
107         if (!m->IsFinal()) {  // NOTE(vpukhov): consider internal methods, final methods in builtins
108             targetMethodsIdx.push_back(i);
109         }
110     }
111 
112     size_t const numTargets = targetMethodsIdx.size();
113     Span<Method> proxyMethods {classLinker->GetAllocator()->AllocArray<Method>(numTargets), numTargets};
114 
115     for (size_t i = 0; i < numTargets; ++i) {
116         InitProxyMethod(cls, targetMethods[targetMethodsIdx[i]], &proxyMethods[i],
117                         reinterpret_cast<void *>(entryPoint));
118     }
119 
120     return proxyMethods;
121 }
122 
123 /*static*/
CreateInterfaceProxy(const PandaSet<Class * > & interfaces,std::string & interfaceName)124 JSProxy *JSProxy::CreateInterfaceProxy(const PandaSet<Class *> &interfaces, std::string &interfaceName)
125 {
126     auto coro = EtsCoroutine::GetCurrent();
127     auto ctx = InteropCtx::Current(coro);
128     auto descriptor = MakeProxyDescriptor(utf::CStringAsMutf8(interfaceName.data()));
129     Class *objectClass = ctx->GetObjectClass();
130     ClassLinker *classLinker = Runtime::GetCurrent()->GetClassLinker();
131     ClassLinkerContext *context = objectClass->GetLoadContext();
132 
133     Class *proxyCls = classLinker->GetClass(descriptor.get(), true, context);
134     if (proxyCls == nullptr) {
135         PandaSet<Class *> interfaceSet;
136         MethodMap methodMap;
137         for (auto cls : interfaces) {
138             GetInterfaceMethodDistinct(cls, methodMap, interfaceSet);
139         }
140 
141         PandaVector<Method *> methodPtrs;
142         for (auto &[n, p] : methodMap) {
143             methodPtrs.push_back(p);
144         }
145         PandaVector<Class *> interfacesList = {interfaces.begin(), interfaces.end()};
146         return CreateProxy(descriptor.get(), objectClass, {methodPtrs.data(), methodPtrs.size()}, interfacesList,
147                            reinterpret_cast<void *>(CallJSProxyBridge));
148     }
149 
150     ASSERT(IsProxyClass(proxyCls));
151 
152     auto jsProxy = Runtime::GetCurrent()->GetInternalAllocator()->New<JSProxy>(EtsClass::FromRuntimeClass(proxyCls));
153     return jsProxy;
154 }
155 
156 /*static*/
CreateBuiltinProxy(EtsClass * etsClass,Span<Method * > targetMethods)157 JSProxy *JSProxy::CreateBuiltinProxy(EtsClass *etsClass, Span<Method *> targetMethods)
158 {
159     Class *cls = etsClass->GetRuntimeClass();
160     ASSERT(!IsProxyClass(cls) && !etsClass->IsFinal());
161     auto descriptor = MakeProxyDescriptor(cls->GetDescriptor());
162     return CreateProxy(descriptor.get(), cls, targetMethods, {}, reinterpret_cast<void *>(CallJSProxyBridge));
163 }
164 
165 /*static*/
CreateFunctionProxy(EtsClass * functionInterface)166 JSProxy *JSProxy::CreateFunctionProxy(EtsClass *functionInterface)
167 {
168     auto coro = EtsCoroutine::GetCurrent();
169     auto ctx = InteropCtx::Current(coro);
170 
171     Class *interfaceCls = functionInterface->GetRuntimeClass();
172     ASSERT(interfaceCls->IsInterface());
173 
174     // create JSFunctoinProxy class descriptor that will respond to IsProxyClass
175     auto descriptor = MakeProxyDescriptor(interfaceCls->GetDescriptor());
176 
177     // use `Object` as the base class for the proxy function
178     Class *objectClass = ctx->GetObjectClass();
179 
180     // get the proxy function class if it is already created
181     // otherwise, create the class
182     ClassLinker *classLinker = Runtime::GetCurrent()->GetClassLinker();
183     ClassLinkerContext *context = objectClass->GetLoadContext();
184 
185     Class *proxyFunctionCls = classLinker->GetClass(descriptor.get(), true, context);
186 
187     if (proxyFunctionCls == nullptr) {
188         PandaSet<Class *> methodPSet;
189         PandaVector<Method *> methodPtrs;
190         GetAllInterfaceMethod(interfaceCls, methodPtrs, methodPSet);
191 
192         return CreateProxy(descriptor.get(), objectClass, {methodPtrs.data(), methodPtrs.size()}, {interfaceCls},
193                            reinterpret_cast<void *>(CallJSFunctionBridge));
194     }
195 
196     ASSERT(IsProxyClass(proxyFunctionCls));
197 
198     auto functionProxy =
199         Runtime::GetCurrent()->GetInternalAllocator()->New<JSProxy>(EtsClass::FromRuntimeClass(proxyFunctionCls));
200     return functionProxy;
201 }
202 
203 /*static*/
CreateProxy(const uint8_t * descriptor,Class * baseClass,Span<Method * > targetMethods,const PandaVector<Class * > & interfaces,void * callBridge)204 JSProxy *JSProxy::CreateProxy(const uint8_t *descriptor, Class *baseClass, Span<Method *> targetMethods,
205                               const PandaVector<Class *> &interfaces, void *callBridge)
206 {
207     ClassLinker *classLinker = Runtime::GetCurrent()->GetClassLinker();
208     ClassLinkerContext *context = baseClass->GetLoadContext();
209     auto proxyMethods = BuildProxyMethods(baseClass, targetMethods, callBridge);
210 
211     uint32_t accessFlags = baseClass->GetAccessFlags() | ACC_PROXY | ACC_FINAL;
212     Span<Field> fields {};
213     Span<Class *> interfacesSpan {classLinker->GetAllocator()->AllocArray<Class *>(interfaces.size()),
214                                   interfaces.size()};
215     for (size_t i = 0; i < interfaces.size(); i++) {
216         interfacesSpan[i] = interfaces[i];
217     }
218 
219     Class *proxyCls = classLinker->BuildClass(descriptor, true, accessFlags, proxyMethods, fields, baseClass,
220                                               interfacesSpan, context, false);
221     ASSERT(proxyCls != nullptr);
222     proxyCls->SetState(Class::State::INITIALIZING);
223     proxyCls->SetState(Class::State::INITIALIZED);
224 
225     ASSERT(IsProxyClass(proxyCls));
226 
227     auto jsProxy = Runtime::GetCurrent()->GetInternalAllocator()->New<JSProxy>(EtsClass::FromRuntimeClass(proxyCls));
228     return jsProxy;
229 }
230 
231 }  // namespace ark::ets::interop::js::js_proxy
232