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