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