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