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 "etsFunctionType.h"
17 #include "checker/types/typeRelation.h"
18 #include "checker/ETSchecker.h"
19 #include "checker/ets/conversion.h"
20 #include "ir/base/scriptFunction.h"
21 #include "ir/expressions/identifier.h"
22
23 namespace ark::es2panda::checker {
24
ETSFunctionType(ETSChecker * checker,util::StringView name,ArenaVector<Signature * > && signatures)25 ETSFunctionType::ETSFunctionType(ETSChecker *checker, util::StringView name, ArenaVector<Signature *> &&signatures)
26 : Type(TypeFlag::FUNCTION),
27 callSignatures_(std::move(signatures)),
28 name_(name),
29 funcInterface_(checker->ResolveFunctionalInterfaces(callSignatures_))
30 {
31 }
32
FirstAbstractSignature()33 Signature *ETSFunctionType::FirstAbstractSignature()
34 {
35 for (auto *it : callSignatures_) {
36 if (it->HasSignatureFlag(SignatureFlags::ABSTRACT)) {
37 return it;
38 }
39 }
40
41 return nullptr;
42 }
43
ToString(std::stringstream & ss,bool precise) const44 void ETSFunctionType::ToString(std::stringstream &ss, bool precise) const
45 {
46 callSignatures_[0]->ToString(ss, nullptr, false, precise);
47 }
48
Identical(TypeRelation * relation,Type * other)49 void ETSFunctionType::Identical(TypeRelation *relation, Type *other)
50 {
51 if (!other->IsETSFunctionType()) {
52 return;
53 }
54
55 if (callSignatures_.size() == 1 && callSignatures_[0]->HasSignatureFlag(SignatureFlags::TYPE)) {
56 AssignmentTarget(relation, other);
57 return;
58 }
59
60 callSignatures_[0]->Compatible(relation, other->AsETSFunctionType()->CallSignatures()[0]);
61 }
62
AssignmentSource(TypeRelation * relation,Type * target)63 bool ETSFunctionType::AssignmentSource(TypeRelation *relation, Type *target)
64 {
65 if (target->IsETSDynamicType()) {
66 ASSERT(relation->GetNode() != nullptr);
67 if (relation->GetNode()->IsArrowFunctionExpression()) {
68 ASSERT(callSignatures_.size() == 1 && callSignatures_[0]->HasSignatureFlag(SignatureFlags::CALL));
69 relation->Result(true);
70 return true;
71 }
72 relation->Result(false);
73 return false;
74 }
75
76 if (target->IsETSObjectType() && target == relation->GetChecker()->AsETSChecker()->GlobalETSObjectType()) {
77 relation->Result(true);
78 return true;
79 }
80
81 relation->Result(false);
82 return false;
83 }
84
EnhanceSignatureSubstitution(TypeRelation * relation,Signature * super,Signature * sub)85 static Signature *EnhanceSignatureSubstitution(TypeRelation *relation, Signature *super, Signature *sub)
86 {
87 auto checker = relation->GetChecker()->AsETSChecker();
88 auto *substitution = checker->NewSubstitution();
89
90 auto const enhance = [checker, sub, substitution](Type *param, Type *arg) {
91 return checker->EnhanceSubstitutionForType(sub->GetSignatureInfo()->typeParams, param, arg, substitution);
92 };
93 for (size_t ix = 0; ix < super->MinArgCount(); ix++) {
94 if (!enhance(sub->GetSignatureInfo()->params[ix]->TsType(), super->GetSignatureInfo()->params[ix]->TsType())) {
95 return nullptr;
96 }
97 }
98 if (super->RestVar() != nullptr) {
99 if (!enhance(sub->RestVar()->TsType(), super->RestVar()->TsType())) {
100 return nullptr;
101 }
102 }
103 return sub->Substitute(relation, substitution);
104 }
105
IsCompatibleSignature(TypeRelation * relation,Signature * super,Signature * sub)106 static bool IsCompatibleSignature(TypeRelation *relation, Signature *super, Signature *sub)
107 {
108 if (super->MinArgCount() != sub->MinArgCount()) {
109 return false;
110 }
111 if ((super->RestVar() != nullptr && sub->RestVar() == nullptr) ||
112 (super->RestVar() == nullptr && sub->RestVar() != nullptr)) {
113 return false;
114 }
115 if (!sub->GetSignatureInfo()->typeParams.empty()) {
116 sub = EnhanceSignatureSubstitution(relation, super, sub);
117 if (sub == nullptr) {
118 return false;
119 }
120 }
121
122 for (size_t idx = 0; idx != super->MinArgCount(); idx++) {
123 if (!relation->IsSupertypeOf(sub->Params()[idx]->TsType(), super->Params()[idx]->TsType())) {
124 return false;
125 }
126 }
127 if (super->RestVar() != nullptr && !relation->IsSupertypeOf(sub->RestVar()->TsType(), super->RestVar()->TsType())) {
128 return false;
129 }
130 if (!relation->IsSupertypeOf(super->ReturnType(), sub->ReturnType())) {
131 return false;
132 }
133 return true;
134 }
135
CoerceToFunctionType(Type * type)136 static ETSFunctionType *CoerceToFunctionType(Type *type)
137 {
138 if (type->IsETSFunctionType()) {
139 return type->AsETSFunctionType();
140 }
141 if (type->IsETSObjectType() && type->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL)) {
142 return type->AsETSObjectType()->GetFunctionalInterfaceInvokeType();
143 }
144 return nullptr;
145 }
146
IsSupertypeOf(TypeRelation * relation,Type * source)147 void ETSFunctionType::IsSupertypeOf(TypeRelation *relation, Type *source)
148 {
149 ETSFunctionType *const targetFnType = this;
150 ETSFunctionType *const sourceFnType = CoerceToFunctionType(source);
151 if (sourceFnType == nullptr) {
152 relation->Result(false);
153 return;
154 }
155
156 ASSERT(targetFnType->IsETSArrowType() && sourceFnType->IsETSArrowType());
157 Signature *const targetSig = targetFnType->CallSignatures()[0];
158 Signature *const sourceSig = sourceFnType->CallSignatures()[0];
159
160 SavedTypeRelationFlagsContext savedFlagsCtx(relation, relation->GetTypeRelationFlags() |
161 TypeRelationFlag::ONLY_CHECK_BOXING_UNBOXING);
162 if (!IsCompatibleSignature(relation, targetSig, sourceSig)) {
163 relation->Result(false);
164 return;
165 }
166
167 if (!(targetSig->Function()->IsThrowing() || targetSig->HasSignatureFlag(SignatureFlags::THROWS))) {
168 if (sourceSig->Function()->IsThrowing() || sourceSig->Function()->IsRethrowing() ||
169 sourceSig->HasSignatureFlag(SignatureFlags::THROWS) ||
170 sourceSig->HasSignatureFlag(SignatureFlags::RETHROWS)) {
171 relation->Result(false);
172 return;
173 }
174 }
175
176 ASSERT(relation->GetNode() != nullptr);
177 relation->Result(true);
178 }
179
AssignmentTarget(TypeRelation * relation,Type * source)180 void ETSFunctionType::AssignmentTarget(TypeRelation *relation, Type *source)
181 {
182 ETSFunctionType *const targetFnType = this;
183 ETSFunctionType *const sourceFnType = CoerceToFunctionType(source);
184 if (sourceFnType == nullptr) {
185 relation->Result(false);
186 return;
187 }
188
189 ASSERT(IsETSArrowType() && CallSignatures()[0]->HasSignatureFlag(SignatureFlags::TYPE));
190 Signature *const targetSig = targetFnType->CallSignatures()[0];
191
192 SavedTypeRelationFlagsContext savedFlagsCtx(relation, relation->GetTypeRelationFlags() |
193 TypeRelationFlag::ONLY_CHECK_BOXING_UNBOXING);
194
195 // NOTE(vpukhov): #18866 optionally-typed lambda shouldnt have mulutiple signatures
196 // CC-OFFNXT(G.FMT.14-CPP) project code style
197 Signature *const match = [relation, targetSig, sourceFnType]() -> Signature * {
198 for (auto it : sourceFnType->CallSignatures()) {
199 if (IsCompatibleSignature(relation, targetSig, it)) {
200 return it;
201 }
202 }
203 return nullptr;
204 }();
205 if (match == nullptr) {
206 relation->Result(false);
207 return;
208 }
209
210 if (!(targetSig->Function()->IsThrowing() || targetSig->HasSignatureFlag(SignatureFlags::THROWS))) {
211 if (match->Function()->IsThrowing() || match->Function()->IsRethrowing() ||
212 match->HasSignatureFlag(SignatureFlags::THROWS) || match->HasSignatureFlag(SignatureFlags::RETHROWS)) {
213 relation->GetChecker()->LogTypeError(
214 "Functions that can throw exceptions cannot be assigned to non throwing functions.",
215 relation->GetNode()->Start());
216 relation->Result(RelationResult::ERROR);
217 return;
218 }
219 }
220
221 ASSERT(relation->GetNode() != nullptr);
222 relation->Result(true);
223 }
224
Instantiate(ArenaAllocator * allocator,TypeRelation * relation,GlobalTypesHolder * globalTypes)225 Type *ETSFunctionType::Instantiate([[maybe_unused]] ArenaAllocator *allocator, [[maybe_unused]] TypeRelation *relation,
226 [[maybe_unused]] GlobalTypesHolder *globalTypes)
227 {
228 auto *copiedType = relation->GetChecker()->AsETSChecker()->CreateETSFunctionType(name_);
229
230 for (auto *it : callSignatures_) {
231 copiedType->AddCallSignature(it->Copy(allocator, relation, globalTypes));
232 }
233
234 return copiedType;
235 }
236
Substitute(TypeRelation * relation,const Substitution * substitution)237 ETSFunctionType *ETSFunctionType::Substitute(TypeRelation *relation, const Substitution *substitution)
238 {
239 if (substitution == nullptr || substitution->empty()) {
240 return this;
241 }
242
243 auto *checker = relation->GetChecker()->AsETSChecker();
244
245 auto *copiedType = checker->CreateETSFunctionType(name_);
246 bool anyChange = false;
247
248 for (auto *sig : callSignatures_) {
249 auto *newSig = sig->Substitute(relation, substitution);
250 copiedType->AddCallSignature(newSig);
251 if (newSig != sig) {
252 anyChange = true;
253 }
254 }
255
256 return anyChange ? copiedType : this;
257 }
258
CastFunctionParams(TypeRelation * relation,Signature * targetInvokeSig)259 checker::RelationResult ETSFunctionType::CastFunctionParams(TypeRelation *relation, Signature *targetInvokeSig)
260 {
261 auto *ourSig = callSignatures_[0];
262 auto &ourParams = ourSig->Params();
263 auto &theirParams = targetInvokeSig->Params();
264 if (ourParams.size() != theirParams.size()) {
265 return RelationResult::FALSE;
266 }
267 for (size_t i = 0; i < theirParams.size(); i++) {
268 relation->Result(RelationResult::FALSE);
269 auto savedBoxFlags = relation->GetNode()->GetBoxingUnboxingFlags();
270 relation->IsCastableTo(ourParams[i]->TsType(), theirParams[i]->TsType());
271 relation->GetNode()->SetBoxingUnboxingFlags(savedBoxFlags);
272 if (!relation->IsTrue()) {
273 return RelationResult::FALSE;
274 }
275 }
276 return RelationResult::TRUE;
277 }
278
Cast(TypeRelation * relation,Type * target)279 void ETSFunctionType::Cast(TypeRelation *relation, Type *target)
280 {
281 ASSERT(relation->GetNode()->IsArrowFunctionExpression());
282 auto *savedNode = relation->GetNode();
283 conversion::Forbidden(relation);
284 if (target->HasTypeFlag(TypeFlag::ETS_OBJECT)) {
285 auto *targetType = target->AsETSObjectType();
286 if (targetType->HasObjectFlag(ETSObjectFlags::FUNCTIONAL)) {
287 auto *targetInvokeVar = targetType->GetProperty(FUNCTIONAL_INTERFACE_INVOKE_METHOD_NAME,
288 PropertySearchFlags::SEARCH_INSTANCE_METHOD);
289 if (targetInvokeVar == nullptr || !targetInvokeVar->TsType()->IsETSFunctionType()) {
290 return;
291 }
292 auto *targetInvokeSig = targetInvokeVar->TsType()->AsETSFunctionType()->CallSignatures()[0];
293 relation->Result(CastFunctionParams(relation, targetInvokeSig));
294 auto *targetReturnType = targetInvokeSig->ReturnType();
295 auto savedBoxFlags = relation->GetNode()->GetBoxingUnboxingFlags();
296 relation->IsCastableTo(callSignatures_[0]->ReturnType(), targetReturnType);
297 relation->GetNode()->SetBoxingUnboxingFlags(savedBoxFlags);
298 }
299 if (relation->IsTrue()) {
300 relation->SetNode(savedNode);
301 return;
302 }
303 }
304 }
305
IsSubtypeOf(TypeRelation * relation,Type * target)306 void ETSFunctionType::IsSubtypeOf(TypeRelation *relation, Type *target)
307 {
308 ETSChecker *checker = relation->GetChecker()->AsETSChecker();
309 auto *source = checker->FunctionTypeToFunctionalInterfaceType(callSignatures_[0]);
310
311 if (relation->IsSupertypeOf(target, source)) {
312 relation->Result(true);
313 return;
314 }
315
316 relation->Result(false);
317 }
318
BoxPrimitives(ETSChecker * checker)319 ETSFunctionType *ETSFunctionType::BoxPrimitives(ETSChecker *checker)
320 {
321 auto *allocator = checker->Allocator();
322 auto *ret = allocator->New<ETSFunctionType>(name_, allocator);
323 for (auto *sig : callSignatures_) {
324 ret->AddCallSignature(sig->BoxPrimitives(checker));
325 }
326 return ret;
327 }
328 } // namespace ark::es2panda::checker
329