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 "intrinsics.h"
17
18 #include "plugins/ets/runtime/ets_exceptions.h"
19 #include "runtime/include/exceptions.h"
20 #include "compiler/optimizer/ir/constants.h"
21
22 #include "plugins/ets/runtime/ets_class_linker_extension.h"
23 #include "plugins/ets/runtime/ets_stubs-inl.h"
24 #include "plugins/ets/runtime/intrinsics/helpers/ets_to_string_cache.h"
25 namespace ark::ets::intrinsics {
26
27 template <panda_file::Type::TypeId FIELD_TYPE, bool IS_GETTER>
TryGetField(ark::Method * method,Field * rawField,uint32_t pc,ark::Class * klass)28 Field *TryGetField(ark::Method *method, Field *rawField, uint32_t pc, ark::Class *klass)
29 {
30 bool useIc = pc != ark::compiler::INVALID_PC;
31 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
32 auto address = method->GetInstructions() + (useIc ? pc : 0);
33 ASSERT(EtsCoroutine::GetCurrent() != nullptr);
34 InterpreterCache *cache = EtsCoroutine::GetCurrent()->GetInterpreterCache();
35 return GetFieldByName<IS_GETTER>(cache->GetEntry(address), method, rawField, address, klass);
36 }
37
38 template <panda_file::Type::TypeId FIELD_TYPE, bool IS_GETTER>
TryGetCallee(ark::Method * method,Field * rawField,uint32_t pc,ark::Class * klass)39 ark::Method *TryGetCallee(ark::Method *method, Field *rawField, uint32_t pc, ark::Class *klass)
40 {
41 bool useIc = pc != ark::compiler::INVALID_PC;
42 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
43 auto address = method->GetInstructions() + (useIc ? pc : 0);
44 ASSERT(EtsCoroutine::GetCurrent() != nullptr);
45 InterpreterCache *cache = EtsCoroutine::GetCurrent()->GetInterpreterCache();
46 return GetAccessorByName<FIELD_TYPE, IS_GETTER>(cache->GetEntry(address), method, rawField, address, klass);
47 }
48
49 template <panda_file::Type::TypeId FIELD_TYPE, class T>
GetFieldPrimitiveType(Field * field,const VMHandle<ObjectHeader> & handleObj)50 static T GetFieldPrimitiveType(Field *field, const VMHandle<ObjectHeader> &handleObj)
51 {
52 ASSERT(handleObj.GetPtr() != nullptr);
53 switch (field->GetTypeId()) {
54 case panda_file::Type::TypeId::U1:
55 case panda_file::Type::TypeId::U8: {
56 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I32);
57 return handleObj.GetPtr()->template GetFieldPrimitive<uint8_t>(*field);
58 }
59 case panda_file::Type::TypeId::I8: {
60 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I32);
61 return handleObj.GetPtr()->template GetFieldPrimitive<int8_t>(*field);
62 }
63 case panda_file::Type::TypeId::I16: {
64 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I32);
65 return handleObj.GetPtr()->template GetFieldPrimitive<int16_t>(*field);
66 }
67 case panda_file::Type::TypeId::U16: {
68 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I32);
69 return handleObj.GetPtr()->template GetFieldPrimitive<uint16_t>(*field);
70 }
71 case panda_file::Type::TypeId::I32: {
72 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I32);
73 return handleObj.GetPtr()->template GetFieldPrimitive<int32_t>(*field);
74 }
75 case panda_file::Type::TypeId::U32: {
76 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I32);
77 return handleObj.GetPtr()->template GetFieldPrimitive<uint32_t>(*field);
78 }
79 case panda_file::Type::TypeId::I64: {
80 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I64);
81 return handleObj.GetPtr()->template GetFieldPrimitive<int64_t>(*field);
82 }
83 case panda_file::Type::TypeId::U64: {
84 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I64);
85 return handleObj.GetPtr()->template GetFieldPrimitive<uint64_t>(*field);
86 }
87 case panda_file::Type::TypeId::F32: {
88 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::F32);
89 return handleObj.GetPtr()->template GetFieldPrimitive<float>(*field);
90 }
91 case panda_file::Type::TypeId::F64: {
92 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::F64);
93 return handleObj.GetPtr()->template GetFieldPrimitive<double>(*field);
94 }
95 default:
96 UNREACHABLE();
97 return handleObj.GetPtr()->template GetFieldPrimitive<T>(*field);
98 }
99 }
100
101 template <panda_file::Type::TypeId FIELD_TYPE, class T>
CompilerEtsLdObjByName(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj)102 T CompilerEtsLdObjByName(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj)
103 {
104 ASSERT(method != nullptr);
105 ark::Class *klass;
106 ark::Field *rawField;
107 {
108 auto *thread = ManagedThread::GetCurrent();
109 [[maybe_unused]] HandleScope<ObjectHeader *> scope(thread);
110 VMHandle<ObjectHeader> handleObj(thread, obj);
111 auto *classLinker = Runtime::GetCurrent()->GetClassLinker();
112 ASSERT(handleObj.GetPtr() != nullptr);
113 klass = static_cast<ark::Class *>(handleObj.GetPtr()->ClassAddr<ark::BaseClass>());
114 rawField = classLinker->GetField(*method, panda_file::File::EntityId(id), false);
115
116 Field *field = TryGetField<FIELD_TYPE, true>(method, rawField, pc, klass);
117 if (field != nullptr) {
118 if constexpr (FIELD_TYPE == panda_file::Type::TypeId::REFERENCE) {
119 return handleObj.GetPtr()->GetFieldObject(*field);
120 } else {
121 return GetFieldPrimitiveType<FIELD_TYPE, T>(field, handleObj);
122 }
123 }
124
125 auto callee = TryGetCallee<FIELD_TYPE, true>(method, rawField, pc, klass);
126 if (callee != nullptr) {
127 Value val(handleObj.GetPtr());
128 Value result = callee->Invoke(Coroutine::GetCurrent(), &val);
129 return result.GetAs<T>();
130 }
131 }
132 LookUpException<true>(klass, rawField);
133 HandlePendingException();
134 UNREACHABLE();
135 }
136
137 template <panda_file::Type::TypeId FIELD_TYPE, class T>
138 /* CC-OFFNXT(G.FUN.01-CPP, huge_method) big switch-case */
SetTypedFieldPrimitive(Field * field,const VMHandle<ObjectHeader> & handleObj,T storeValue)139 static void SetTypedFieldPrimitive(Field *field, const VMHandle<ObjectHeader> &handleObj, T storeValue)
140 {
141 ASSERT(handleObj.GetPtr() != nullptr);
142 switch (field->GetTypeId()) {
143 case panda_file::Type::TypeId::U1:
144 case panda_file::Type::TypeId::U8: {
145 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I32);
146 handleObj.GetPtr()->SetFieldPrimitive(*field, static_cast<uint8_t>(storeValue));
147 return;
148 }
149 case panda_file::Type::TypeId::I8: {
150 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I32);
151 handleObj.GetPtr()->SetFieldPrimitive(*field, static_cast<int8_t>(storeValue));
152 return;
153 }
154 case panda_file::Type::TypeId::I16: {
155 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I32);
156 handleObj.GetPtr()->SetFieldPrimitive(*field, static_cast<int16_t>(storeValue));
157 return;
158 }
159 case panda_file::Type::TypeId::U16: {
160 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I32);
161 handleObj.GetPtr()->SetFieldPrimitive(*field, static_cast<uint16_t>(storeValue));
162 return;
163 }
164 case panda_file::Type::TypeId::I32: {
165 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I32);
166 handleObj.GetPtr()->SetFieldPrimitive(*field, static_cast<int32_t>(storeValue));
167 return;
168 }
169 case panda_file::Type::TypeId::U32: {
170 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I32);
171 handleObj.GetPtr()->SetFieldPrimitive(*field, static_cast<uint32_t>(storeValue));
172 return;
173 }
174 case panda_file::Type::TypeId::I64: {
175 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I64);
176 handleObj.GetPtr()->SetFieldPrimitive(*field, static_cast<int64_t>(storeValue));
177 return;
178 }
179 case panda_file::Type::TypeId::U64: {
180 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I64);
181 handleObj.GetPtr()->SetFieldPrimitive(*field, static_cast<uint64_t>(storeValue));
182 return;
183 }
184 case panda_file::Type::TypeId::F32: {
185 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::F32);
186 handleObj.GetPtr()->SetFieldPrimitive(*field, storeValue);
187 return;
188 }
189 case panda_file::Type::TypeId::F64: {
190 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::F64);
191 handleObj.GetPtr()->SetFieldPrimitive(*field, storeValue);
192 return;
193 }
194 default: {
195 UNREACHABLE();
196 }
197 }
198 }
199
200 template <panda_file::Type::TypeId FIELD_TYPE, class T>
CompilerEtsStObjByName(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj,T storeValue)201 void CompilerEtsStObjByName(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj, T storeValue)
202 {
203 ASSERT(method != nullptr);
204 ark::Class *klass;
205 ark::Field *rawField;
206 {
207 auto *thread = ManagedThread::GetCurrent();
208 [[maybe_unused]] HandleScope<ObjectHeader *> scope(thread);
209 VMHandle<ObjectHeader> handleObj(thread, obj);
210 auto *classLinker = Runtime::GetCurrent()->GetClassLinker();
211 klass = static_cast<ark::Class *>(obj->ClassAddr<ark::BaseClass>());
212 rawField = classLinker->GetField(*method, panda_file::File::EntityId(id), false);
213
214 Field *field = TryGetField<FIELD_TYPE, false>(method, rawField, pc, klass);
215 if (field != nullptr) {
216 if constexpr (FIELD_TYPE == panda_file::Type::TypeId::REFERENCE) {
217 UNREACHABLE();
218 } else {
219 SetTypedFieldPrimitive<FIELD_TYPE, T>(field, handleObj, storeValue);
220 return;
221 }
222 }
223
224 auto callee = TryGetCallee<FIELD_TYPE, false>(method, rawField, pc, klass);
225 if (callee != nullptr) {
226 PandaVector<Value> args {Value(handleObj.GetPtr()), Value(storeValue)};
227 callee->Invoke(Coroutine::GetCurrent(), args.data());
228 return;
229 }
230 }
231 LookUpException<false>(klass, rawField);
232 HandlePendingException();
233 UNREACHABLE();
234 }
235
CompilerEtsStObjByNameRef(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj,ark::ObjectHeader * storeValue)236 void CompilerEtsStObjByNameRef(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj,
237 ark::ObjectHeader *storeValue)
238 {
239 ASSERT(method != nullptr);
240 ark::Class *klass;
241 ark::Field *rawField;
242 {
243 auto *thread = ManagedThread::GetCurrent();
244 [[maybe_unused]] HandleScope<ObjectHeader *> scope(thread);
245 VMHandle<ObjectHeader> handleObj(thread, obj);
246 VMHandle<ObjectHeader> handleStore(thread, storeValue);
247 auto *classLinker = Runtime::GetCurrent()->GetClassLinker();
248 klass = static_cast<ark::Class *>(obj->ClassAddr<ark::BaseClass>());
249 rawField = classLinker->GetField(*method, panda_file::File::EntityId(id), false);
250
251 Field *field = TryGetField<panda_file::Type::TypeId::REFERENCE, false>(method, rawField, pc, klass);
252 if (field != nullptr) {
253 ASSERT(handleObj.GetPtr() != nullptr);
254 return handleObj.GetPtr()->SetFieldObject(*field, handleStore.GetPtr());
255 }
256
257 auto callee = TryGetCallee<panda_file::Type::TypeId::REFERENCE, false>(method, rawField, pc, klass);
258 if (callee != nullptr) {
259 PandaVector<Value> args {Value(handleObj.GetPtr()), Value(handleStore.GetPtr())};
260 callee->Invoke(Coroutine::GetCurrent(), args.data());
261 return;
262 }
263 }
264 LookUpException<false>(klass, rawField);
265 HandlePendingException();
266 UNREACHABLE();
267 }
268
CompilerEtsLdObjByNameI32(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj)269 extern "C" int32_t CompilerEtsLdObjByNameI32(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj)
270 {
271 return CompilerEtsLdObjByName<panda_file::Type::TypeId::I32, int32_t>(method, id, pc, obj);
272 }
273
CompilerEtsLdObjByNameI64(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj)274 extern "C" int64_t CompilerEtsLdObjByNameI64(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj)
275 {
276 return CompilerEtsLdObjByName<panda_file::Type::TypeId::I64, int64_t>(method, id, pc, obj);
277 }
278
CompilerEtsLdObjByNameF32(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj)279 extern "C" float CompilerEtsLdObjByNameF32(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj)
280 {
281 return CompilerEtsLdObjByName<panda_file::Type::TypeId::F32, float>(method, id, pc, obj);
282 }
283
CompilerEtsLdObjByNameF64(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj)284 extern "C" double CompilerEtsLdObjByNameF64(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj)
285 {
286 return CompilerEtsLdObjByName<panda_file::Type::TypeId::F64, double>(method, id, pc, obj);
287 }
288
CompilerEtsLdObjByNameObj(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj)289 extern "C" ark::ObjectHeader *CompilerEtsLdObjByNameObj(ark::Method *method, int32_t id, uint32_t pc,
290 ark::ObjectHeader *obj)
291 {
292 return CompilerEtsLdObjByName<panda_file::Type::TypeId::REFERENCE, ark::ObjectHeader *>(method, id, pc, obj);
293 }
294
CompilerEtsStObjByNameI8(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj,int8_t storeValue)295 extern "C" void CompilerEtsStObjByNameI8(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj,
296 int8_t storeValue)
297 {
298 CompilerEtsStObjByName<panda_file::Type::TypeId::I32, int32_t>(method, id, pc, obj,
299 static_cast<int32_t>(storeValue));
300 }
301
CompilerEtsStObjByNameI16(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj,int16_t storeValue)302 extern "C" void CompilerEtsStObjByNameI16(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj,
303 int16_t storeValue)
304 {
305 CompilerEtsStObjByName<panda_file::Type::TypeId::I32, int32_t>(method, id, pc, obj,
306 static_cast<int32_t>(storeValue));
307 }
308
CompilerEtsStObjByNameI32(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj,int32_t storeValue)309 extern "C" void CompilerEtsStObjByNameI32(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj,
310 int32_t storeValue)
311 {
312 CompilerEtsStObjByName<panda_file::Type::TypeId::I32, int32_t>(method, id, pc, obj, storeValue);
313 }
314
CompilerEtsStObjByNameI64(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj,int64_t storeValue)315 extern "C" void CompilerEtsStObjByNameI64(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj,
316 int64_t storeValue)
317 {
318 CompilerEtsStObjByName<panda_file::Type::TypeId::I64, int64_t>(method, id, pc, obj, storeValue);
319 }
320
CompilerEtsStObjByNameF32(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj,float storeValue)321 extern "C" void CompilerEtsStObjByNameF32(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj,
322 float storeValue)
323 {
324 CompilerEtsStObjByName<panda_file::Type::TypeId::F32, float>(method, id, pc, obj, storeValue);
325 }
326
CompilerEtsStObjByNameF64(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj,double storeValue)327 extern "C" void CompilerEtsStObjByNameF64(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj,
328 double storeValue)
329 {
330 CompilerEtsStObjByName<panda_file::Type::TypeId::F64, double>(method, id, pc, obj, storeValue);
331 }
332
CompilerEtsStObjByNameObj(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj,ark::ObjectHeader * storeValue)333 extern "C" void CompilerEtsStObjByNameObj(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj,
334 ark::ObjectHeader *storeValue)
335 {
336 CompilerEtsStObjByNameRef(method, id, pc, obj, storeValue);
337 }
338
CompilerEtsEquals(ObjectHeader * obj1,ObjectHeader * obj2)339 extern "C" uint8_t CompilerEtsEquals(ObjectHeader *obj1, ObjectHeader *obj2)
340 {
341 auto coro = EtsCoroutine::GetCurrent();
342 return static_cast<uint8_t>(EtsReferenceEquals(coro, EtsObject::FromCoreType(obj1), EtsObject::FromCoreType(obj2)));
343 }
344
CompilerEtsStrictEquals(ObjectHeader * obj1,ObjectHeader * obj2)345 extern "C" uint8_t CompilerEtsStrictEquals(ObjectHeader *obj1, ObjectHeader *obj2)
346 {
347 auto coro = EtsCoroutine::GetCurrent();
348 return static_cast<uint8_t>(
349 EtsReferenceEquals<true>(coro, EtsObject::FromCoreType(obj1), EtsObject::FromCoreType(obj2)));
350 }
351
CompilerEtsTypeof(ObjectHeader * obj)352 extern "C" EtsString *CompilerEtsTypeof(ObjectHeader *obj)
353 {
354 auto coro = EtsCoroutine::GetCurrent();
355 return EtsReferenceTypeof(coro, EtsObject::FromCoreType(obj));
356 }
357
CompilerEtsIstrue(ObjectHeader * obj)358 extern "C" uint8_t CompilerEtsIstrue(ObjectHeader *obj)
359 {
360 auto coro = EtsCoroutine::GetCurrent();
361 return static_cast<uint8_t>(EtsIstrue(coro, EtsObject::FromCoreType(obj)));
362 }
363
CompilerDoubleToStringDecimal(ObjectHeader * cache,uint64_t number,uint64_t unused)364 extern "C" EtsString *CompilerDoubleToStringDecimal(ObjectHeader *cache, uint64_t number,
365 [[maybe_unused]] uint64_t unused)
366 {
367 if (UNLIKELY(cache == nullptr)) {
368 return DoubleToStringCache::GetNoCache(bit_cast<double>(number));
369 }
370 return DoubleToStringCache::FromCoreType(cache)->GetOrCache(EtsCoroutine::GetCurrent(), bit_cast<double>(number));
371 }
372
373 } // namespace ark::ets::intrinsics
374