• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021 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 "classDefinition.h"
17 
18 #include "binder/binder.h"
19 #include "binder/scope.h"
20 #include "compiler/base/literals.h"
21 #include "compiler/base/lreference.h"
22 #include "compiler/core/pandagen.h"
23 #include "typescript/checker.h"
24 #include "ir/astDump.h"
25 #include "ir/base/classProperty.h"
26 #include "ir/base/methodDefinition.h"
27 #include "ir/base/scriptFunction.h"
28 #include "ir/expression.h"
29 #include "ir/expressions/functionExpression.h"
30 #include "ir/expressions/identifier.h"
31 #include "ir/expressions/literals/nullLiteral.h"
32 #include "ir/expressions/literals/numberLiteral.h"
33 #include "ir/expressions/literals/stringLiteral.h"
34 #include "ir/expressions/literals/taggedLiteral.h"
35 #include "ir/ts/tsClassImplements.h"
36 #include "ir/ts/tsIndexSignature.h"
37 #include "ir/ts/tsTypeParameter.h"
38 #include "ir/ts/tsTypeParameterDeclaration.h"
39 #include "ir/ts/tsTypeParameterInstantiation.h"
40 #include "util/helpers.h"
41 
42 namespace panda::es2panda::ir {
43 
Ctor() const44 const FunctionExpression *ClassDefinition::Ctor() const
45 {
46     ASSERT(ctor_ != nullptr);
47     return ctor_->Value();
48 }
49 
GetName() const50 util::StringView ClassDefinition::GetName() const
51 {
52     if (ident_) {
53         return ident_->Name();
54     }
55 
56     if (exportDefault_) {
57         return parser::SourceTextModuleRecord::DEFAULT_LOCAL_NAME;
58     }
59 
60     return "";
61 }
62 
Iterate(const NodeTraverser & cb) const63 void ClassDefinition::Iterate(const NodeTraverser &cb) const
64 {
65     if (ident_) {
66         cb(ident_);
67     }
68 
69     if (typeParams_) {
70         cb(typeParams_);
71     }
72 
73     if (superClass_) {
74         cb(superClass_);
75     }
76 
77     if (superTypeParams_) {
78         cb(superTypeParams_);
79     }
80 
81     for (auto *it : implements_) {
82         cb(it);
83     }
84 
85     cb(ctor_);
86 
87     for (auto *it : body_) {
88         cb(it);
89     }
90 
91     for (auto *it : indexSignatures_) {
92         cb(it);
93     }
94 }
95 
Dump(ir::AstDumper * dumper) const96 void ClassDefinition::Dump(ir::AstDumper *dumper) const
97 {
98     dumper->Add({{"id", AstDumper::Nullable(ident_)},
99                  {"typeParameters", AstDumper::Optional(typeParams_)},
100                  {"superClass", AstDumper::Nullable(superClass_)},
101                  {"superTypeParameters", AstDumper::Optional(superTypeParams_)},
102                  {"implements", implements_},
103                  {"constructor", ctor_},
104                  {"body", body_},
105                  {"indexSignatures", indexSignatures_}});
106 }
107 
CompileHeritageClause(compiler::PandaGen * pg) const108 compiler::VReg ClassDefinition::CompileHeritageClause(compiler::PandaGen *pg) const
109 {
110     compiler::VReg baseReg = pg->AllocReg();
111 
112     if (superClass_) {
113         superClass_->Compile(pg);
114     } else {
115         pg->LoadConst(this, compiler::Constant::JS_HOLE);
116     }
117 
118     pg->StoreAccumulator(this, baseReg);
119     return baseReg;
120 }
121 
InitializeClassName(compiler::PandaGen * pg) const122 void ClassDefinition::InitializeClassName(compiler::PandaGen *pg) const
123 {
124     if (!ident_) {
125         return;
126     }
127 
128     compiler::LReference lref = compiler::LReference::CreateLRef(pg, ident_, true);
129     lref.SetValue();
130 }
131 
132 // NOLINTNEXTLINE(google-runtime-references)
CreateClassStaticProperties(compiler::PandaGen * pg,util::BitSet & compiled) const133 int32_t ClassDefinition::CreateClassStaticProperties(compiler::PandaGen *pg, util::BitSet &compiled) const
134 {
135     auto *buf = pg->NewLiteralBuffer();
136     compiler::LiteralBuffer staticBuf(pg->Allocator());
137     bool seenComputed = false;
138     uint32_t instancePropertyCount = 0;
139     std::unordered_map<util::StringView, size_t> propNameMap;
140     std::unordered_map<util::StringView, size_t> staticPropNameMap;
141 
142     const auto &properties = body_;
143 
144     for (size_t i = 0; i < properties.size(); i++) {
145         if (!properties[i]->IsMethodDefinition()) {
146             continue;
147         }
148         const ir::MethodDefinition *prop = properties[i]->AsMethodDefinition();
149 
150         if (prop->Computed()) {
151             seenComputed = true;
152             continue;
153         }
154 
155         if (prop->IsAccessor()) {
156             break;
157         }
158 
159         if (prop->IsAbstract()) {
160             compiled.Set(i);
161             continue;
162         }
163 
164         if (prop->IsOptional() && prop->Value()->Function()->IsOverload()) {
165             compiled.Set(i);
166             continue;
167         }
168 
169         util::StringView name = util::Helpers::LiteralToPropName(pg->Allocator(), prop->Key());
170         compiler::LiteralBuffer *literalBuf = prop->IsStatic() ? &staticBuf : buf;
171         auto &nameMap = prop->IsStatic() ? staticPropNameMap : propNameMap;
172 
173         size_t bufferPos = literalBuf->Literals().size();
174         auto res = nameMap.insert({name, bufferPos});
175         if (res.second) {
176             if (seenComputed) {
177                 break;
178             }
179 
180             if (!prop->IsStatic()) {
181                 instancePropertyCount++;
182             }
183 
184             literalBuf->Add(pg->Allocator()->New<StringLiteral>(name));
185             literalBuf->Add(nullptr); // save for method internalname
186             literalBuf->Add(nullptr); // save for method affiliate
187         } else {
188             bufferPos = res.first->second;
189         }
190 
191         Literal *value = nullptr;
192 
193         switch (prop->Kind()) {
194             case ir::MethodDefinitionKind::METHOD: {
195                 const ir::FunctionExpression *func = prop->Value()->AsFunctionExpression();
196                 const util::StringView &internalName = func->Function()->Scope()->InternalName();
197 
198                 value = pg->Allocator()->New<TaggedLiteral>(LiteralTag::METHOD, internalName);
199                 literalBuf->ResetLiteral(bufferPos + 1, value);
200                 Literal *methodAffiliate = pg->Allocator()->New<TaggedLiteral>(LiteralTag::METHODAFFILIATE,
201                                                                                func->Function()->FormalParamsLength());
202                 literalBuf->ResetLiteral(bufferPos + 2, methodAffiliate); // bufferPos + 2 is saved for method affiliate
203                 compiled.Set(i);
204                 break;
205             }
206             // TODO refactor this part later
207             case ir::MethodDefinitionKind::GET:
208             case ir::MethodDefinitionKind::SET: {
209                 value = pg->Allocator()->New<NullLiteral>();
210                 literalBuf->ResetLiteral(bufferPos + 1, value);
211                 break;
212             }
213             default: {
214                 UNREACHABLE();
215             }
216         }
217     }
218 
219     /* Static items are stored at the end of the buffer */
220     buf->Insert(&staticBuf);
221 
222     /* The last literal item represents the offset of the first static property. The regular property literal count
223      * is divided by 2 as key/value pairs count as one. */
224     buf->Add(pg->Allocator()->New<NumberLiteral>(instancePropertyCount));
225 
226     return pg->AddLiteralBuffer(buf);
227 }
228 
CompileMissingProperties(compiler::PandaGen * pg,const util::BitSet & compiled,compiler::VReg classReg) const229 void ClassDefinition::CompileMissingProperties(compiler::PandaGen *pg, const util::BitSet &compiled,
230                                                compiler::VReg classReg) const
231 {
232     const auto &properties = body_;
233 
234     compiler::VReg protoReg = pg->AllocReg();
235 
236     pg->LoadObjByName(this, classReg, "prototype");
237     pg->StoreAccumulator(this, protoReg);
238 
239     for (size_t i = 0; i < properties.size(); i++) {
240         if (!properties[i]->IsMethodDefinition() || compiled.Test(i)) {
241             continue;
242         }
243 
244         const ir::MethodDefinition *prop = properties[i]->AsMethodDefinition();
245         if (prop->IsOptional() && prop->Value()->Function()->IsOverload()) {
246             continue;
247         }
248 
249         if (prop->IsAbstract()) {
250             continue;
251         }
252 
253         compiler::VReg dest = prop->IsStatic() ? classReg : protoReg;
254         compiler::RegScope rs(pg);
255 
256         switch (prop->Kind()) {
257             case ir::MethodDefinitionKind::METHOD: {
258                 compiler::Operand key = pg->ToPropertyKey(prop->Key(), prop->Computed());
259 
260                 pg->LoadAccumulator(this, dest);
261                 const ir::FunctionExpression *func = prop->Value()->AsFunctionExpression();
262                 func->Compile(pg);
263 
264                 pg->StoreOwnProperty(prop->Value()->Parent(), dest, key, prop->Computed());
265                 break;
266             }
267             case ir::MethodDefinitionKind::GET:
268             case ir::MethodDefinitionKind::SET: {
269                 compiler::VReg keyReg = pg->LoadPropertyKey(prop->Key(), prop->Computed());
270 
271                 compiler::VReg undef = pg->AllocReg();
272                 pg->LoadConst(this, compiler::Constant::JS_UNDEFINED);
273                 pg->StoreAccumulator(this, undef);
274 
275                 compiler::VReg getter = undef;
276                 compiler::VReg setter = undef;
277 
278                 pg->LoadAccumulator(this, dest);
279 
280                 compiler::VReg accessor = pg->AllocReg();
281                 prop->Value()->Compile(pg);
282                 pg->StoreAccumulator(prop->Value(), accessor);
283 
284                 if (prop->Kind() == ir::MethodDefinitionKind::GET) {
285                     getter = accessor;
286                 } else {
287                     setter = accessor;
288                 }
289 
290                 pg->DefineGetterSetterByValue(this, dest, keyReg, getter, setter, prop->Computed());
291                 break;
292             }
293             default: {
294                 UNREACHABLE();
295             }
296         }
297     }
298 
299     pg->LoadAccumulator(this, classReg);
300 }
301 
Compile(compiler::PandaGen * pg) const302 void ClassDefinition::Compile(compiler::PandaGen *pg) const
303 {
304     if (declare_) {
305         return;
306     }
307 
308     compiler::RegScope rs(pg);
309     compiler::VReg classReg = pg->AllocReg();
310 
311     compiler::LocalRegScope lrs(pg, scope_);
312 
313     compiler::VReg baseReg = CompileHeritageClause(pg);
314     util::StringView ctorId = ctor_->Function()->Scope()->InternalName();
315     util::BitSet compiled(body_.size());
316 
317     int32_t bufIdx = CreateClassStaticProperties(pg, compiled);
318     pg->DefineClassWithBuffer(this, ctorId, bufIdx, baseReg);
319 
320     pg->StoreAccumulator(this, classReg);
321     InitializeClassName(pg);
322 
323     CompileMissingProperties(pg, compiled, classReg);
324 }
325 
Check(checker::Checker * checker) const326 checker::Type *ClassDefinition::Check(checker::Checker *checker) const
327 {
328     // TODO(aszilagyi)
329     return checker->GlobalAnyType();
330 }
331 
UpdateSelf(const NodeUpdater & cb,binder::Binder * binder)332 void ClassDefinition::UpdateSelf(const NodeUpdater &cb, binder::Binder *binder)
333 {
334     auto scopeCtx = binder::LexicalScope<binder::LocalScope>::Enter(binder, scope_);
335 
336     if (ident_) {
337         ident_ = std::get<ir::AstNode *>(cb(ident_))->AsIdentifier();
338     }
339 
340     if (typeParams_) {
341         typeParams_ = std::get<ir::AstNode *>(cb(typeParams_))->AsTSTypeParameterDeclaration();
342     }
343 
344     if (superClass_) {
345         superClass_ = std::get<ir::AstNode *>(cb(superClass_))->AsExpression();
346     }
347 
348     if (superTypeParams_) {
349         superTypeParams_ = std::get<ir::AstNode *>(cb(superTypeParams_))->AsTSTypeParameterInstantiation();
350     }
351 
352     for (auto iter = implements_.begin(); iter != implements_.end(); iter++) {
353         *iter = std::get<ir::AstNode *>(cb(*iter))->AsTSClassImplements();
354     }
355 
356     ctor_ = std::get<ir::AstNode *>(cb(ctor_))->AsMethodDefinition();
357 
358     for (auto iter = body_.begin(); iter != body_.end(); iter++) {
359         *iter = std::get<ir::AstNode *>(cb(*iter))->AsStatement();
360     }
361 
362     for (auto iter = indexSignatures_.begin(); iter != indexSignatures_.end(); iter++) {
363         *iter = std::get<ir::AstNode *>(cb(*iter))->AsTSIndexSignature();
364     }
365 }
366 
367 }  // namespace panda::es2panda::ir
368