1 /**
2 * Copyright (c) 2023-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 #include "plugins/ets/runtime/interop_js/js_proxy/js_proxy.h"
17 #include "plugins/ets/runtime/interop_js/interop_context.h"
18
19 #include "plugins/ets/runtime/types/ets_object.h"
20
21 namespace ark::ets::interop::js::js_proxy {
22
23 extern "C" void CallJSProxyBridge(Method *method, ...);
24
25 // Create JSProxy class descriptor that will respond to IsProxyClass
26 // NOLINTNEXTLINE(modernize-avoid-c-arrays)
MakeProxyDescriptor(const uint8_t * descriptorP)27 static std::unique_ptr<uint8_t[]> MakeProxyDescriptor(const uint8_t *descriptorP)
28 {
29 Span<const uint8_t> descriptor(descriptorP, utf::Mutf8Size(descriptorP));
30
31 ASSERT(descriptor.size() > 2U);
32 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
33 ASSERT(descriptor[0] == 'L');
34 ASSERT(descriptor[descriptor.size() - 1] == ';');
35
36 size_t proxyDescriptorSize = descriptor.size() + 3U; // + $$\0
37 // NOLINTNEXTLINE(modernize-avoid-c-arrays)
38 auto proxyDescriptorData = std::make_unique<uint8_t[]>(proxyDescriptorSize);
39 Span<uint8_t> proxyDescriptor(proxyDescriptorData.get(), proxyDescriptorSize);
40
41 proxyDescriptor[0] = 'L';
42 proxyDescriptor[1] = '$';
43 std::copy_n(&descriptor[1], descriptor.size() - 2U, &proxyDescriptor[2U]);
44 proxyDescriptor[proxyDescriptor.size() - 3U] = '$';
45 proxyDescriptor[proxyDescriptor.size() - 2U] = ';';
46 proxyDescriptor[proxyDescriptor.size() - 1U] = '\0';
47
48 return proxyDescriptorData;
49 }
50
InitProxyMethod(Class * cls,Method * src,Method * proxy)51 static void InitProxyMethod(Class *cls, Method *src, Method *proxy)
52 {
53 new (proxy) Method(src);
54
55 proxy->SetAccessFlags((src->GetAccessFlags() & ~(ACC_ABSTRACT | ACC_DEFAULT_INTERFACE_METHOD)) | ACC_FINAL);
56 proxy->SetClass(cls);
57 proxy->SetCompiledEntryPoint(reinterpret_cast<void *>(CallJSProxyBridge));
58 }
59
60 /*static*/
Create(EtsClass * etsClass,Span<Method * > targetMethods)61 std::unique_ptr<JSProxy> JSProxy::Create(EtsClass *etsClass, Span<Method *> targetMethods)
62 {
63 Class *cls = etsClass->GetRuntimeClass();
64 ASSERT(!IsProxyClass(cls) && !etsClass->IsFinal());
65 ClassLinker *classLinker = Runtime::GetCurrent()->GetClassLinker();
66
67 PandaVector<size_t> targetMethodsIdx;
68
69 for (size_t i = 0; i < targetMethods.size(); ++i) {
70 auto m = targetMethods[i];
71 if (!m->IsFinal()) { // NOTE(vpukhov): consider internal methods, final methods in builtins
72 targetMethodsIdx.push_back(i);
73 }
74 }
75
76 size_t const numTargets = targetMethodsIdx.size();
77 Span<Method> proxyMethods {classLinker->GetAllocator()->AllocArray<Method>(numTargets), numTargets};
78
79 for (size_t i = 0; i < numTargets; ++i) {
80 InitProxyMethod(cls, targetMethods[targetMethodsIdx[i]], &proxyMethods[i]);
81 }
82
83 auto descriptor = MakeProxyDescriptor(cls->GetDescriptor());
84 uint32_t accessFlags = cls->GetAccessFlags() | ACC_PROXY | ACC_FINAL;
85 Span<Field> fields {};
86 Class *baseClass = cls;
87 Span<Class *> interfaces {};
88 ClassLinkerContext *context = cls->GetLoadContext();
89
90 Class *proxyCls = classLinker->BuildClass(descriptor.get(), true, accessFlags, proxyMethods, fields, baseClass,
91 interfaces, context, false);
92 proxyCls->SetState(Class::State::INITIALIZING);
93 proxyCls->SetState(Class::State::INITIALIZED);
94
95 ASSERT(IsProxyClass(proxyCls));
96
97 // CC-OFFNXT(G.RES.09) private constructor
98 auto jsProxy = std::unique_ptr<JSProxy>(new JSProxy(EtsClass::FromRuntimeClass(proxyCls)));
99 return jsProxy;
100 }
101
102 } // namespace ark::ets::interop::js::js_proxy
103