• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2023-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 "recordLowering.h"
17 #include <algorithm>
18 #include <sstream>
19 #include <string_view>
20 
21 #include "checker/ETSchecker.h"
22 #include "ir/astDump.h"
23 #include "ir/srcDump.h"
24 #include "macros.h"
25 
26 #include "compiler/lowering/scopesInit/scopesInitPhase.h"
27 #include "utils/arena_containers.h"
28 #include "varbinder/ETSBinder.h"
29 #include "compiler/lowering/util.h"
30 
31 namespace ark::es2panda::compiler {
32 
Name() const33 std::string_view RecordLowering::Name() const
34 {
35     static std::string const NAME = "RecordLowering";
36     return NAME;
37 }
38 
TypeToString(checker::Type * type) const39 std::string RecordLowering::TypeToString(checker::Type *type) const
40 {
41     std::stringstream ss;
42     type->ToString(ss);
43     return ss.str();
44 }
45 
Perform(public_lib::Context * ctx,parser::Program * program)46 bool RecordLowering::Perform(public_lib::Context *ctx, parser::Program *program)
47 {
48     for (auto &[_, extPrograms] : program->ExternalSources()) {
49         (void)_;
50         for (auto *extProg : extPrograms) {
51             Perform(ctx, extProg);
52         }
53     }
54 
55     // Replace Record Object Expressions with Block Expressions
56     program->Ast()->TransformChildrenRecursively(
57         [this, ctx](ir::AstNode *ast) -> ir::AstNode * {
58             if (ast->IsObjectExpression()) {
59                 return UpdateObjectExpression(ast->AsObjectExpression(), ctx);
60             }
61 
62             return ast;
63         },
64         Name());
65 
66     return true;
67 }
68 
CheckDuplicateKey(ir::ObjectExpression * expr,public_lib::Context * ctx)69 void RecordLowering::CheckDuplicateKey(ir::ObjectExpression *expr, public_lib::Context *ctx)
70 {
71     std::unordered_set<std::variant<int32_t, int64_t, float, double, util::StringView>> keySet;
72     for (auto *it : expr->Properties()) {
73         auto *prop = it->AsProperty();
74         switch (prop->Key()->Type()) {
75             case ir::AstNodeType::NUMBER_LITERAL: {
76                 auto number = prop->Key()->AsNumberLiteral()->Number();
77                 if ((number.IsInt() && keySet.insert(number.GetInt()).second) ||
78                     (number.IsLong() && keySet.insert(number.GetLong()).second) ||
79                     (number.IsFloat() && keySet.insert(number.GetFloat()).second) ||
80                     (number.IsDouble() && keySet.insert(number.GetDouble()).second)) {
81                     continue;
82                 }
83                 ctx->checker->AsETSChecker()->ThrowTypeError(
84                     "An object literal cannot multiple properties with same name", expr->Start());
85             }
86             case ir::AstNodeType::STRING_LITERAL: {
87                 if (keySet.insert(prop->Key()->AsStringLiteral()->Str()).second) {
88                     continue;
89                 }
90                 ctx->checker->AsETSChecker()->ThrowTypeError(
91                     "An object literal cannot multiple properties with same name", expr->Start());
92             }
93             case ir::AstNodeType::IDENTIFIER: {
94                 ctx->checker->AsETSChecker()->ThrowTypeError("Object literal may only specify known properties",
95                                                              expr->Start());
96             }
97             default: {
98                 UNREACHABLE();
99                 break;
100             }
101         }
102     }
103 }
104 
CreateStatement(const std::string & src,ir::Expression * ident,ir::Expression * key,ir::Expression * value,public_lib::Context * ctx)105 ir::Statement *RecordLowering::CreateStatement(const std::string &src, ir::Expression *ident, ir::Expression *key,
106                                                ir::Expression *value, public_lib::Context *ctx)
107 {
108     std::vector<ir::AstNode *> nodes;
109     if (ident != nullptr) {
110         nodes.push_back(ident);
111     }
112 
113     if (key != nullptr) {
114         nodes.push_back(key);
115     }
116 
117     if (value != nullptr) {
118         nodes.push_back(value);
119     }
120 
121     auto parser = ctx->parser->AsETSParser();
122     auto statements = parser->CreateFormattedStatements(src, nodes);
123     if (!statements.empty()) {
124         return *statements.begin();
125     }
126 
127     return nullptr;
128 }
129 
UpdateObjectExpression(ir::ObjectExpression * expr,public_lib::Context * ctx)130 ir::Expression *RecordLowering::UpdateObjectExpression(ir::ObjectExpression *expr, public_lib::Context *ctx)
131 {
132     auto checker = ctx->checker->AsETSChecker();
133     if (expr->TsType() == nullptr) {
134         // Hasn't been through checker
135         checker->ThrowTypeError("Unexpected type error in Record object literal", expr->Start());
136     }
137 
138     if (!expr->PreferredType()->IsETSObjectType()) {
139         // Unexpected preferred type
140         return expr;
141     }
142 
143     std::stringstream ss;
144     expr->TsType()->ToAssemblerType(ss);
145     if (!(ss.str() == "escompat.Record" || ss.str() == "escompat.Map")) {
146         // Only update object expressions for Map/Record types
147         return expr;
148     }
149 
150     // Access type arguments
151     [[maybe_unused]] size_t constexpr NUM_ARGUMENTS = 2;
152     auto typeArguments = expr->PreferredType()->AsETSObjectType()->TypeArguments();
153     ASSERT(typeArguments.size() == NUM_ARGUMENTS);
154 
155     // check Duplicate key
156     CheckDuplicateKey(expr, ctx);
157 
158     auto *const scope = NearestScope(expr);
159     checker::SavedCheckerContext scc {checker, checker::CheckerStatus::IGNORE_VISIBILITY};
160     auto expressionCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(checker->VarBinder(), scope);
161 
162     // Create Block Expression
163     auto block = CreateBlockExpression(expr, typeArguments[0], typeArguments[1], ctx);
164     block->SetParent(expr->Parent());
165 
166     // Run checks
167     InitScopesPhaseETS::RunExternalNode(block, ctx->checker->VarBinder());
168     checker->VarBinder()->AsETSBinder()->ResolveReferencesForScope(block, NearestScope(block));
169     block->Check(checker);
170 
171     // Replace Object Expression with Block Expression
172     return block;
173 }
174 
CreateBlockExpression(ir::ObjectExpression * expr,checker::Type * keyType,checker::Type * valueType,public_lib::Context * ctx)175 ir::Expression *RecordLowering::CreateBlockExpression(ir::ObjectExpression *expr, checker::Type *keyType,
176                                                       checker::Type *valueType, public_lib::Context *ctx)
177 {
178     /* This function will create block expression in the following format
179      *
180      * let map = new Map<key_type, value_type>();
181      * map.set(k1, v1)
182      * map.set(k2, v2)
183      * ...
184      * map
185      */
186     auto checker = ctx->checker->AsETSChecker();
187 
188     // Initialize map with provided type arguments
189     auto *ident = Gensym(checker->Allocator());
190     std::stringstream ss;
191     expr->TsType()->ToAssemblerType(ss);
192 
193     ArenaVector<ir::Statement *> statements(ctx->allocator->Adapter());
194     auto &properties = expr->Properties();
195     // currently we only have Map and Record in this if branch
196     std::string containerType;
197     if (ss.str() == "escompat.Map") {
198         containerType = "Map";
199     } else {
200         containerType = "Record";
201     }
202 
203     const std::string createSrc =
204         "let @@I1 = new " + containerType + "<" + TypeToString(keyType) + "," + TypeToString(valueType) + ">()";
205     statements.push_back(CreateStatement(createSrc, ident, nullptr, nullptr, ctx));
206 
207     // Build statements from properties
208 
209     for (const auto &property : properties) {
210         ASSERT(property->IsProperty());
211         auto p = property->AsProperty();
212         statements.push_back(
213             CreateStatement("@@I1.set(@@E2, @@E3)", ident->Clone(ctx->allocator, nullptr), p->Key(), p->Value(), ctx));
214     }
215     statements.push_back(CreateStatement("@@I1", ident->Clone(ctx->allocator, nullptr), nullptr, nullptr, ctx));
216 
217     // Create Block Expression
218     auto block = checker->AllocNode<ir::BlockExpression>(std::move(statements));
219     return block;
220 }
221 
222 }  // namespace ark::es2panda::compiler
223