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 ¶meters =
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