1 /*
2  * Copyright (c) 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 #ifndef ES2PANDA_EVALUATE_HELPERS_H
17 #define ES2PANDA_EVALUATE_HELPERS_H
18 
19 #include "checker/ETSchecker.h"
20 #include "evaluate/varbinderScopes.h"
21 #include "ir/astNodeFlags.h"
22 #include "varbinder/ETSBinder.h"
23 
24 #include "libpandafile/field_data_accessor.h"
25 #include "libpandafile/method_data_accessor.h"
26 #include "libpandafile/class_data_accessor.h"
27 #include "libpandafile/file.h"
28 #include "type.h"
29 
30 #include <optional>
31 #include <string>
32 
33 namespace ark::es2panda::checker {
34 class Type;
35 }  // namespace ark::es2panda::checker
36 
37 namespace ark::es2panda::ir {
38 class BlockStatement;
39 class Identifier;
40 class TypeNode;
41 class ETSTypeReference;
42 class ClassProperty;
43 }  // namespace ark::es2panda::ir
44 
45 namespace ark::es2panda::parser {
46 class Program;
47 }  // namespace ark::es2panda::parser
48 
49 namespace ark::es2panda::evaluate::helpers {
50 
51 class SafeStateScope final {
52 public:
53     explicit SafeStateScope(checker::ETSChecker *checker, varbinder::ETSBinder *varBinder);
54 
55     ~SafeStateScope();
56 
57     NO_COPY_SEMANTIC(SafeStateScope);
58     NO_MOVE_SEMANTIC(SafeStateScope);
59 
60     void *operator new(size_t) = delete;
61     void *operator new[](size_t) = delete;
62 
63 private:
64     checker::ETSChecker *checker_ {nullptr};
65     varbinder::ETSBinder *varBinder_ {nullptr};
66     varbinder::Scope *checkerScope_ {nullptr};
67     varbinder::GlobalScope *binderTopScope_ {nullptr};
68     varbinder::VariableScope *binderVarScope_ {nullptr};
69     varbinder::Scope *binderScope_ {nullptr};
70     parser::Program *binderProgram_ {nullptr};
71     varbinder::RecordTable *recordTable_ {nullptr};
72 };
73 
74 static inline constexpr std::string_view DEBUGGER_API_CLASS_NAME = "DebuggerAPI";
75 
76 // CC-OFFNXT(G.PRE.02-CPP) code generation
77 // CC-OFFNXT(G.PRE.06) solid logic
78 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
79 #define TYPED_ACCESSOR_NAME_SWITCH(TYPE_NAME_BASE)     \
80     do {                                               \
81         switch (typeId) {                              \
82             case panda_file::Type::TypeId::U1:         \
83                 /* CC-OFFNXT(G.PRE.05) function gen */ \
84                 return #TYPE_NAME_BASE "Boolean";      \
85             case panda_file::Type::TypeId::I8:         \
86                 /* CC-OFFNXT(G.PRE.05) function gen */ \
87                 return #TYPE_NAME_BASE "Byte";         \
88             case panda_file::Type::TypeId::U16:        \
89                 /* CC-OFFNXT(G.PRE.05) function gen */ \
90                 return #TYPE_NAME_BASE "Char";         \
91             case panda_file::Type::TypeId::I16:        \
92                 /* CC-OFFNXT(G.PRE.05) function gen */ \
93                 return #TYPE_NAME_BASE "Short";        \
94             case panda_file::Type::TypeId::I32:        \
95                 /* CC-OFFNXT(G.PRE.05) function gen */ \
96                 return #TYPE_NAME_BASE "Int";          \
97             case panda_file::Type::TypeId::I64:        \
98                 /* CC-OFFNXT(G.PRE.05) function gen */ \
99                 return #TYPE_NAME_BASE "Long";         \
100             case panda_file::Type::TypeId::F32:        \
101                 /* CC-OFFNXT(G.PRE.05) function gen */ \
102                 return #TYPE_NAME_BASE "Float";        \
103             case panda_file::Type::TypeId::F64:        \
104                 /* CC-OFFNXT(G.PRE.05) function gen */ \
105                 return #TYPE_NAME_BASE "Double";       \
106             case panda_file::Type::TypeId::REFERENCE:  \
107                 /* CC-OFFNXT(G.PRE.05) function gen */ \
108                 return #TYPE_NAME_BASE "Object";       \
109             default:                                   \
110                 UNREACHABLE();                         \
111                 /* CC-OFFNXT(G.PRE.05) function gen */ \
112                 return {};                             \
113         }                                              \
114         /* CC-OFFNXT(G.PRE.05) function gen */         \
115         return {};                                     \
116     } while (false)
117 
CreateGetterName(panda_file::Type::TypeId typeId)118 constexpr inline std::string_view CreateGetterName(panda_file::Type::TypeId typeId)
119 {
120     TYPED_ACCESSOR_NAME_SWITCH(getLocal);
121 }
122 
CreateSetterName(panda_file::Type::TypeId typeId)123 constexpr inline std::string_view CreateSetterName(panda_file::Type::TypeId typeId)
124 {
125     TYPED_ACCESSOR_NAME_SWITCH(setLocal);
126 }
127 
128 #undef TYPED_ACCESSOR_NAME_SWITCH
129 
130 template <typename F>
DoScopedAction(checker::ETSChecker *checker, varbinder::ETSBinder *varBinder, parser::Program *program, varbinder::Scope *scope, ir::AstNode *parentClass, F &&action)131 void DoScopedAction(checker::ETSChecker *checker, varbinder::ETSBinder *varBinder, parser::Program *program,
132                     varbinder::Scope *scope, ir::AstNode *parentClass, F &&action)
133 {
134     ASSERT(checker);
135     ASSERT(varBinder);
136     // Must enter either program global scope or a local scope.
137     ASSERT(program != nullptr || scope != nullptr);
138 
139     SafeStateScope s(checker, varBinder);
140 
141     auto runInScope = [checker, varBinder, scope, parentClass](auto &&f) {
142         RecordTableClassScope recordTableScope(varBinder, parentClass);
143         if (scope != nullptr) {
144             auto lexScope = varbinder::LexicalScope<varbinder::Scope>::Enter(varBinder, scope);
145             checker::ScopeContext checkerScope(checker, scope);
146             f();
147         } else {
148             f();
149         }
150     };
151 
152     if (program != nullptr && program != varBinder->Program()) {
153         // Save checker scope because it can differ from VarBinder's scope.
154         checker::ScopeContext savedCheckerScope(checker, checker->Scope());
155         {
156             ProgramScope rcScope(varBinder, program);
157             checker->Initialize(varBinder);
158 
159             runInScope(std::forward<F>(action));
160         }
161         // Switch checker's state back after leaving another program's context.
162         checker->Initialize(varBinder);
163     } else {
164         runInScope(std::forward<F>(action));
165     }
166 }
167 
168 ir::TypeNode *ToTypeNode(std::string_view typeSignature, checker::ETSChecker *checker);
169 
170 ir::TypeNode *PandaTypeToTypeNode(const panda_file::File &pf, panda_file::FieldDataAccessor &fda,
171                                   checker::ETSChecker *checker);
172 
173 ir::TypeNode *PandaTypeToTypeNode(const panda_file::File &pf, panda_file::Type pandaType,
174                                   panda_file::File::EntityId classId, checker::ETSChecker *checker);
175 
176 std::optional<std::string> ToTypeName(std::string_view typeSignature, checker::GlobalTypesHolder *globalTypes);
177 
178 panda_file::Type::TypeId GetTypeId(std::string_view typeSignature);
179 
180 ir::BlockStatement *GetEnclosingBlock(ir::Identifier *ident);
181 
182 template <typename AccessorType>
GetModifierFlags(AccessorType &da, bool forceAddPublicFlag)183 ir::ModifierFlags GetModifierFlags(AccessorType &da, bool forceAddPublicFlag)
184 {
185     auto flags = ir::ModifierFlags::NONE;
186     if (da.IsStatic()) {
187         flags |= ir::ModifierFlags::STATIC;
188     }
189     if (da.IsFinal()) {
190         flags |= ir::ModifierFlags::FINAL;
191     }
192 
193     if (forceAddPublicFlag) {
194         flags |= ir::ModifierFlags::PUBLIC;
195     } else {
196         if (da.IsPublic()) {
197             flags |= ir::ModifierFlags::PUBLIC;
198         }
199         if (da.IsProtected()) {
200             flags |= ir::ModifierFlags::PROTECTED;
201         }
202         if (da.IsPrivate()) {
203             flags |= ir::ModifierFlags::PRIVATE;
204         }
205     }
206 
207     if constexpr (std::is_same_v<AccessorType, panda_file::FieldDataAccessor>) {
208         if (da.IsReadonly()) {
209             flags |= ir::ModifierFlags::READONLY;
210         }
211     } else if constexpr (std::is_same_v<AccessorType, panda_file::MethodDataAccessor>) {
212         if (da.IsNative()) {
213             flags |= ir::ModifierFlags::NATIVE;
214         }
215         if (da.IsAbstract()) {
216             flags |= ir::ModifierFlags::ABSTRACT;
217         }
218     } else {
219         LOG(FATAL, ES2PANDA) << "Should be passed only reference on FieldDataAccessor or MethodDataAccessor.";
220     }
221 
222     return flags;
223 }
224 
225 ir::ModifierFlags GetModifierFlags(panda_file::ClassDataAccessor &da);
226 
227 // Adds `extProgram` into external programs list of the given `program`.
228 void AddExternalProgram(parser::Program *program, parser::Program *extProgram, std::string_view moduleName);
229 
230 ir::ETSTypeReference *CreateETSTypeReference(checker::ETSChecker *checker, util::StringView name);
231 
232 std::pair<std::string_view, std::string_view> SplitRecordName(std::string_view recordName);
233 
234 ir::ClassProperty *CreateClassProperty(checker::ETSChecker *checker, std::string_view name, ir::TypeNode *type,
235                                        ir::ModifierFlags modifiers);
236 
237 }  // namespace ark::es2panda::evaluate::helpers
238 
239 #endif  // ES2PANDA_EVALUATE_HELPERS_H
240