• 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 #ifndef ES2PANDA_COMPILER_CHECKER_ETS_TYPE_RELATION_CONTEXT_H
17 #define ES2PANDA_COMPILER_CHECKER_ETS_TYPE_RELATION_CONTEXT_H
18 
19 #include "checker/ETSchecker.h"
20 
21 namespace ark::es2panda::checker {
22 class ETSChecker;
23 
24 class AssignmentContext {
25 public:
26     // CC-OFFNXT(G.FUN.01-CPP) solid logic
27     AssignmentContext(TypeRelation *relation, ir::Expression *node, Type *source, Type *target,
28                       const lexer::SourcePosition &pos, std::optional<util::DiagnosticWithParams> diag = std::nullopt,
29                       TypeRelationFlag flags = TypeRelationFlag::NONE)
30     {
31         flags_ |= ((flags & TypeRelationFlag::NO_BOXING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::BOXING;
32         flags_ |= ((flags & TypeRelationFlag::NO_UNBOXING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::UNBOXING;
33         flags_ |= ((flags & TypeRelationFlag::NO_WIDENING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::WIDENING;
34 
35         auto *const etsChecker = relation->GetChecker()->AsETSChecker();
36 
37         ES2PANDA_ASSERT(target != nullptr);
38         ES2PANDA_ASSERT(node != nullptr);
39         ES2PANDA_ASSERT(source != nullptr);
40         if (target->IsETSArrayType() && node->IsArrayExpression()) {
41             assignable_ =
42                 ValidateArrayTypeInitializerByElement(relation, node->AsArrayExpression(), target->AsETSArrayType());
43             relation->Result(assignable_);
44             return;
45         }
46 
47         // CC-OFFNXT(G.FMT.02) project code style
48         flags_ |= flags;
49         relation->SetNode(node);
50 
51         // NOTE (oeotvos) The narrowing flag will be applied here. It means, that the result of "let tmp: int = 1.5"
52         // will be 1, which could cause problems.
53         if (source->HasTypeFlag(TypeFlag::CONSTANT)) {
54             flags_ |= TypeRelationFlag::NARROWING;
55         }
56 
57         relation->SetFlags(flags_);
58 
59         if (!relation->IsAssignableTo(source, target)) {
60             if (relation->IsLegalBoxedPrimitiveConversion(target, source)) {
61                 Type *sourceUnboxedType = etsChecker->MaybeUnboxType(source);
62                 relation->GetNode()->AddBoxingUnboxingFlags(etsChecker->GetUnboxingFlag(sourceUnboxedType));
63                 relation->GetNode()->AddBoxingUnboxingFlags(etsChecker->GetBoxingFlag(target));
64             }
65             if (((flags_ & TypeRelationFlag::UNBOXING) != 0) && !relation->IsTrue() && source->IsETSObjectType() &&
66                 !target->IsETSObjectType()) {
67                 etsChecker->CheckUnboxedTypesAssignable(relation, source, target);
68             }
69             if (((flags_ & TypeRelationFlag::BOXING) != 0) && target->IsETSObjectType() && !relation->IsTrue()) {
70                 etsChecker->CheckBoxedSourceTypeAssignable(relation, source, target);
71             }
72         }
73 
74         if (!relation->IsTrue() && (flags_ & TypeRelationFlag::NO_THROW) == 0) {
75             relation->RaiseError(diag->kind, diag->params, pos);
76         }
77 
78         relation->SetNode(nullptr);
79         relation->SetFlags(TypeRelationFlag::NONE);
80         assignable_ = relation->IsTrue();
81     }
82 
IsAssignable()83     bool IsAssignable() const
84     {
85         return assignable_;
86     }
87 
88     bool ValidateArrayTypeInitializerByElement(TypeRelation *relation, ir::ArrayExpression *node, ETSArrayType *target);
89 
90 private:
91     TypeRelationFlag flags_ = TypeRelationFlag::IN_ASSIGNMENT_CONTEXT;
92     bool assignable_ {false};
93 };
94 
95 class InvocationContext {
96 public:
97     // CC-OFFNXT(G.FUN.01-CPP) solid logic
98     InvocationContext(TypeRelation *relation, ir::Expression *node, Type *source, Type *target,
99                       const lexer::SourcePosition &pos, const std::optional<util::DiagnosticWithParams> &diag,
100                       TypeRelationFlag initialFlags = TypeRelationFlag::NONE)
101     {
102         flags_ |=
103             ((initialFlags & TypeRelationFlag::NO_BOXING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::BOXING;
104         flags_ |=
105             ((initialFlags & TypeRelationFlag::NO_UNBOXING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::UNBOXING;
106 
107         auto *const etsChecker = relation->GetChecker()->AsETSChecker();
108 
109         relation->SetNode(node);
110         relation->SetFlags(flags_ | initialFlags);
111 
112         if (!relation->IsAssignableTo(source, target)) {
113             if (((flags_ & TypeRelationFlag::UNBOXING) != 0U) && !relation->IsTrue() && source->IsETSObjectType() &&
114                 !target->IsETSObjectType()) {
115                 etsChecker->CheckUnboxedSourceTypeWithWideningAssignable(relation, source, target);
116             }
117             if (((flags_ & TypeRelationFlag::BOXING) != 0) && target->IsETSObjectType() && !relation->IsTrue()) {
118                 etsChecker->CheckBoxedSourceTypeAssignable(relation, source, target);
119             }
120         }
121 
122         relation->SetNode(nullptr);
123         relation->SetFlags(TypeRelationFlag::NONE);
124 
125         if (!relation->IsTrue()) {
126             invocable_ = false;
127             if ((initialFlags & TypeRelationFlag::NO_THROW) == 0) {
128                 relation->RaiseError(diag->kind, diag->params, pos);
129             }
130             return;
131         }
132 
133         invocable_ = true;
134     }
135 
IsInvocable()136     bool IsInvocable() const
137     {
138         return invocable_;
139     }
140 
141 private:
142     TypeRelationFlag flags_ = TypeRelationFlag::NONE;
143     bool invocable_ {false};
144 };
145 
146 class ConstraintCheckScope {
147 public:
ConstraintCheckScope(ETSChecker * checker)148     explicit ConstraintCheckScope(ETSChecker *checker) : checker_(checker), isheld_(true)
149     {
150         size_t &counter = checker_->ConstraintCheckScopesCount();
151         ES2PANDA_ASSERT(counter != 0 || checker_->PendingConstraintCheckRecords().empty());
152         counter++;
153     }
154 
~ConstraintCheckScope()155     ~ConstraintCheckScope()
156     {
157         if (isheld_) {
158             Unlock();
159         }
160     }
161 
162     void TryCheckConstraints();
163 
164     NO_COPY_SEMANTIC(ConstraintCheckScope);
165     NO_MOVE_SEMANTIC(ConstraintCheckScope);
166 
167 private:
Unlock()168     bool Unlock()
169     {
170         ES2PANDA_ASSERT(isheld_);
171         isheld_ = false;
172         return --checker_->ConstraintCheckScopesCount() == 0;
173     }
174 
175     ETSChecker *checker_;
176     bool isheld_ {};
177 };
178 
179 class InstantiationContext {
180 public:
InstantiationContext(ETSChecker * checker,ETSObjectType * type,ir::TSTypeParameterInstantiation * typeArgs,const lexer::SourcePosition & pos)181     InstantiationContext(ETSChecker *checker, ETSObjectType *type, ir::TSTypeParameterInstantiation *typeArgs,
182                          const lexer::SourcePosition &pos)
183         : checker_(checker)
184     {
185         if (ValidateTypeArguments(type, typeArgs, pos)) {
186             return;
187         }
188         InstantiateType(type, typeArgs);
189     }
190 
InstantiationContext(ETSChecker * checker,ETSObjectType * type,ArenaVector<Type * > && typeArgs,const lexer::SourcePosition & pos)191     InstantiationContext(ETSChecker *checker, ETSObjectType *type, ArenaVector<Type *> &&typeArgs,
192                          const lexer::SourcePosition &pos)
193         : checker_(checker)
194     {
195         if (type->HasObjectFlag(ETSObjectFlags::ENUM)) {
196             return;
197         }
198         InstantiateType(type, std::move(typeArgs), pos);
199     }
200 
Result()201     Type *Result()
202     {
203         return result_;
204     }
205 
206 private:
207     bool ValidateTypeArguments(ETSObjectType *type, ir::TSTypeParameterInstantiation *typeArgs,
208                                const lexer::SourcePosition &pos);
209 
210     void InstantiateType(ETSObjectType *type, ir::TSTypeParameterInstantiation *typeArgs);
211 
212     void InstantiateType(ETSObjectType *type, ArenaVector<Type *> &&typeArgTypes, const lexer::SourcePosition &pos);
213     util::StringView GetHashFromTypeArguments(ArenaVector<Type *> &typeArgTypes);
214 
215     ETSChecker *checker_;
216     Type *result_ {};
217 };
218 
219 }  // namespace ark::es2panda::checker
220 
221 #endif
222