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 "checker/ETSchecker.h"
17 #include "checker/types/globalTypesHolder.h"
18
19 namespace ark::es2panda::checker {
20
ETSFunctionType(ETSChecker * checker,util::StringView name,ArenaVector<Signature * > && signatures)21 ETSFunctionType::ETSFunctionType([[maybe_unused]] ETSChecker *checker, util::StringView name,
22 ArenaVector<Signature *> &&signatures)
23 : Type(TypeFlag::FUNCTION | TypeFlag::ETS_METHOD),
24 callSignatures_(std::move(signatures)),
25 extensionFunctionSigs_(ArenaVector<Signature *>(checker->ProgramAllocator()->Adapter())),
26 extensionAccessorSigs_(ArenaVector<Signature *>(checker->ProgramAllocator()->Adapter())),
27 name_(name)
28 {
29 auto flag = TypeFlag::NONE;
30 for (auto *sig : callSignatures_) {
31 flag |= sig->HasSignatureFlag(SignatureFlags::GETTER) ? TypeFlag::GETTER : TypeFlag::NONE;
32 flag |= sig->HasSignatureFlag(SignatureFlags::SETTER) ? TypeFlag::SETTER : TypeFlag::NONE;
33 if (sig->IsExtensionAccessor()) {
34 extensionAccessorSigs_.emplace_back(sig);
35 } else if (sig->IsExtensionFunction()) {
36 extensionFunctionSigs_.emplace_back(sig);
37 }
38 }
39 AddTypeFlag(flag);
40 }
41
ETSFunctionType(ETSChecker * checker,Signature * signature)42 ETSFunctionType::ETSFunctionType(ETSChecker *checker, Signature *signature)
43 : Type(TypeFlag::FUNCTION),
44 callSignatures_({{signature->ToArrowSignature(checker)}, checker->ProgramAllocator()->Adapter()}),
45 extensionFunctionSigs_(ArenaVector<Signature *>(checker->ProgramAllocator()->Adapter())),
46 extensionAccessorSigs_(ArenaVector<Signature *>(checker->ProgramAllocator()->Adapter())),
47 name_(""),
48 assemblerName_(checker->GlobalBuiltinFunctionType(signature->MinArgCount(), signature->HasRestParameter()) !=
49 nullptr
50 ? checker->GlobalBuiltinFunctionType(signature->MinArgCount(), signature->HasRestParameter())
51 ->AsETSObjectType()
52 ->AssemblerName()
53 : "")
54 {
55 }
56
57 // #22951: proper this type implementation
HackThisParameterInExtensionFunctionInvoke(ETSObjectType * interface,size_t arity)58 static void HackThisParameterInExtensionFunctionInvoke(ETSObjectType *interface, size_t arity)
59 {
60 auto invokeName = FunctionalInterfaceInvokeName(arity, false);
61 auto *property = interface->AsETSObjectType()->GetOwnProperty<checker::PropertyType::INSTANCE_METHOD>(
62 util::StringView(invokeName));
63 ES2PANDA_ASSERT(property != nullptr);
64 auto *tsType = property->TsType();
65 ES2PANDA_ASSERT(tsType != nullptr);
66 auto &callSigsOfInvoke0 = tsType->AsETSFunctionType()->CallSignatures();
67 for (auto sig : callSigsOfInvoke0) {
68 sig->AddSignatureFlag(SignatureFlags::THIS_RETURN_TYPE);
69 }
70 }
71
FunctionTypeToFunctionalInterfaceType(ETSChecker * checker,Signature * signature,size_t arity)72 static ETSObjectType *FunctionTypeToFunctionalInterfaceType(ETSChecker *checker, Signature *signature, size_t arity)
73 {
74 // #22951: remove flags that affect ETSObjectType::Substitute
75 bool isExtensionHack = signature->HasSignatureFlag(SignatureFlags::EXTENSION_FUNCTION);
76
77 if (signature->RestVar() != nullptr) {
78 auto sigParamsSize = signature->Params().size();
79 auto nPosParams = arity < sigParamsSize ? arity : sigParamsSize;
80 auto *functionN = checker->GlobalBuiltinFunctionType(nPosParams, true);
81 auto *substitution = checker->NewSubstitution();
82 ES2PANDA_ASSERT(functionN != nullptr && nPosParams <= functionN->TypeArguments().size());
83 ES2PANDA_ASSERT(substitution != nullptr);
84 for (size_t i = 0; i < nPosParams; i++) {
85 substitution->emplace(functionN->TypeArguments()[i]->AsETSTypeParameter(),
86 checker->MaybeBoxType(signature->Params()[i]->TsType()));
87 }
88 auto *elementType = !signature->RestVar()->TsType()->IsETSTupleType()
89 ? checker->GetElementTypeOfArray(signature->RestVar()->TsType())
90 : checker->GlobalETSAnyType();
91 substitution->emplace(functionN->TypeArguments()[0]->AsETSTypeParameter(), checker->MaybeBoxType(elementType));
92 return functionN->Substitute(checker->Relation(), substitution, true, isExtensionHack);
93 }
94
95 ES2PANDA_ASSERT(arity >= signature->MinArgCount() && arity <= signature->ArgCount());
96
97 // Note: FunctionN is not supported yet
98 if (arity >= checker->GetGlobalTypesHolder()->VariadicFunctionTypeThreshold()) {
99 return nullptr;
100 }
101
102 auto *funcIface = checker->GlobalBuiltinFunctionType(arity, false);
103 auto *substitution = checker->NewSubstitution();
104
105 ES2PANDA_ASSERT(funcIface != nullptr);
106 ES2PANDA_ASSERT(substitution != nullptr);
107 for (size_t i = 0; i < arity; i++) {
108 substitution->emplace(funcIface->TypeArguments()[i]->AsETSTypeParameter(),
109 checker->MaybeBoxType(signature->Params()[i]->TsType()));
110 }
111 substitution->emplace(funcIface->TypeArguments()[arity]->AsETSTypeParameter(),
112 checker->MaybeBoxType(signature->ReturnType()));
113 auto result = funcIface->Substitute(checker->Relation(), substitution, true, isExtensionHack);
114
115 if (signature->HasSignatureFlag(SignatureFlags::THIS_RETURN_TYPE)) {
116 HackThisParameterInExtensionFunctionInvoke(result, arity);
117 }
118 return result;
119 }
120
ArrowToFunctionalInterface(ETSChecker * checker)121 ETSObjectType *ETSFunctionType::ArrowToFunctionalInterface(ETSChecker *checker)
122 {
123 auto &cached = arrowToFuncInterface_;
124 if (LIKELY(cached != nullptr)) {
125 return cached;
126 }
127 return cached = FunctionTypeToFunctionalInterfaceType(checker, ArrowSignature(), ArrowSignature()->MinArgCount());
128 }
129
ArrowToFunctionalInterfaceDesiredArity(ETSChecker * checker,size_t arity)130 ETSObjectType *ETSFunctionType::ArrowToFunctionalInterfaceDesiredArity(ETSChecker *checker, size_t arity)
131 {
132 if (LIKELY(arity == ArrowSignature()->MinArgCount())) {
133 return ArrowToFunctionalInterface(checker);
134 }
135 return FunctionTypeToFunctionalInterfaceType(checker, ArrowSignature(), arity);
136 }
137
MethodToArrow(ETSChecker * checker)138 ETSFunctionType *ETSFunctionType::MethodToArrow(ETSChecker *checker)
139 {
140 auto &cached = invokeToArrowSignature_;
141 if (LIKELY(cached != nullptr)) {
142 return cached;
143 }
144
145 ES2PANDA_ASSERT(!IsETSArrowType() && CallSignatures().size() == 1);
146 return cached = checker->CreateETSArrowType(CallSignatures()[0]);
147 }
148
AddCallSignature(Signature * signature)149 void ETSFunctionType::AddCallSignature(Signature *signature)
150 {
151 ES2PANDA_ASSERT(!IsETSArrowType());
152
153 if (signature->Function()->IsGetter()) {
154 AddTypeFlag(TypeFlag::GETTER);
155 } else if (signature->Function()->IsSetter()) {
156 AddTypeFlag(TypeFlag::SETTER);
157 }
158 callSignatures_.push_back(signature);
159 }
160
ToString(std::stringstream & ss,bool precise) const161 void ETSFunctionType::ToString(std::stringstream &ss, bool precise) const
162 {
163 callSignatures_[0]->ToString(ss, nullptr, false, precise);
164 }
165
166 // Method types do not participate in most type relations
AssertNoMethodsInFunctionRelation(Type * left,Type * right)167 static inline void AssertNoMethodsInFunctionRelation([[maybe_unused]] Type *left, [[maybe_unused]] Type *right)
168 {
169 ES2PANDA_ASSERT(!left->IsETSMethodType());
170 ES2PANDA_ASSERT(!right->IsETSMethodType());
171 }
172
EnhanceSignatureSubstitution(TypeRelation * relation,Signature * super,Signature * sub)173 static Signature *EnhanceSignatureSubstitution(TypeRelation *relation, Signature *super, Signature *sub)
174 {
175 auto checker = relation->GetChecker()->AsETSChecker();
176 auto *substitution = checker->NewSubstitution();
177
178 auto const enhance = [checker, sub, substitution](Type *param, Type *arg) {
179 return checker->EnhanceSubstitutionForType(sub->GetSignatureInfo()->typeParams, param, arg, substitution);
180 };
181 for (size_t ix = 0; ix < sub->ArgCount(); ix++) {
182 if (!enhance(sub->GetSignatureInfo()->params[ix]->TsType(), super->GetSignatureInfo()->params[ix]->TsType())) {
183 return nullptr;
184 }
185 }
186 if (super->RestVar() != nullptr) {
187 if (!enhance(sub->RestVar()->TsType(), super->RestVar()->TsType())) {
188 return nullptr;
189 }
190 }
191 return sub->Substitute(relation, substitution);
192 }
193
SignatureThrowKindToOrder(Signature * sig)194 static uint8_t SignatureThrowKindToOrder(Signature *sig)
195 {
196 return sig->HasSignatureFlag(SignatureFlags::THROWS) ? 0U
197 : sig->HasSignatureFlag(SignatureFlags::RETHROWS) ? 1U
198 : 2U;
199 }
200
SignatureIsSupertypeOf(TypeRelation * relation,Signature * super,Signature * sub)201 static bool SignatureIsSupertypeOf(TypeRelation *relation, Signature *super, Signature *sub)
202 {
203 if (super->MinArgCount() < sub->MinArgCount()) {
204 return false;
205 }
206 if ((super->RestVar() != nullptr && sub->RestVar() == nullptr) ||
207 (super->RestVar() == nullptr && sub->RestVar() != nullptr)) {
208 return false;
209 }
210 if (!sub->GetSignatureInfo()->typeParams.empty()) {
211 sub = EnhanceSignatureSubstitution(relation, super, sub);
212 if (sub == nullptr) {
213 return false;
214 }
215 }
216 for (size_t idx = 0; idx != sub->MinArgCount(); idx++) {
217 if (!relation->IsSupertypeOf(sub->Params()[idx]->TsType(), super->Params()[idx]->TsType())) {
218 return false;
219 }
220 }
221 if (super->RestVar() != nullptr && !relation->IsSupertypeOf(sub->RestVar()->TsType(), super->RestVar()->TsType())) {
222 return false;
223 }
224 if (!relation->IsSupertypeOf(super->ReturnType(), sub->ReturnType())) {
225 return false;
226 }
227 return SignatureThrowKindToOrder(super) <= SignatureThrowKindToOrder(sub);
228 }
229
CoerceToArrowType(TypeRelation * relation,Type * type)230 static ETSFunctionType *CoerceToArrowType(TypeRelation *relation, Type *type)
231 {
232 if (type->IsETSFunctionType()) {
233 return type->AsETSFunctionType();
234 }
235 if (type->IsETSObjectType() && type->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL)) {
236 auto checker = relation->GetChecker()->AsETSChecker();
237 return type->AsETSObjectType()->GetFunctionalInterfaceInvokeType()->MethodToArrow(checker);
238 }
239 return nullptr;
240 }
241
Identical(TypeRelation * relation,Type * other)242 void ETSFunctionType::Identical(TypeRelation *relation, Type *other)
243 {
244 AssertNoMethodsInFunctionRelation(this, other);
245
246 ETSFunctionType *const otherFnType = CoerceToArrowType(relation, other);
247 if (otherFnType == nullptr) {
248 relation->Result(false);
249 return;
250 }
251
252 relation->Result(SignatureIsSupertypeOf(relation, this->ArrowSignature(), otherFnType->ArrowSignature()) &&
253 SignatureIsSupertypeOf(relation, otherFnType->ArrowSignature(), this->ArrowSignature()));
254 }
255
AssignmentSource(TypeRelation * relation,Type * target)256 bool ETSFunctionType::AssignmentSource(TypeRelation *relation, Type *target)
257 {
258 AssertNoMethodsInFunctionRelation(this, target);
259
260 // this should be defined by the dynamic type itself
261 if (target->IsETSDynamicType()) {
262 ES2PANDA_ASSERT(relation->GetNode() != nullptr);
263 if (relation->GetNode()->IsArrowFunctionExpression()) {
264 ES2PANDA_ASSERT(callSignatures_.size() == 1 && ArrowSignature()->HasSignatureFlag(SignatureFlags::CALL));
265 return relation->Result(true);
266 }
267 return relation->Result(false);
268 }
269
270 return relation->IsSupertypeOf(target, this);
271 }
272
IsSupertypeOf(TypeRelation * relation,Type * source)273 void ETSFunctionType::IsSupertypeOf(TypeRelation *relation, Type *source)
274 {
275 AssertNoMethodsInFunctionRelation(this, source);
276
277 ETSFunctionType *const sourceFnType = CoerceToArrowType(relation, source);
278 if (sourceFnType == nullptr) {
279 relation->Result(false);
280 return;
281 }
282
283 relation->Result(SignatureIsSupertypeOf(relation, this->ArrowSignature(), sourceFnType->ArrowSignature()));
284 }
285
AssignmentTarget(TypeRelation * relation,Type * source)286 void ETSFunctionType::AssignmentTarget(TypeRelation *relation, Type *source)
287 {
288 AssertNoMethodsInFunctionRelation(this, source);
289
290 relation->IsSupertypeOf(this, source);
291 }
292
Instantiate(ArenaAllocator * allocator,TypeRelation * relation,GlobalTypesHolder * globalTypes)293 Type *ETSFunctionType::Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes)
294 {
295 auto checker = relation->GetChecker()->AsETSChecker();
296 if (IsETSArrowType()) {
297 return allocator->New<ETSFunctionType>(checker, ArrowSignature());
298 }
299
300 auto signatures = ArenaVector<Signature *>(allocator->Adapter());
301 for (auto *const signature : callSignatures_) {
302 signatures.emplace_back(signature->Copy(allocator, relation, globalTypes));
303 }
304 return allocator->New<ETSFunctionType>(checker, name_, std::move(signatures));
305 }
306
Substitute(TypeRelation * relation,const Substitution * substitution)307 ETSFunctionType *ETSFunctionType::Substitute(TypeRelation *relation, const Substitution *substitution)
308 {
309 if (substitution != nullptr && !substitution->empty()) {
310 auto *const checker = relation->GetChecker()->AsETSChecker();
311 auto *const allocator = checker->ProgramAllocator();
312
313 auto signatures = ArenaVector<Signature *>(allocator->Adapter());
314 bool anyChange = false;
315
316 for (auto *const signature : callSignatures_) {
317 auto *newSignature = signature->Substitute(relation, substitution);
318 anyChange |= newSignature != signature;
319 signatures.emplace_back(newSignature);
320 }
321
322 if (anyChange) {
323 if (IsETSArrowType()) {
324 return allocator->New<ETSFunctionType>(checker, signatures[0]);
325 }
326 return allocator->New<ETSFunctionType>(checker, name_, std::move(signatures));
327 }
328 }
329
330 return this;
331 }
332
Cast(TypeRelation * relation,Type * target)333 void ETSFunctionType::Cast(TypeRelation *relation, Type *target)
334 {
335 AssertNoMethodsInFunctionRelation(this, target);
336
337 if (target->IsETSObjectType() && target->AsETSObjectType()->IsGlobalETSObjectType()) {
338 relation->Result(true);
339 return;
340 }
341 }
342
CastTarget(TypeRelation * relation,Type * source)343 void ETSFunctionType::CastTarget(TypeRelation *relation, Type *source)
344 {
345 AssertNoMethodsInFunctionRelation(this, source);
346
347 if (relation->IsSupertypeOf(this, source)) {
348 relation->RemoveFlags(TypeRelationFlag::UNCHECKED_CAST);
349 return;
350 }
351 if (relation->InCastingContext() && relation->IsSupertypeOf(source, this)) {
352 relation->RemoveFlags(TypeRelationFlag::UNCHECKED_CAST);
353 return;
354 }
355 relation->Result(false);
356 }
357
IsSubtypeOf(TypeRelation * relation,Type * target)358 void ETSFunctionType::IsSubtypeOf(TypeRelation *relation, Type *target)
359 {
360 AssertNoMethodsInFunctionRelation(this, target);
361
362 ETSChecker *checker = relation->GetChecker()->AsETSChecker();
363
364 // fastpath
365 if (relation->IsSupertypeOf(target, checker->GlobalBuiltinFunctionType())) {
366 relation->Result(true);
367 return;
368 }
369
370 // fastpath, as IsSupertypeOf has already failed
371 if (target->IsETSFunctionType()) {
372 return;
373 }
374
375 relation->IsSupertypeOf(target, ArrowToFunctionalInterface(checker));
376 }
377
ToAssemblerType(std::stringstream & ss) const378 void ETSFunctionType::ToAssemblerType([[maybe_unused]] std::stringstream &ss) const
379 {
380 ss << assemblerName_;
381 }
382
383 // #22952: some function types are still in AST
FunctionAssemblyTypeFromArity(uint32_t arity)384 static std::string FunctionAssemblyTypeFromArity(uint32_t arity)
385 {
386 return "std.core.Function" + std::to_string(arity);
387 }
388
ToDebugInfoType(std::stringstream & ss) const389 void ETSFunctionType::ToDebugInfoType([[maybe_unused]] std::stringstream &ss) const
390 {
391 ES2PANDA_ASSERT(IsETSArrowType());
392 ss << FunctionAssemblyTypeFromArity(ArrowSignature()->MinArgCount());
393 }
394
CheckVarianceRecursively(TypeRelation * relation,VarianceFlag varianceFlag)395 void ETSFunctionType::CheckVarianceRecursively(TypeRelation *relation, VarianceFlag varianceFlag)
396 {
397 // For function, param is `in` and returntype is `out`,(in)=>out
398 for (auto *sig : callSignatures_) {
399 if (sig->HasFunction() && sig->Function()->ReturnTypeAnnotation() != nullptr) {
400 relation->SetNode(sig->Function()->ReturnTypeAnnotation());
401 }
402 relation->CheckVarianceRecursively(sig->ReturnType(),
403 relation->TransferVariant(varianceFlag, VarianceFlag::COVARIANT));
404
405 if (sig->HasRestParameter()) {
406 if (sig->HasFunction()) {
407 relation->SetNode(sig->Function()->Params().at(sig->Params().size()));
408 }
409 relation->CheckVarianceRecursively(sig->RestVar()->TsType(),
410 relation->TransferVariant(varianceFlag, VarianceFlag::COVARIANT));
411 }
412 for (auto *typeParam : sig->Params()) {
413 relation->SetNode(
414 typeParam->Declaration()->AsParameterDecl()->Node()->AsETSParameterExpression()->TypeAnnotation());
415 relation->CheckVarianceRecursively(typeParam->TsType(),
416 relation->TransferVariant(varianceFlag, VarianceFlag::CONTRAVARIANT));
417 }
418 }
419 }
420
421 } // namespace ark::es2panda::checker
422