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