• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021 - 2023 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 "signature.h"
17 
18 #include "varbinder/scope.h"
19 #include "ir/base/scriptFunction.h"
20 #include "ir/ts/tsTypeParameter.h"
21 #include "checker/ETSchecker.h"
22 
23 namespace panda::es2panda::checker {
24 
InternalName() const25 util::StringView Signature::InternalName() const
26 {
27     return internalName_.Empty() ? func_->Scope()->InternalName() : internalName_;
28 }
29 
Substitute(TypeRelation * relation,const Substitution * substitution)30 Signature *Signature::Substitute(TypeRelation *relation, const Substitution *substitution)
31 {
32     if (substitution == nullptr || substitution->empty()) {
33         return this;
34     }
35     auto *checker = relation->GetChecker()->AsETSChecker();
36     auto *allocator = checker->Allocator();
37     bool anyChange = false;
38     SignatureInfo *newSigInfo = allocator->New<SignatureInfo>(allocator);
39     const Substitution *newSubstitution = substitution;
40 
41     if (!signatureInfo_->typeParams.empty()) {
42         auto *newSubstitutionSeed = checker->CopySubstitution(substitution);
43         for (auto *tparam : signatureInfo_->typeParams) {
44             auto *newTparam = tparam->Substitute(relation, newSubstitutionSeed);
45             newSigInfo->typeParams.push_back(newTparam);
46             anyChange |= (newTparam != tparam);
47             if (newTparam != tparam && tparam->IsETSTypeParameter()) {
48                 newSubstitutionSeed->insert({tparam->AsETSTypeParameter(), newTparam});
49             }
50         }
51         newSubstitution = newSubstitutionSeed;
52     }
53     newSigInfo->minArgCount = signatureInfo_->minArgCount;
54 
55     for (auto *param : signatureInfo_->params) {
56         auto *newParam = param;
57         auto *newParamType = param->TsType()->Substitute(relation, newSubstitution);
58         if (newParamType != param->TsType()) {
59             anyChange = true;
60             newParam = param->Copy(allocator, param->Declaration());
61             newParam->SetTsType(newParamType);
62         }
63         newSigInfo->params.push_back(newParam);
64     }
65 
66     if (signatureInfo_->restVar != nullptr) {
67         auto *newRestType = signatureInfo_->restVar->TsType()->Substitute(relation, newSubstitution);
68         if (newRestType != signatureInfo_->restVar->TsType()) {
69             anyChange = true;
70             newSigInfo->restVar = signatureInfo_->restVar->Copy(allocator, signatureInfo_->restVar->Declaration());
71             newSigInfo->restVar->SetTsType(newRestType);
72         }
73     }
74 
75     if (!anyChange) {
76         newSigInfo = signatureInfo_;
77     }
78 
79     auto *newReturnType = returnType_->Substitute(relation, newSubstitution);
80     if (newReturnType != returnType_) {
81         anyChange = true;
82     }
83     if (!anyChange) {
84         return this;
85     }
86     auto *result = allocator->New<Signature>(newSigInfo, newReturnType);
87     result->func_ = func_;
88     result->flags_ = flags_;
89     result->internalName_ = internalName_;
90     result->ownerObj_ = ownerObj_;
91     result->ownerVar_ = ownerVar_;
92 
93     return result;
94 }
95 
Copy(ArenaAllocator * allocator,TypeRelation * relation,GlobalTypesHolder * globalTypes)96 Signature *Signature::Copy(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes)
97 {
98     SignatureInfo *copiedInfo = allocator->New<SignatureInfo>(signatureInfo_, allocator);
99 
100     for (size_t idx = 0; idx < signatureInfo_->params.size(); idx++) {
101         auto *const paramType = signatureInfo_->params[idx]->TsType();
102         if (paramType->HasTypeFlag(TypeFlag::GENERIC) && paramType->IsETSObjectType()) {
103             copiedInfo->params[idx]->SetTsType(paramType->Instantiate(allocator, relation, globalTypes));
104             auto originalTypeArgs = paramType->AsETSObjectType()->GetOriginalBaseType()->TypeArguments();
105             copiedInfo->params[idx]->TsType()->AsETSObjectType()->SetTypeArguments(std::move(originalTypeArgs));
106         } else {
107             copiedInfo->params[idx]->SetTsType(
108                 ETSChecker::TryToInstantiate(paramType, allocator, relation, globalTypes));
109         }
110     }
111 
112     auto *const copiedSignature = allocator->New<Signature>(copiedInfo, returnType_, func_);
113     copiedSignature->flags_ = flags_;
114     copiedSignature->internalName_ = internalName_;
115     copiedSignature->ownerObj_ = ownerObj_;
116     copiedSignature->ownerVar_ = ownerVar_;
117 
118     return copiedSignature;
119 }
120 
ToString(std::stringstream & ss,const varbinder::Variable * variable,bool printAsMethod) const121 void Signature::ToString(std::stringstream &ss, const varbinder::Variable *variable, bool printAsMethod) const
122 {
123     if (!signatureInfo_->typeParams.empty()) {
124         ss << "<";
125         for (auto it = signatureInfo_->typeParams.begin(); it != signatureInfo_->typeParams.end(); ++it) {
126             (*it)->ToString(ss);
127             if (std::next(it) != signatureInfo_->typeParams.end()) {
128                 ss << ", ";
129             }
130         }
131         ss << ">";
132     }
133 
134     ss << "(";
135 
136     for (auto it = signatureInfo_->params.begin(); it != signatureInfo_->params.end(); it++) {
137         ss << (*it)->Name();
138 
139         if ((*it)->HasFlag(varbinder::VariableFlags::OPTIONAL)) {
140             ss << "?";
141         }
142 
143         ss << ": ";
144 
145         (*it)->TsType()->ToString(ss);
146 
147         if (std::next(it) != signatureInfo_->params.end()) {
148             ss << ", ";
149         }
150     }
151 
152     if (signatureInfo_->restVar != nullptr) {
153         if (!signatureInfo_->params.empty()) {
154             ss << ", ";
155         }
156 
157         ss << "...";
158         ss << signatureInfo_->restVar->Name();
159         ss << ": ";
160         signatureInfo_->restVar->TsType()->ToString(ss);
161         ss << "[]";
162     }
163 
164     ss << ")";
165 
166     if (printAsMethod || (variable != nullptr && variable->HasFlag(varbinder::VariableFlags::METHOD))) {
167         ss << ": ";
168     } else {
169         ss << " => ";
170     }
171 
172     returnType_->ToString(ss);
173 }
174 
175 namespace {
GetToCheckParamCount(Signature * signature,bool isEts)176 std::size_t GetToCheckParamCount(Signature *signature, bool isEts)
177 {
178     auto paramNumber = static_cast<ssize_t>(signature->Params().size());
179     if (!isEts || signature->Function() == nullptr) {
180         return paramNumber;
181     }
182     for (auto i = paramNumber - 1; i >= 0; i--) {
183         if (!signature->Function()->Params()[i]->AsETSParameterExpression()->IsDefault()) {
184             return static_cast<std::size_t>(i + 1);
185         }
186     }
187     return 0;
188 }
189 }  // namespace
190 
IdenticalParameter(TypeRelation * relation,Type * type1,Type * type2)191 bool Signature::IdenticalParameter(TypeRelation *relation, Type *type1, Type *type2)
192 {
193     if (!CheckFunctionalInterfaces(relation, type1, type2)) {
194         relation->IsIdenticalTo(type1, type2);
195     }
196     return relation->IsTrue();
197 }
198 
Identical(TypeRelation * relation,Signature * other)199 void Signature::Identical(TypeRelation *relation, Signature *other)
200 {
201     bool isEts = relation->GetChecker()->IsETSChecker();
202     auto const thisToCheckParametersNumber = GetToCheckParamCount(this, isEts);
203     auto const otherToCheckParametersNumber = GetToCheckParamCount(other, isEts);
204     if ((thisToCheckParametersNumber != otherToCheckParametersNumber || this->MinArgCount() != other->MinArgCount()) &&
205         this->RestVar() == nullptr && other->RestVar() == nullptr) {
206         // skip check for ets cases only when all parameters are mandatory
207         if (!isEts || (thisToCheckParametersNumber == this->Params().size() &&
208                        otherToCheckParametersNumber == other->Params().size())) {
209             relation->Result(false);
210             return;
211         }
212     }
213 
214     if (relation->NoReturnTypeCheck()) {
215         relation->Result(true);
216     } else {
217         relation->IsIdenticalTo(this->ReturnType(), other->ReturnType());
218     }
219 
220     if (relation->IsTrue()) {
221         /* In ETS, the functions "foo(a: int)" and "foo(a: int, b: int = 1)" should be considered as having an
222            equivalent signature. Hence, we only need to check if the mandatory parameters of the signature with
223            more mandatory parameters can match the parameters of the other signature (including the optional
224            parameter or rest parameters) here.
225 
226            XXX_to_check_parameters_number is calculated beforehand by counting mandatory parameters.
227            Signature::params() stores all parameters (mandatory and optional), excluding the rest parameter.
228            Signature::restVar() stores the rest parameters of the function.
229 
230            For example:
231            foo(a: int): params().size: 1, to_check_param_number: 1, restVar: nullptr
232            foo(a: int, b: int = 0): params().size: 2, to_check_param_number: 1, restVar: nullptr
233            foo(a: int, ...b: int[]): params().size: 1, to_check_param_number: 1, restVar: ...b: int[]
234 
235            Note that optional parameters always come after mandatory parameters, and signatures containing both
236            optional and rest parameters are not allowed.
237 
238            "to_check_parameters_number" is the number of parameters that need to be checked to ensure identical.
239            "parameters_number" is the number of parameters that can be checked in Signature::params().
240         */
241         auto const toCheckParametersNumber = std::max(thisToCheckParametersNumber, otherToCheckParametersNumber);
242         auto const parametersNumber =
243             std::min({this->Params().size(), other->Params().size(), toCheckParametersNumber});
244 
245         std::size_t i = 0U;
246         for (; i < parametersNumber; ++i) {
247             if (!IdenticalParameter(relation, this->Params()[i]->TsType(), other->Params()[i]->TsType())) {
248                 return;
249             }
250         }
251 
252         /* "i" could be one of the following three cases:
253             1. == to_check_parameters_number, we have finished the checking and can directly return.
254             2. == other->Params().size(), must be < this_to_check_parameters_number in this case since
255             xxx->Params().size() always >= xxx_to_check_parameters_number. We need to check the remaining
256             mandatory parameters of "this" against ths RestVar of "other".
257             3. == this->Params().size(), must be < other_to_check_parameters_number as described in 2, and
258             we need to check the remaining mandatory parameters of "other" against the RestVar of "this".
259         */
260         if (i == toCheckParametersNumber) {
261             return;
262         }
263         bool isOtherMandatoryParamsMatched = i < thisToCheckParametersNumber;
264         ArenaVector<varbinder::LocalVariable *> const &parameters =
265             isOtherMandatoryParamsMatched ? this->Params() : other->Params();
266         varbinder::LocalVariable const *restParameter =
267             isOtherMandatoryParamsMatched ? other->RestVar() : this->RestVar();
268         if (restParameter == nullptr) {
269             relation->Result(false);
270             return;
271         }
272         auto *const restParameterType = restParameter->TsType()->AsETSArrayType()->ElementType();
273         for (; i < toCheckParametersNumber; ++i) {
274             if (!IdenticalParameter(relation, parameters[i]->TsType(), restParameterType)) {
275                 return;
276             }
277         }
278     }
279 }
280 
CheckFunctionalInterfaces(TypeRelation * relation,Type * source,Type * target)281 bool Signature::CheckFunctionalInterfaces(TypeRelation *relation, Type *source, Type *target)
282 {
283     if (!source->IsETSObjectType() || !source->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL)) {
284         return false;
285     }
286 
287     if (!target->IsETSObjectType() || !target->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL)) {
288         return false;
289     }
290 
291     auto sourceInvokeFunc = source->AsETSObjectType()
292                                 ->GetProperty(util::StringView("invoke"), PropertySearchFlags::SEARCH_INSTANCE_METHOD)
293                                 ->TsType()
294                                 ->AsETSFunctionType()
295                                 ->CallSignatures()[0];
296 
297     auto targetInvokeFunc = target->AsETSObjectType()
298                                 ->GetProperty(util::StringView("invoke"), PropertySearchFlags::SEARCH_INSTANCE_METHOD)
299                                 ->TsType()
300                                 ->AsETSFunctionType()
301                                 ->CallSignatures()[0];
302 
303     relation->IsIdenticalTo(sourceInvokeFunc, targetInvokeFunc);
304     return true;
305 }
306 
AssignmentTarget(TypeRelation * relation,Signature * source)307 void Signature::AssignmentTarget(TypeRelation *relation, Signature *source)
308 {
309     if (signatureInfo_->restVar == nullptr &&
310         (source->Params().size() - source->OptionalArgCount()) > signatureInfo_->params.size()) {
311         relation->Result(false);
312         return;
313     }
314 
315     for (size_t i = 0; i < source->Params().size(); i++) {
316         if (signatureInfo_->restVar == nullptr && i >= Params().size()) {
317             break;
318         }
319 
320         if (signatureInfo_->restVar != nullptr) {
321             relation->IsAssignableTo(source->Params()[i]->TsType(), signatureInfo_->restVar->TsType());
322 
323             if (!relation->IsTrue()) {
324                 return;
325             }
326 
327             continue;
328         }
329 
330         relation->IsAssignableTo(source->Params()[i]->TsType(), Params()[i]->TsType());
331 
332         if (!relation->IsTrue()) {
333             return;
334         }
335     }
336 
337     relation->IsAssignableTo(source->ReturnType(), returnType_);
338 
339     if (relation->IsTrue() && signatureInfo_->restVar != nullptr && source->RestVar() != nullptr) {
340         relation->IsAssignableTo(source->RestVar()->TsType(), signatureInfo_->restVar->TsType());
341     }
342 }
343 }  // namespace panda::es2panda::checker
344