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