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