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 <cstdio>
17 #include <string>
18
19 #include "assembler/assembly-program.h"
20 #include "assembler/assembly-function.h"
21 #include "assembly-type.h"
22 #include "ins_create_api.h"
23
24 #include "plugins/ets/runtime/ets_class_linker_extension.h"
25 #include "plugins/ets/runtime/ets_coroutine.h"
26 #include "runtime/include/runtime.h"
27
28 #include "ets_typeapi_create.h"
29 #include "ets_object.h"
30 #include "ets_array.h"
31 #include "utils/utf.h"
32
33 namespace ark::ets {
34
35 constexpr size_t TYPEAPI_CTX_DATA_CCTOR_ARR_REG = 0;
36
AddInitField(uint32_t id,const pandasm::Type & type)37 std::string TypeCreatorCtx::AddInitField(uint32_t id, const pandasm::Type &type)
38 {
39 ASSERT(!type.IsPrimitive());
40 auto &rec = GetTypeAPICtxDataRecord();
41 auto filedIdForIns = rec.name;
42 pandasm::Field fld {SourceLanguage::ETS};
43 fld.name = "f";
44 fld.name += std::to_string(id);
45 filedIdForIns += ".";
46 filedIdForIns += fld.name;
47
48 fld.type = type;
49 fld.metadata->SetAttribute(typeapi_create_consts::ATTR_FINAL);
50 fld.metadata->SetAttribute(typeapi_create_consts::ATTR_STATIC);
51 rec.fieldList.emplace_back(std::move(fld));
52
53 ctxDataRecordCctor_.AddInstruction(pandasm::Create_LDAI(id));
54 ctxDataRecordCctor_.AddInstruction(pandasm::Create_LDARR_OBJ(TYPEAPI_CTX_DATA_CCTOR_ARR_REG));
55 ctxDataRecordCctor_.AddInstruction(pandasm::Create_CHECKCAST(type.GetPandasmName()));
56 ctxDataRecordCctor_.AddInstruction(pandasm::Create_STSTATIC_OBJ(filedIdForIns));
57
58 return filedIdForIns;
59 }
60
FlushTypeAPICtxDataRecordsToProgram()61 void TypeCreatorCtx::FlushTypeAPICtxDataRecordsToProgram()
62 {
63 if (ctxDataRecord_.name.empty()) {
64 return;
65 }
66
67 ctxDataRecordCctor_.AddInstruction(pandasm::Create_RETURN_VOID());
68
69 [[maybe_unused]] auto res = AddPandasmRecord(std::move(ctxDataRecord_));
70 // Record must be unique
71 ASSERT(res.second);
72 auto name = ctxDataRecordCctor_.name;
73 prog_.functionStaticTable.emplace(std::move(name), std::move(ctxDataRecordCctor_));
74 }
75
SaveObjects(EtsCoroutine * coro,VMHandle<EtsArray> & objects,ClassLinkerContext * ctx)76 void TypeCreatorCtx::SaveObjects(EtsCoroutine *coro, VMHandle<EtsArray> &objects, ClassLinkerContext *ctx)
77 {
78 auto arrayKlass = coro->GetPandaVM()->GetClassLinker()->GetClass(
79 pandasm::Type {typeapi_create_consts::TYPE_OBJECT, 1, true}.GetDescriptor(true).c_str(), true, ctx);
80 ASSERT(arrayKlass != nullptr);
81 auto arr = coretypes::Array::Create(arrayKlass->GetRuntimeClass(), 1, SpaceType::SPACE_TYPE_NON_MOVABLE_OBJECT);
82 arr->Set<ObjectHeader *>(0, objects.GetPtr()->GetCoreType());
83 initArrObject_ = arr;
84 objects = VMHandle<EtsArray>(coro, initArrObject_);
85 }
86
GetObjects(EtsCoroutine * coro)87 EtsArray *TypeCreatorCtx::GetObjects([[maybe_unused]] EtsCoroutine *coro)
88 {
89 // NOTE: this code is possible because that array is allocated as non-movable!
90 // and is stored in vmhandle => can't be moved or deallcoated
91 return reinterpret_cast<EtsArray *>(initArrObject_->Get<ObjectHeader *>(0));
92 }
93
InitializeCtxDataRecord(EtsCoroutine * coro,ClassLinkerContext * ctx,const panda_file::File * pf)94 bool TypeCreatorCtx::InitializeCtxDataRecord(EtsCoroutine *coro, ClassLinkerContext *ctx, const panda_file::File *pf)
95 {
96 ASSERT(coro != nullptr);
97 ASSERT(ctx != nullptr);
98 ASSERT(pf != nullptr);
99
100 LoadCreatedClasses(ctx, pf);
101
102 if (ctxDataRecordName_.empty()) {
103 return true;
104 }
105
106 auto *etsLinker = coro->GetPandaVM()->GetClassLinker();
107 auto *errorHandler = etsLinker->GetEtsClassLinkerExtension()->GetErrorHandler();
108 auto clsDescriptor = pandasm::Type(ctxDataRecordName_, 0).GetDescriptor();
109 // At this point, the class must be already loaded in `LoadCreatedClasses`
110 auto *klass = etsLinker->GetClass(clsDescriptor.c_str(), true, ctx, errorHandler);
111 ASSERT(klass != nullptr);
112 ASSERT(!klass->IsInitialized());
113 auto result = etsLinker->InitializeClass(coro, klass);
114 if (LIKELY(result)) {
115 ASSERT(klass->IsInitialized());
116 } else {
117 ASSERT(coro->HasPendingException());
118 }
119 return result;
120 }
121
LoadCreatedClasses(ClassLinkerContext * ctx,const panda_file::File * pf)122 void TypeCreatorCtx::LoadCreatedClasses(ClassLinkerContext *ctx, const panda_file::File *pf)
123 {
124 auto *coreLinker = Runtime::GetCurrent()->GetClassLinker();
125 for (const auto &clsName : createdRecords_) {
126 // Ignore primitive, so that generated class could have the same name as primitive type encoding
127 auto clsDescriptor = pandasm::Type(clsName, 0, true).GetDescriptor(true);
128 auto classId = pf->GetClassId(utf::CStringAsMutf8(clsDescriptor.c_str()));
129 ASSERT(classId.IsValid());
130 ASSERT(!pf->IsExternal(classId));
131 // Note that the classes will be within loaded classes of the context,
132 // while their corresponding abc file will not be visible in managed `AbcFile` list
133 auto *klass = coreLinker->LoadClass(*pf, classId, ctx, nullptr, true);
134 // Check that the class was generated and loaded correctly
135 LOG_IF(klass == nullptr, FATAL, RUNTIME) << "Type API failed to load a created class '" << clsDescriptor << "'";
136 }
137 createdRecords_.clear();
138 }
139
GetTypeAPICtxDataRecord()140 pandasm::Record &TypeCreatorCtx::GetTypeAPICtxDataRecord()
141 {
142 if (!ctxDataRecord_.name.empty()) {
143 return ctxDataRecord_;
144 }
145 static std::atomic_uint ctxDataNextName {};
146 ctxDataRecordName_ = typeapi_create_consts::CREATOR_CTX_DATA_PREFIX;
147 ctxDataRecordName_ += std::to_string(ctxDataNextName++);
148 ctxDataRecord_.name = ctxDataRecordName_;
149
150 AddRefTypeAsExternal(std::string {typeapi_create_consts::TYPE_OBJECT});
151 AddRefTypeAsExternal(pandasm::Type {typeapi_create_consts::TYPE_OBJECT, 1}.GetName());
152
153 ctxDataRecordCctor_.name = ctxDataRecord_.name;
154 ctxDataRecordCctor_.name += '.';
155 ctxDataRecordCctor_.name += panda_file::GetCctorName(SourceLanguage::ETS);
156 ctxDataRecordCctor_.metadata->SetAttribute(typeapi_create_consts::ATTR_CCTOR);
157 ctxDataRecordCctor_.metadata->SetAttribute(typeapi_create_consts::ATTR_STATIC);
158 ctxDataRecordCctor_.regsNum = 1;
159 ctxDataRecordCctor_.AddInstruction(pandasm::Create_MOVI_64(0, reinterpret_cast<EtsLong>(this)));
160 ctxDataRecordCctor_.AddInstruction(
161 pandasm::Create_CALL_SHORT(0, 0, std::string {typeapi_create_consts::FUNCTION_GET_OBJECTS_FOR_CCTOR}));
162 ctxDataRecordCctor_.AddInstruction(pandasm::Create_STA_OBJ(TYPEAPI_CTX_DATA_CCTOR_ARR_REG));
163
164 AddRefTypeAsExternal(std::string {typeapi_create_consts::TYPE_TYPE_CREATOR_CTX});
165 pandasm::Function getObjectsArray {std::string {typeapi_create_consts::FUNCTION_GET_OBJECTS_FOR_CCTOR},
166 SourceLanguage::ETS};
167 getObjectsArray.metadata->SetAttribute(typeapi_create_consts::ATTR_EXTERNAL);
168 getObjectsArray.returnType = pandasm::Type {typeapi_create_consts::TYPE_OBJECT, 1};
169 getObjectsArray.params.emplace_back(
170 pandasm::Type::FromPrimitiveId(ConvertEtsTypeToPandaType(EtsType::LONG).GetId()), SourceLanguage::ETS);
171 prog_.AddToFunctionTable(std::move(getObjectsArray));
172
173 return ctxDataRecord_;
174 }
175
AddRefTypeAsExternal(const std::string & name)176 pandasm::Record &TypeCreatorCtx::AddRefTypeAsExternal(const std::string &name)
177 {
178 pandasm::Record objectRec {name, SourceLanguage::ETS};
179 objectRec.metadata->SetAttribute(typeapi_create_consts::ATTR_EXTERNAL);
180 return AddPandasmRecord(std::move(objectRec)).first;
181 }
182
AddPandasmRecord(pandasm::Record && record)183 std::pair<pandasm::Record &, bool> TypeCreatorCtx::AddPandasmRecord(pandasm::Record &&record)
184 {
185 if (!record.metadata->GetAttribute(std::string(typeapi_create_consts::ATTR_EXTERNAL))) {
186 createdRecords_.emplace_back(record.name);
187 }
188 auto [iter, inserted] = prog_.recordTable.emplace(record.name, std::move(record));
189 return {iter->second, inserted};
190 }
191
DeclarePrimitive(const std::string & primTypeName)192 const std::pair<std::string, std::string> &TypeCreatorCtx::DeclarePrimitive(const std::string &primTypeName)
193 {
194 if (auto found = primitiveTypesCtorDtor_.find(primTypeName); found != primitiveTypesCtorDtor_.end()) {
195 return found->second;
196 }
197
198 auto primType = pandasm::Type {primTypeName, 0};
199
200 std::string objectTypeName {typeapi_create_consts::TYPE_BOXED_PREFIX};
201 if (primTypeName == "u1") {
202 objectTypeName += "Boolean";
203 } else if (primTypeName == "i8") {
204 objectTypeName += "Byte";
205 } else if (primTypeName == "i16") {
206 objectTypeName += "Short";
207 } else if (primTypeName == "i32") {
208 objectTypeName += "Int";
209 } else if (primTypeName == "i64") {
210 objectTypeName += "Long";
211 } else if (primTypeName == "u16") {
212 objectTypeName += "Char";
213 } else if (primTypeName == "void") {
214 objectTypeName += "Void";
215 } else if (primTypeName == "f32") {
216 objectTypeName += "Float";
217 } else if (primTypeName == "f64") {
218 objectTypeName += "Double";
219 } else {
220 UNREACHABLE();
221 }
222 AddRefTypeAsExternal(objectTypeName);
223 auto objectType = pandasm::Type {objectTypeName, 0};
224
225 PandasmMethodCreator ctor {objectTypeName + "." + panda_file::GetCtorName(SourceLanguage::ETS), this};
226 ctor.AddParameter(objectType);
227 ctor.AddParameter(primType);
228 ctor.GetFn().metadata->SetAttribute(typeapi_create_consts::ATTR_EXTERNAL);
229 ctor.GetFn().metadata->SetAttribute(typeapi_create_consts::ATTR_CTOR);
230 ctor.Create();
231
232 PandasmMethodCreator unboxed {objectTypeName + ".unboxed", this};
233 unboxed.AddParameter(objectType);
234 unboxed.AddResult(primType);
235 unboxed.GetFn().metadata->SetAttribute(typeapi_create_consts::ATTR_EXTERNAL);
236 unboxed.Create();
237
238 return primitiveTypesCtorDtor_
239 .emplace(primTypeName, std::make_pair(ctor.GetFunctionName(), unboxed.GetFunctionName()))
240 .first->second;
241 }
242
AddParameter(pandasm::Type param)243 void LambdaTypeCreator::AddParameter(pandasm::Type param)
244 {
245 ASSERT(!param.IsVoid());
246 fn_.params.emplace_back(std::move(param), SourceLanguage::ETS);
247 }
248
AddResult(const pandasm::Type & type)249 void LambdaTypeCreator::AddResult(const pandasm::Type &type)
250 {
251 fn_.returnType = type;
252 }
253
Create()254 void LambdaTypeCreator::Create()
255 {
256 ASSERT(!finished_);
257 finished_ = true;
258 // IMPORTANT: must be synchronized with
259 // tools/es2panda/varbinder/ETSBinder.cpp
260 // ETSBinder::FormLambdaName
261
262 static constexpr size_t MAX_NUMBER_OF_PARAMS_FOR_FUNCTIONAL_INTERFACE = 16;
263
264 if (fn_.params.size() > MAX_NUMBER_OF_PARAMS_FOR_FUNCTIONAL_INTERFACE) {
265 GetCtx()->AddError("Function types with more than 16 parameters are not supported");
266 return;
267 }
268 auto aritySuffix = std::to_string(fn_.params.size());
269 name_ += aritySuffix;
270
271 rec_.name = name_;
272 fnName_ = fn_.name = name_ + "." + STD_CORE_FUNCTION_INVOKE_PREFIX + aritySuffix;
273 fn_.params.insert(fn_.params.begin(), pandasm::Function::Parameter(pandasm::Type(name_, 0), SourceLanguage::ETS));
274 for (const auto &attr : typeapi_create_consts::ATTR_ABSTRACT_METHOD) {
275 fn_.metadata->SetAttribute(attr);
276 }
277 fn_.metadata->SetAttributeValue(typeapi_create_consts::ATTR_ACCESS, typeapi_create_consts::ATTR_ACCESS_VAL_PUBLIC);
278
279 // Register `std.core.Function` type and its `FN_INVOKE_METHOD_PREFIX+N` method as external,
280 // as they are already defined in standard library
281 GetCtx()->AddRefTypeAsExternal(name_);
282 fn_.metadata->SetAttribute(typeapi_create_consts::ATTR_EXTERNAL);
283 fn_.metadata->SetAttributeValue(typeapi_create_consts::ATTR_ACCESS_FUNCTION,
284 typeapi_create_consts::ATTR_ACCESS_VAL_PUBLIC);
285 GetCtx()->Program().AddToFunctionTable(std::move(fn_));
286 }
287
AddParameter(pandasm::Type param)288 void PandasmMethodCreator::AddParameter(pandasm::Type param)
289 {
290 name_ += ':';
291 name_ += param.GetName();
292 fn_.params.emplace_back(std::move(param), SourceLanguage::ETS);
293 }
294
AddResult(pandasm::Type type)295 void PandasmMethodCreator::AddResult(pandasm::Type type)
296 {
297 if (fn_.metadata->IsCtor()) {
298 fn_.returnType = pandasm::Type {"void", 0};
299 return;
300 }
301 fn_.returnType = std::move(type);
302 }
303
Create()304 void PandasmMethodCreator::Create()
305 {
306 ASSERT(!finished_);
307 finished_ = true;
308 fn_.name = name_;
309
310 auto &functionTable = fn_.IsStatic() ? ctx_->Program().functionStaticTable : ctx_->Program().functionInstanceTable;
311 auto ok = functionTable.emplace(name_, std::move(fn_)).second;
312 if (!ok) {
313 ctx_->AddError("duplicate function " + name_);
314 }
315 }
316 } // namespace ark::ets
317