• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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