• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-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 "plugins/ets/runtime/types/ets_method.h"
17 
18 #include "libpandabase/macros.h"
19 #include "libpandabase/utils/utf.h"
20 #include "plugins/ets/runtime/ani/ani_checkers.h"
21 #include "plugins/ets/runtime/ani/scoped_objects_fix.h"
22 #include "plugins/ets/runtime/ets_panda_file_items.h"
23 #include "plugins/ets/runtime/napi/ets_scoped_objects_fix.h"
24 #include "plugins/ets/runtime/types/ets_primitives.h"
25 #include "plugins/ets/runtime/types/ets_type.h"
26 
27 namespace ark::ets {
28 
29 class EtsObject;
30 
IsMethod(const PandaString & td)31 bool EtsMethod::IsMethod(const PandaString &td)
32 {
33     return td[0] == METHOD_PREFIX;
34 }
35 
FindInvokeMethodInFunctionalType(EtsClass * type)36 static EtsMethod *FindInvokeMethodInFunctionalType(EtsClass *type)
37 {
38     ASSERT(type->IsFunction());
39     for (size_t arity = 0; arity <= STD_CORE_FUNCTION_MAX_ARITY; ++arity) {
40         PandaStringStream ss;
41         ss << STD_CORE_FUNCTION_INVOKE_PREFIX << arity;
42         PandaString str = ss.str();
43         EtsMethod *method = type->GetInstanceMethod(str.c_str(), nullptr);
44         if (method != nullptr) {
45             return method;
46         }
47     }
48     UNREACHABLE();
49 }
50 
FromTypeDescriptor(const PandaString & td,EtsRuntimeLinker * contextLinker)51 EtsMethod *EtsMethod::FromTypeDescriptor(const PandaString &td, EtsRuntimeLinker *contextLinker)
52 {
53     ASSERT(contextLinker != nullptr);
54     auto *ctx = contextLinker->GetClassLinkerContext();
55 
56     EtsClassLinker *classLinker = PandaEtsVM::GetCurrent()->GetClassLinker();
57     if (td[0] == METHOD_PREFIX) {
58         // here we resolve method in existing class, which is stored as pointer to panda file + entity id
59         uint64_t filePtr;
60         uint64_t id;
61         const auto scanfStr = std::string_view {td}.substr(1).data();
62         // NOLINTBEGIN(cppcoreguidelines-pro-type-vararg,cert-err34-c)
63         [[maybe_unused]] auto res = sscanf_s(scanfStr, "%" PRIu64 ";%" PRIu64 ";", &filePtr, &id);
64         // NOLINTEND(cppcoreguidelines-pro-type-vararg,cert-err34-c)
65         [[maybe_unused]] static constexpr int SCANF_PARAM_CNT = 2;
66         ASSERT(res == SCANF_PARAM_CNT);
67         auto pandaFile = reinterpret_cast<const panda_file::File *>(filePtr);
68         auto *method = classLinker->GetMethod(*pandaFile, panda_file::File::EntityId(id), ctx);
69         return EtsMethod::FromRuntimeMethod(method);
70     }
71     ASSERT(td[0] == CLASS_TYPE_PREFIX);
72     auto type = classLinker->GetClass(td.c_str(), true, ctx);
73     ASSERT(type != nullptr);
74     EtsMethod *method = type->GetInstanceMethod(ark::ets::INVOKE_METHOD_NAME, nullptr);
75     method = method == nullptr ? type->GetStaticMethod(ark::ets::INVOKE_METHOD_NAME, nullptr) : method;
76     if (method != nullptr) {
77         return method;
78     }
79     return FindInvokeMethodInFunctionalType(type);
80 }
81 
Invoke(napi::ScopedManagedCodeFix * s,Value * args)82 EtsValue EtsMethod::Invoke(napi::ScopedManagedCodeFix *s, Value *args)
83 {
84     Value res = GetPandaMethod()->Invoke(s->GetEtsCoroutine(), args);
85     if (GetReturnValueType() == EtsType::VOID) {
86         // Return any value, will be ignored
87         return EtsValue(0);
88     }
89     if (GetReturnValueType() == EtsType::OBJECT) {
90         auto *obj = reinterpret_cast<EtsObject *>(res.GetAs<ObjectHeader *>());
91         if (obj == nullptr) {
92             return EtsValue(nullptr);
93         }
94         return EtsValue(napi::ScopedManagedCodeFix::AddLocalRef(s->EtsNapiEnv(), obj));
95     }
96 
97     return EtsValue(res.GetAs<EtsLong>());
98 }
99 
Invoke(ani::ScopedManagedCodeFix & s,Value * args,EtsValue * result)100 ani_status EtsMethod::Invoke(ani::ScopedManagedCodeFix &s, Value *args, EtsValue *result)
101 {
102     Value res = GetPandaMethod()->Invoke(s.GetCoroutine(), args);
103     ANI_CHECK_RETURN_IF_EQ(s.HasPendingException(), true, ANI_PENDING_ERROR);
104     if (GetReturnValueType() == EtsType::VOID) {
105         // Return any value, will be ignored
106         *result = EtsValue(0);
107         return ANI_OK;
108     }
109     if (GetReturnValueType() == EtsType::OBJECT) {
110         auto *obj = reinterpret_cast<EtsObject *>(res.GetAs<ObjectHeader *>());
111         return s.AddLocalRef(obj, reinterpret_cast<ani_ref *>(result));
112     }
113     *result = EtsValue(res.GetAs<EtsLong>());
114     return ANI_OK;
115 }
116 
GetNumArgSlots() const117 uint32_t EtsMethod::GetNumArgSlots() const
118 {
119     uint32_t numOfSlots = 0;
120     auto proto = GetPandaMethod()->GetProto();
121     auto &shorty = proto.GetShorty();
122     auto shortyEnd = shorty.end();
123     // must skip the return type
124     auto shortyIt = shorty.begin() + 1;
125     for (; shortyIt != shortyEnd; ++shortyIt) {
126         auto argTypeId = shortyIt->GetId();
127         // double and long arguments take two slots
128         if (argTypeId == panda_file::Type::TypeId::I64 || argTypeId == panda_file::Type::TypeId::F64) {
129             numOfSlots += 2U;
130         } else {
131             numOfSlots += 1U;
132         }
133     }
134     if (!IsStatic()) {
135         ++numOfSlots;
136     }
137     return numOfSlots;
138 }
139 
TryGetMinArgCountFromAnnotation(const panda_file::AnnotationDataAccessor & accessor,const panda_file::File & pandaFile)140 inline std::optional<uint32_t> TryGetMinArgCountFromAnnotation(const panda_file::AnnotationDataAccessor &accessor,
141                                                                const panda_file::File &pandaFile)
142 {
143     for (uint32_t i = 0; i < accessor.GetCount(); i++) {
144         panda_file::AnnotationDataAccessor::Elem accessorElem = accessor.GetElement(i);
145         if (utf::IsEqual(pandaFile.GetStringData(accessorElem.GetNameId()).data, utf::CStringAsMutf8("minArgCount"))) {
146             // annotation value is int in ets code which must be positive, so it is be casted to uint32_t safely
147             auto minArgCount = accessorElem.GetScalarValue().Get<int32_t>();
148             ASSERT(minArgCount >= 0);
149             return static_cast<uint32_t>(minArgCount);
150         }
151     }
152     return std::nullopt;
153 }
154 
TryGetMinArgCount()155 std::optional<uint32_t> EtsMethod::TryGetMinArgCount()
156 {
157     std::optional<uint32_t> resOpt = std::nullopt;
158 
159     panda_file::MethodDataAccessor mda(*(GetPandaMethod()->GetPandaFile()), GetPandaMethod()->GetFileId());
160     mda.EnumerateAnnotations([&](panda_file::File::EntityId annotationId) {
161         panda_file::AnnotationDataAccessor accessor(mda.GetPandaFile(), annotationId);
162         auto annotationName = mda.GetPandaFile().GetStringData(accessor.GetClassId()).data;
163         auto expectedAnnotationName =
164             utf::CStringAsMutf8(panda_file_items::class_descriptors::OPTIONAL_PARAMETERS_ANNOTATION.data());
165         // check if annotation is optional parameters
166         if (utf::IsEqual(annotationName, expectedAnnotationName)) {
167             std::optional<uint32_t> minArgCountOpt = TryGetMinArgCountFromAnnotation(accessor, mda.GetPandaFile());
168 
169             if (minArgCountOpt.has_value()) {
170                 resOpt = minArgCountOpt;
171             }
172         }
173     });
174     return resOpt;
175 }
176 
GetNumMandatoryArgs()177 uint32_t EtsMethod::GetNumMandatoryArgs()
178 {
179     // NOTE(MockMockBlack, #IC787J): support default parameters and optional parameters for lambda functon
180     size_t numMandatoryArgs = 0;
181 
182     bool hasRestParam = ((this->GetAccessFlags() & ACC_VARARGS) != 0);
183     if (hasRestParam) {
184         // rest param is not mandatory
185         numMandatoryArgs = this->GetParametersNum() - 1;
186     } else {
187         numMandatoryArgs = this->GetParametersNum();
188     }
189 
190     auto minArgCountOpt = this->TryGetMinArgCount();
191     if (minArgCountOpt.has_value()) {
192         numMandatoryArgs = minArgCountOpt.value();
193     }
194 
195     return numMandatoryArgs;
196 }
197 
ResolveArgType(uint32_t idx)198 EtsClass *EtsMethod::ResolveArgType(uint32_t idx)
199 {
200     if (!IsStatic()) {
201         if (idx == 0) {
202             return GetClass();
203         }
204     }
205 
206     // get reference type
207     EtsClassLinker *classLinker = PandaEtsVM::GetCurrent()->GetClassLinker();
208     auto type = GetPandaMethod()->GetArgType(idx);
209     if (!type.IsPrimitive()) {
210         size_t refIdx = 0;
211         size_t shortEnd = IsStatic() ? (idx + 1) : idx;  // first is return type
212         auto proto = GetPandaMethod()->GetProto();
213         for (size_t shortIdx = 0; shortIdx < shortEnd; shortIdx++) {
214             if (proto.GetShorty()[shortIdx].IsReference()) {
215                 refIdx++;
216             }
217         }
218         ASSERT(refIdx <= proto.GetRefTypes().size());
219         return classLinker->GetClass(proto.GetRefTypes()[refIdx].data(), false, GetClass()->GetLoadContext());
220     }
221 
222     // get primitive type
223     switch (type.GetId()) {
224         case panda_file::Type::TypeId::U1:
225             return classLinker->GetClassRoot(EtsClassRoot::BOOLEAN);
226         case panda_file::Type::TypeId::I8:
227             return classLinker->GetClassRoot(EtsClassRoot::BYTE);
228         case panda_file::Type::TypeId::I16:
229             return classLinker->GetClassRoot(EtsClassRoot::SHORT);
230         case panda_file::Type::TypeId::U16:
231             return classLinker->GetClassRoot(EtsClassRoot::CHAR);
232         case panda_file::Type::TypeId::I32:
233             return classLinker->GetClassRoot(EtsClassRoot::INT);
234         case panda_file::Type::TypeId::I64:
235             return classLinker->GetClassRoot(EtsClassRoot::LONG);
236         case panda_file::Type::TypeId::F32:
237             return classLinker->GetClassRoot(EtsClassRoot::FLOAT);
238         case panda_file::Type::TypeId::F64:
239             return classLinker->GetClassRoot(EtsClassRoot::DOUBLE);
240         default:
241             LOG(FATAL, RUNTIME) << "ResolveArgType: not a valid ets type for " << type;
242             return nullptr;
243     };
244 }
245 
GetMethodSignature(bool includeReturnType) const246 PandaString EtsMethod::GetMethodSignature(bool includeReturnType) const
247 {
248     PandaOStringStream signature;
249     auto proto = GetPandaMethod()->GetProto();
250     auto &shorty = proto.GetShorty();
251     auto &refTypes = proto.GetRefTypes();
252 
253     auto refIt = refTypes.begin();
254     panda_file::Type returnType = shorty[0];
255     if (!returnType.IsPrimitive()) {
256         ++refIt;
257     }
258 
259     auto shortyEnd = shorty.end();
260     auto shortyIt = shorty.begin() + 1;
261     for (; shortyIt != shortyEnd; ++shortyIt) {
262         if ((*shortyIt).IsPrimitive()) {
263             signature << panda_file::Type::GetSignatureByTypeId(*shortyIt);
264         } else {
265             signature << *refIt;
266             ++refIt;
267         }
268     }
269 
270     if (includeReturnType) {
271         signature << ":";
272         if (returnType.IsPrimitive()) {
273             signature << panda_file::Type::GetSignatureByTypeId(returnType);
274         } else {
275             signature << refTypes[0];
276         }
277     }
278     return signature.str();
279 }
280 
GetDescriptor() const281 PandaString EtsMethod::GetDescriptor() const
282 {
283     constexpr size_t TD_MAX_SIZE = 256;
284     std::array<char, TD_MAX_SIZE> actualTd;  // NOLINT(cppcoreguidelines-pro-type-member-init)
285     // initialize in printf
286     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
287     snprintf_s(actualTd.data(), actualTd.size(), actualTd.size() - 1, "%c%" PRIu64 ";%" PRIu64 ";", METHOD_PREFIX,
288                reinterpret_cast<uint64_t>(GetPandaMethod()->GetPandaFile()),
289                static_cast<uint64_t>(GetPandaMethod()->GetFileId().GetOffset()));
290     return {actualTd.data()};
291 }
292 
293 }  // namespace ark::ets
294