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>
SetTypedFieldPrimitive(Field * field,const VMHandle<ObjectHeader> & handleObj,T storeValue)181 static void SetTypedFieldPrimitive(Field *field, const VMHandle<ObjectHeader> &handleObj, T storeValue)
182 {
183 switch (field->GetTypeId()) {
184 case panda_file::Type::TypeId::U1:
185 case panda_file::Type::TypeId::U8: {
186 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I32);
187 handleObj.GetPtr()->SetFieldPrimitive(*field, static_cast<uint8_t>(storeValue));
188 return;
189 }
190 case panda_file::Type::TypeId::I8: {
191 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I32);
192 handleObj.GetPtr()->SetFieldPrimitive(*field, static_cast<int8_t>(storeValue));
193 return;
194 }
195 case panda_file::Type::TypeId::I16: {
196 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I32);
197 handleObj.GetPtr()->SetFieldPrimitive(*field, static_cast<int16_t>(storeValue));
198 return;
199 }
200 case panda_file::Type::TypeId::U16: {
201 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I32);
202 handleObj.GetPtr()->SetFieldPrimitive(*field, static_cast<uint16_t>(storeValue));
203 return;
204 }
205 case panda_file::Type::TypeId::I32: {
206 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I32);
207 handleObj.GetPtr()->SetFieldPrimitive(*field, static_cast<int32_t>(storeValue));
208 return;
209 }
210 case panda_file::Type::TypeId::U32: {
211 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I32);
212 handleObj.GetPtr()->SetFieldPrimitive(*field, static_cast<uint32_t>(storeValue));
213 return;
214 }
215 case panda_file::Type::TypeId::I64: {
216 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I64);
217 handleObj.GetPtr()->SetFieldPrimitive(*field, static_cast<int64_t>(storeValue));
218 return;
219 }
220 case panda_file::Type::TypeId::U64: {
221 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::I64);
222 handleObj.GetPtr()->SetFieldPrimitive(*field, static_cast<uint64_t>(storeValue));
223 return;
224 }
225 case panda_file::Type::TypeId::F32: {
226 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::F32);
227 handleObj.GetPtr()->SetFieldPrimitive(*field, storeValue);
228 return;
229 }
230 case panda_file::Type::TypeId::F64: {
231 ASSERT(FIELD_TYPE == panda_file::Type::TypeId::F64);
232 handleObj.GetPtr()->SetFieldPrimitive(*field, storeValue);
233 return;
234 }
235 default: {
236 UNREACHABLE();
237 }
238 }
239 }
240
241 template <panda_file::Type::TypeId FIELD_TYPE, class T>
CompilerEtsStObjByName(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj,T storeValue)242 void CompilerEtsStObjByName(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj, T storeValue)
243 {
244 ASSERT(method != nullptr);
245 ark::Class *klass;
246 ark::Field *rawField;
247 {
248 auto *thread = ManagedThread::GetCurrent();
249 [[maybe_unused]] HandleScope<ObjectHeader *> scope(thread);
250 VMHandle<ObjectHeader> handleObj(thread, obj);
251 auto *classLinker = Runtime::GetCurrent()->GetClassLinker();
252 klass = static_cast<ark::Class *>(obj->ClassAddr<ark::BaseClass>());
253 rawField = classLinker->GetField(*method, panda_file::File::EntityId(id));
254
255 auto field = TryGetField<FIELD_TYPE>(method, rawField, pc, klass);
256 if (field != nullptr) {
257 if constexpr (FIELD_TYPE == panda_file::Type::TypeId::REFERENCE) {
258 UNREACHABLE();
259 } else {
260 SetTypedFieldPrimitive<FIELD_TYPE, T>(field, handleObj, storeValue);
261 return;
262 }
263 }
264
265 auto callee = TryGetCallee<FIELD_TYPE, false>(method, rawField, pc, klass);
266 if (callee != nullptr) {
267 PandaVector<Value> args {Value(handleObj.GetPtr()), Value(storeValue)};
268 callee->Invoke(Coroutine::GetCurrent(), args.data());
269 return;
270 }
271 }
272 LookUpException<true>(klass, rawField);
273 UNREACHABLE();
274 }
275
CompilerEtsStObjByNameRef(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj,ark::ObjectHeader * storeValue)276 void CompilerEtsStObjByNameRef(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj,
277 ark::ObjectHeader *storeValue)
278 {
279 ASSERT(method != nullptr);
280 ark::Class *klass;
281 ark::Field *rawField;
282 {
283 auto *thread = ManagedThread::GetCurrent();
284 [[maybe_unused]] HandleScope<ObjectHeader *> scope(thread);
285 VMHandle<ObjectHeader> handleObj(thread, obj);
286 VMHandle<ObjectHeader> handleStore(thread, storeValue);
287 auto *classLinker = Runtime::GetCurrent()->GetClassLinker();
288 klass = static_cast<ark::Class *>(obj->ClassAddr<ark::BaseClass>());
289 rawField = classLinker->GetField(*method, panda_file::File::EntityId(id));
290
291 auto field = TryGetField<panda_file::Type::TypeId::REFERENCE>(method, rawField, pc, klass);
292 if (field != nullptr) {
293 return handleObj.GetPtr()->SetFieldObject(*field, handleStore.GetPtr());
294 }
295
296 auto callee = TryGetCallee<panda_file::Type::TypeId::REFERENCE, false>(method, rawField, pc, klass);
297 if (callee != nullptr) {
298 PandaVector<Value> args {Value(handleObj.GetPtr()), Value(handleStore.GetPtr())};
299 callee->Invoke(Coroutine::GetCurrent(), args.data());
300 return;
301 }
302 }
303 LookUpException<true>(klass, rawField);
304 UNREACHABLE();
305 }
306
CompilerEtsLdObjByNameI32(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj)307 extern "C" int32_t CompilerEtsLdObjByNameI32(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj)
308 {
309 return CompilerEtsLdObjByName<panda_file::Type::TypeId::I32, int32_t>(method, id, pc, obj);
310 }
311
CompilerEtsLdObjByNameI64(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj)312 extern "C" int64_t CompilerEtsLdObjByNameI64(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj)
313 {
314 return CompilerEtsLdObjByName<panda_file::Type::TypeId::I64, int64_t>(method, id, pc, obj);
315 }
316
CompilerEtsLdObjByNameF32(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj)317 extern "C" float CompilerEtsLdObjByNameF32(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj)
318 {
319 return CompilerEtsLdObjByName<panda_file::Type::TypeId::F32, float>(method, id, pc, obj);
320 }
321
CompilerEtsLdObjByNameF64(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj)322 extern "C" double CompilerEtsLdObjByNameF64(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj)
323 {
324 return CompilerEtsLdObjByName<panda_file::Type::TypeId::F64, double>(method, id, pc, obj);
325 }
326
CompilerEtsLdObjByNameObj(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj)327 extern "C" ark::ObjectHeader *CompilerEtsLdObjByNameObj(ark::Method *method, int32_t id, uint32_t pc,
328 ark::ObjectHeader *obj)
329 {
330 return CompilerEtsLdObjByName<panda_file::Type::TypeId::REFERENCE, ark::ObjectHeader *>(method, id, pc, obj);
331 }
332
CompilerEtsStObjByNameI8(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj,int8_t storeValue)333 extern "C" void CompilerEtsStObjByNameI8(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj,
334 int8_t storeValue)
335 {
336 CompilerEtsStObjByName<panda_file::Type::TypeId::I32, int32_t>(method, id, pc, obj,
337 static_cast<int32_t>(storeValue));
338 }
339
CompilerEtsStObjByNameI16(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj,int16_t storeValue)340 extern "C" void CompilerEtsStObjByNameI16(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj,
341 int16_t storeValue)
342 {
343 CompilerEtsStObjByName<panda_file::Type::TypeId::I32, int32_t>(method, id, pc, obj,
344 static_cast<int32_t>(storeValue));
345 }
346
CompilerEtsStObjByNameI32(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj,int32_t storeValue)347 extern "C" void CompilerEtsStObjByNameI32(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj,
348 int32_t storeValue)
349 {
350 CompilerEtsStObjByName<panda_file::Type::TypeId::I32, int32_t>(method, id, pc, obj, storeValue);
351 }
352
CompilerEtsStObjByNameI64(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj,int64_t storeValue)353 extern "C" void CompilerEtsStObjByNameI64(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj,
354 int64_t storeValue)
355 {
356 CompilerEtsStObjByName<panda_file::Type::TypeId::I64, int64_t>(method, id, pc, obj, storeValue);
357 }
358
CompilerEtsStObjByNameF32(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj,float storeValue)359 extern "C" void CompilerEtsStObjByNameF32(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj,
360 float storeValue)
361 {
362 CompilerEtsStObjByName<panda_file::Type::TypeId::F32, float>(method, id, pc, obj, storeValue);
363 }
364
CompilerEtsStObjByNameF64(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj,double storeValue)365 extern "C" void CompilerEtsStObjByNameF64(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj,
366 double storeValue)
367 {
368 CompilerEtsStObjByName<panda_file::Type::TypeId::F64, double>(method, id, pc, obj, storeValue);
369 }
370
CompilerEtsStObjByNameObj(ark::Method * method,int32_t id,uint32_t pc,ark::ObjectHeader * obj,ark::ObjectHeader * storeValue)371 extern "C" void CompilerEtsStObjByNameObj(ark::Method *method, int32_t id, uint32_t pc, ark::ObjectHeader *obj,
372 ark::ObjectHeader *storeValue)
373 {
374 CompilerEtsStObjByNameRef(method, id, pc, obj, storeValue);
375 }
376
CompilerEtsEquals(ObjectHeader * obj1,ObjectHeader * obj2)377 extern "C" uint8_t CompilerEtsEquals(ObjectHeader *obj1, ObjectHeader *obj2)
378 {
379 auto coro = EtsCoroutine::GetCurrent();
380 return static_cast<uint8_t>(EtsReferenceEquals(coro, EtsObject::FromCoreType(obj1), EtsObject::FromCoreType(obj2)));
381 }
382
CompilerDoubleToStringDecimal(ObjectHeader * cache,uint64_t number,uint64_t unused)383 extern "C" EtsString *CompilerDoubleToStringDecimal(ObjectHeader *cache, uint64_t number,
384 [[maybe_unused]] uint64_t unused)
385 {
386 ASSERT(cache != nullptr);
387 return DoubleToStringCache::FromCoreType(cache)->GetOrCache(EtsCoroutine::GetCurrent(), bit_cast<double>(number));
388 }
389
390 } // namespace ark::ets::intrinsics
391