1 /**
2 * Copyright (c) 2021-2025 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 "compiler/base/literals.h"
19 #include "compiler/base/lreference.h"
20 #include "compiler/core/pandagen.h"
21 #include "typescript/checker.h"
22 #include "ir/astDump.h"
23 #include "ir/base/scriptFunction.h"
24 #include "ir/expressions/assignmentExpression.h"
25 #include "ir/expressions/functionExpression.h"
26 #include "ir/expressions/literals/numberLiteral.h"
27 #include "ir/expressions/literals/stringLiteral.h"
28 #include "ir/expressions/literals/taggedLiteral.h"
29 #include "ir/expressions/memberExpression.h"
30 #include "ir/statements/blockStatement.h"
31 #include "ir/statements/expressionStatement.h"
32 #include "ir/ts/tsClassImplements.h"
33 #include "ir/ts/tsIndexSignature.h"
34 #include "ir/ts/tsTypeParameter.h"
35 #include "ir/ts/tsTypeParameterDeclaration.h"
36 #include "ir/ts/tsTypeParameterInstantiation.h"
37 #include "ir/ts/tsUnionType.h"
38
39 namespace panda::es2panda::ir {
40
Ctor() const41 const FunctionExpression *ClassDefinition::Ctor() const
42 {
43 ASSERT(ctor_ != nullptr);
44 return ctor_->Value();
45 }
46
GetName() const47 util::StringView ClassDefinition::GetName() const
48 {
49 if (ident_) {
50 return ident_->Name();
51 }
52
53 if (exportDefault_) {
54 return parser::SourceTextModuleRecord::DEFAULT_LOCAL_NAME;
55 }
56
57 return "";
58 }
59
Iterate(const NodeTraverser & cb) const60 void ClassDefinition::Iterate(const NodeTraverser &cb) const
61 {
62 if (ident_) {
63 cb(ident_);
64 }
65
66 if (typeParams_) {
67 cb(typeParams_);
68 }
69
70 if (superClass_) {
71 cb(superClass_);
72 }
73
74 if (superTypeParams_) {
75 cb(superTypeParams_);
76 }
77
78 for (auto *it : implements_) {
79 cb(it);
80 }
81
82 cb(ctor_);
83
84 for (auto *it : body_) {
85 cb(it);
86 }
87
88 for (auto *it : indexSignatures_) {
89 cb(it);
90 }
91 }
92
Dump(ir::AstDumper * dumper) const93 void ClassDefinition::Dump(ir::AstDumper *dumper) const
94 {
95 dumper->Add({{"id", AstDumper::Nullable(ident_)},
96 {"typeParameters", AstDumper::Optional(typeParams_)},
97 {"superClass", AstDumper::Nullable(superClass_)},
98 {"superTypeParameters", AstDumper::Optional(superTypeParams_)},
99 {"implements", implements_},
100 {"constructor", ctor_},
101 {"body", body_},
102 {"indexSignatures", indexSignatures_}});
103 }
104
CompileHeritageClause(compiler::PandaGen * pg) const105 compiler::VReg ClassDefinition::CompileHeritageClause(compiler::PandaGen *pg) const
106 {
107 compiler::VReg baseReg = pg->AllocReg();
108
109 if (superClass_) {
110 superClass_->Compile(pg);
111 } else {
112 pg->LoadConst(this, compiler::Constant::JS_HOLE);
113 }
114
115 pg->StoreAccumulator(this, baseReg);
116 return baseReg;
117 }
118
InitializeClassName(compiler::PandaGen * pg) const119 void ClassDefinition::InitializeClassName(compiler::PandaGen *pg) const
120 {
121 if (!ident_) {
122 return;
123 }
124
125 compiler::LReference lref = compiler::LReference::CreateLRef(pg, ident_, true);
126 lref.SetValue();
127 }
128
129 // NOLINTNEXTLINE(google-runtime-references)
CreateClassPublicBuffer(compiler::PandaGen * pg,util::BitSet & compiled,int32_t fieldTypeBufIdx) const130 int32_t ClassDefinition::CreateClassPublicBuffer(compiler::PandaGen *pg, util::BitSet &compiled,
131 int32_t fieldTypeBufIdx) const
132 {
133 auto *buf = pg->NewLiteralBuffer();
134 compiler::LiteralBuffer staticBuf(pg->Allocator());
135 uint32_t instancePropertyCount = 0;
136 std::unordered_map<util::StringView, size_t> propNameMap;
137 std::unordered_map<util::StringView, size_t> staticPropNameMap;
138
139 const auto &properties = body_;
140
141 for (size_t i = 0; i < properties.size(); i++) {
142 if (!properties[i]->IsMethodDefinition()) {
143 continue;
144 }
145 const ir::MethodDefinition *prop = properties[i]->AsMethodDefinition();
146 /* If it's sendable, put the getters/setters into literal buffer.
147 * If not, break at getters/setters to be compatible with api10. */
148 if (prop->IsPrivate()) {
149 continue;
150 }
151
152 if (prop->Computed()) {
153 break;
154 }
155
156 if (prop->IsAccessor() && !isSendable_) {
157 break;
158 }
159
160 if (prop->IsAbstract()) {
161 compiled.Set(i);
162 continue;
163 }
164
165 if (prop->Value()->Function()->IsOverload()) {
166 compiled.Set(i);
167 continue;
168 }
169
170 util::StringView name = util::Helpers::LiteralToPropName(pg->Allocator(), prop->Key());
171 compiler::LiteralBuffer *literalBuf = prop->IsStatic() ? &staticBuf : buf;
172 auto &nameMap = prop->IsStatic() ? staticPropNameMap : propNameMap;
173
174 size_t bufferPos = literalBuf->Literals().size();
175 auto res = nameMap.insert({name, bufferPos});
176 if (res.second || isSendable_) {
177 if (!prop->IsStatic()) {
178 instancePropertyCount++;
179 }
180
181 literalBuf->Add(pg->Allocator()->New<StringLiteral>(name));
182 literalBuf->Add(nullptr); // save for method internalname
183 literalBuf->Add(nullptr); // save for method affiliate
184 } else {
185 bufferPos = res.first->second;
186 }
187
188 const ir::FunctionExpression *func = prop->Value()->AsFunctionExpression();
189 const util::StringView &internalName = func->Function()->Scope()->InternalName();
190
191 LiteralTag litTag = (prop->Kind() == MethodDefinitionKind::METHOD) ? LiteralTag::METHOD :
192 ((prop->Kind() == MethodDefinitionKind::SET) ? LiteralTag::SETTER : LiteralTag::GETTER);
193
194 Literal *value = pg->Allocator()->New<TaggedLiteral>(litTag, internalName);
195 literalBuf->ResetLiteral(bufferPos + 1, value);
196 Literal *methodAffiliate = pg->Allocator()->New<TaggedLiteral>(LiteralTag::METHODAFFILIATE,
197 func->Function()->FormalParamsLength());
198 literalBuf->ResetLiteral(bufferPos + 2, methodAffiliate); // bufferPos + 2 is saved for method affiliate
199 compiled.Set(i);
200 }
201
202 /* Static items are stored at the end of the buffer */
203 buf->Insert(&staticBuf);
204
205 /* The last literal item represents the offset of the first static property. The regular property literal count
206 * is divided by 2 as key/value pairs count as one. */
207 buf->Add(pg->Allocator()->New<NumberLiteral>(instancePropertyCount));
208
209 if (IsSendable()) {
210 std::string recordName = std::string(pg->Binder()->Program()->RecordName());
211 std::string fieldTypeIdxStr = recordName + "_" + std::to_string(fieldTypeBufIdx);
212 util::UString fieldTypeLitId(fieldTypeIdxStr, pg->Allocator());
213 buf->Add(pg->Allocator()->New<TaggedLiteral>(LiteralTag::LITERALARRAY, fieldTypeLitId.View()));
214 }
215
216 if (IsImplementFromEts()) {
217 buf->Add(pg->Allocator()->New<TaggedLiteral>(LiteralTag::ETS_IMPLEMENTS, etsImplementsMessage_));
218 }
219
220 return pg->AddLiteralBuffer(buf);
221 }
222
CreateClassPrivateBuffer(compiler::PandaGen * pg) const223 int32_t ClassDefinition::CreateClassPrivateBuffer(compiler::PandaGen *pg) const
224 {
225 auto *buf = pg->NewLiteralBuffer();
226 compiler::LiteralBuffer staticBuf(pg->Allocator());
227 uint32_t instancePropertyCount = 0;
228
229 for (const auto *prop : body_) {
230 if (!prop->IsMethodDefinition()) {
231 continue;
232 }
233
234 const auto *methodDef = prop->AsMethodDefinition();
235 if (!methodDef->IsPrivate()) {
236 continue;
237 }
238
239 compiler::LiteralBuffer *literalBuf = methodDef->IsStatic() ? &staticBuf : (instancePropertyCount++, buf);
240 const ir::FunctionExpression *func = methodDef->Value()->AsFunctionExpression();
241 const util::StringView &internalName = func->Function()->Scope()->InternalName();
242 Literal *value = nullptr;
243 Literal *methodAffiliate = pg->Allocator()->New<TaggedLiteral>(LiteralTag::METHODAFFILIATE,
244 func->Function()->FormalParamsLength());
245 switch (methodDef->Kind()) {
246 case MethodDefinitionKind::METHOD: {
247 value = pg->Allocator()->New<TaggedLiteral>(LiteralTag::METHOD, internalName);
248 break;
249 }
250 case MethodDefinitionKind::GET: {
251 value = pg->Allocator()->New<TaggedLiteral>(LiteralTag::GETTER, internalName);
252 break;
253 }
254 case MethodDefinitionKind::SET: {
255 value = pg->Allocator()->New<TaggedLiteral>(LiteralTag::SETTER, internalName);
256 break;
257 }
258 default: {
259 UNREACHABLE();
260 }
261 }
262 literalBuf->Add(value);
263 literalBuf->Add(methodAffiliate);
264 }
265
266 buf->Insert(&staticBuf);
267 buf->Add(pg->Allocator()->New<NumberLiteral>(instancePropertyCount));
268
269 return pg->AddLiteralBuffer(buf);
270 }
271
CompileMissingProperties(compiler::PandaGen * pg,const util::BitSet & compiled,compiler::VReg classReg) const272 void ClassDefinition::CompileMissingProperties(compiler::PandaGen *pg, const util::BitSet &compiled,
273 compiler::VReg classReg) const
274 {
275 const auto &properties = body_;
276
277 compiler::VReg protoReg = pg->AllocReg();
278
279 pg->LoadObjByName(this, classReg, "prototype");
280 pg->StoreAccumulator(this, protoReg);
281
282 for (size_t i = 0; i < properties.size(); i++) {
283 if (!properties[i]->IsMethodDefinition() || compiled.Test(i)) {
284 continue;
285 }
286
287 const ir::MethodDefinition *prop = properties[i]->AsMethodDefinition();
288 if (prop->IsOptional() && prop->Value()->Function()->IsOverload()) {
289 continue;
290 }
291
292 if (prop->IsPrivate() || prop->IsAbstract()) {
293 continue;
294 }
295
296 compiler::VReg dest = prop->IsStatic() ? classReg : protoReg;
297 compiler::RegScope rs(pg);
298
299 switch (prop->Kind()) {
300 case ir::MethodDefinitionKind::METHOD: {
301 compiler::Operand key = prop->Computed() ? prop->KeyReg() :
302 pg->ToPropertyKey(prop->Key(), false);
303
304 pg->LoadAccumulator(this, dest);
305 const ir::FunctionExpression *func = prop->Value()->AsFunctionExpression();
306 func->Compile(pg);
307
308 pg->StoreOwnProperty(prop->Value()->Parent(), dest, key, prop->Computed());
309 break;
310 }
311 case ir::MethodDefinitionKind::GET:
312 case ir::MethodDefinitionKind::SET: {
313 CompileGetterOrSetter(pg, dest, prop);
314 break;
315 }
316 default: {
317 UNREACHABLE();
318 }
319 }
320 }
321
322 if (NeedInstanceInitializer()) {
323 InstanceInitialize(pg, protoReg);
324 }
325 }
326
StaticInitialize(compiler::PandaGen * pg,compiler::VReg classReg) const327 void ClassDefinition::StaticInitialize(compiler::PandaGen *pg, compiler::VReg classReg) const
328 {
329 compiler::VReg callee = pg->AllocReg();
330 compiler::VReg thisReg = pg->AllocReg();
331
332 const ir::FunctionExpression *func = staticInitializer_->Value();
333 func->Compile(pg);
334 pg->StoreAccumulator(this, callee);
335
336 pg->MoveVreg(this, thisReg, classReg);
337 pg->CallThis(this, callee, 1);
338
339 pg->LoadAccumulator(this, classReg);
340 }
341
InstanceInitialize(compiler::PandaGen * pg,compiler::VReg protoReg) const342 void ClassDefinition::InstanceInitialize(compiler::PandaGen *pg, compiler::VReg protoReg) const
343 {
344 pg->StoreAccumulator(this, protoReg);
345 instanceInitializer_->Value()->Compile(pg);
346 pg->StoreLexicalVar(instanceInitializer_, 0, GetSlot(instanceInitializer_->Key()));
347 }
348
CompileComputedKeys(compiler::PandaGen * pg) const349 void ClassDefinition::CompileComputedKeys(compiler::PandaGen *pg) const
350 {
351 for (const auto &stmt : body_) {
352 if (stmt->IsClassProperty()) {
353 const ir::ClassProperty *prop = stmt->AsClassProperty();
354
355 // Do not process non-static public fields when not using define semantic.
356 if (!prop->IsStatic() && !pg->Binder()->Program()->UseDefineSemantic()) {
357 continue;
358 }
359
360 if (prop->IsComputed() && prop->NeedCompileKey()) {
361 prop->Key()->Compile(pg);
362 pg->ToComputedPropertyKey(prop->Key());
363 pg->StoreLexicalVar(prop->Key(), 0, GetSlot(prop->Key()));
364 }
365 } else if (stmt->IsMethodDefinition()) {
366 auto *methodDef = stmt->AsMethodDefinition();
367 if (methodDef->Computed()) {
368 compiler::VReg keyReg = pg->AllocReg();
369 methodDef->SetKeyReg(keyReg);
370 methodDef->Key()->Compile(pg);
371 pg->ToComputedPropertyKey(methodDef->Key());
372 pg->StoreAccumulator(methodDef->Key(), keyReg);
373 }
374 }
375 }
376 }
377
Compile(compiler::PandaGen * pg) const378 void ClassDefinition::Compile(compiler::PandaGen *pg) const
379 {
380 if (declare_) {
381 return;
382 }
383
384 if (isSendable_) {
385 CompileSendableClass(pg);
386 return;
387 }
388
389 compiler::RegScope rs(pg);
390 compiler::VReg classReg = pg->AllocReg();
391
392 compiler::LabelTarget target(pg);
393 compiler::VariableEnvScope classEnvScope(pg, scope_, target);
394 compiler::VReg baseReg = CompileHeritageClause(pg);
395 util::StringView ctorId = ctor_->Function()->Scope()->InternalName();
396 util::BitSet compiled(body_.size());
397
398 if (hasComputedKey_) {
399 CompileComputedKeys(pg);
400 }
401
402 int32_t bufIdx = CreateClassPublicBuffer(pg, compiled);
403 pg->DefineClassWithBuffer(this, ctorId, bufIdx, baseReg);
404
405 pg->StoreAccumulator(this, classReg);
406
407 if (HasStaticPrivateMethod()) {
408 pg->StoreLexicalVar(this, 0, scope_->staticMethodValidation_);
409 }
410
411 InitializeClassName(pg);
412
413 CompileMissingProperties(pg, compiled, classReg);
414
415 if (hasPrivateElement_) {
416 int32_t bufIdx = CreateClassPrivateBuffer(pg);
417 pg->CreatePrivateProperty(this, scope_->privateFieldCnt_, bufIdx);
418 }
419
420 pg->LoadAccumulator(this, classReg);
421
422 if (NeedStaticInitializer()) {
423 StaticInitialize(pg, classReg);
424 }
425 }
426
Check(checker::Checker * checker) const427 checker::Type *ClassDefinition::Check(checker::Checker *checker) const
428 {
429 return checker->GlobalAnyType();
430 }
431
UpdateSelf(const NodeUpdater & cb,binder::Binder * binder)432 void ClassDefinition::UpdateSelf(const NodeUpdater &cb, binder::Binder *binder)
433 {
434 auto scopeCtx = binder::LexicalScope<binder::ClassScope>::Enter(binder, scope_);
435
436 if (ident_) {
437 ident_ = std::get<ir::AstNode *>(cb(ident_))->AsIdentifier();
438 }
439
440 if (typeParams_) {
441 typeParams_ = std::get<ir::AstNode *>(cb(typeParams_))->AsTSTypeParameterDeclaration();
442 }
443
444 if (superClass_) {
445 superClass_ = std::get<ir::AstNode *>(cb(superClass_))->AsExpression();
446 }
447
448 if (superTypeParams_) {
449 superTypeParams_ = std::get<ir::AstNode *>(cb(superTypeParams_))->AsTSTypeParameterInstantiation();
450 }
451
452 for (auto iter = implements_.begin(); iter != implements_.end(); iter++) {
453 *iter = std::get<ir::AstNode *>(cb(*iter))->AsTSClassImplements();
454 }
455
456 ctor_ = std::get<ir::AstNode *>(cb(ctor_))->AsMethodDefinition();
457
458 for (auto iter = body_.begin(); iter != body_.end(); iter++) {
459 *iter = std::get<ir::AstNode *>(cb(*iter))->AsStatement();
460 }
461
462 for (auto iter = indexSignatures_.begin(); iter != indexSignatures_.end(); iter++) {
463 *iter = std::get<ir::AstNode *>(cb(*iter))->AsTSIndexSignature();
464 }
465 }
466
467
BuildClassEnvironment(bool useDefineSemantic)468 void ClassDefinition::BuildClassEnvironment(bool useDefineSemantic)
469 {
470 int instancePrivateMethodCnt = 0;
471 int staticPrivateMethodCnt = 0;
472 int privateFieldCnt = 0;
473 std::vector<const Statement *> privateProperties;
474 for (const auto *stmt : body_) {
475 if (stmt->IsMethodDefinition()) {
476 auto *methodDef = stmt->AsMethodDefinition();
477 if (methodDef->IsPrivate()) {
478 privateProperties.push_back(stmt);
479 methodDef->IsStatic() ? staticPrivateMethodCnt ++ : instancePrivateMethodCnt++;
480 } else if (methodDef->Computed()) {
481 hasComputedKey_ = true;
482 }
483 continue;
484 }
485
486 if (stmt->IsClassStaticBlock()) {
487 needStaticInitializer_ = true;
488 continue;
489 }
490
491 ASSERT(stmt->IsClassProperty());
492 const auto *prop = stmt->AsClassProperty();
493 // Do not process non-static public fields when not using define semantic.
494 if (!prop->IsPrivate() && !prop->IsStatic() && !useDefineSemantic) {
495 continue;
496 }
497
498 prop->IsStatic() ? needStaticInitializer_ = true : needInstanceInitializer_ = true;
499
500 if (prop->IsComputed() && prop->NeedCompileKey()) {
501 hasComputedKey_ = true;
502 scope_->AddClassVariable(prop->Key());
503 } else if (prop->IsPrivate()) {
504 privateFieldCnt++;
505 privateProperties.push_back(stmt);
506 }
507 }
508
509 if (!privateProperties.empty()) {
510 hasPrivateElement_ = true;
511 scope_->AddPrivateName(privateProperties, privateFieldCnt, instancePrivateMethodCnt, staticPrivateMethodCnt);
512 }
513
514 if (instancePrivateMethodCnt > 0) {
515 needInstanceInitializer_ = true;
516 }
517
518 if (NeedInstanceInitializer()) {
519 scope_->AddClassVariable(instanceInitializer_->Key());
520 }
521 }
522
AddFieldType(FieldType & fieldType,const Expression * typeAnnotation,compiler::PandaGen * pg) const523 void ClassDefinition::AddFieldType(FieldType &fieldType, const Expression *typeAnnotation,
524 compiler::PandaGen *pg) const
525 {
526 switch (typeAnnotation->Type()) {
527 case AstNodeType::TS_NUMBER_KEYWORD: {
528 fieldType |= FieldType::NUMBER;
529 break;
530 }
531 case AstNodeType::TS_STRING_KEYWORD: {
532 fieldType |= FieldType::STRING;
533 break;
534 }
535 case AstNodeType::TS_BOOLEAN_KEYWORD: {
536 fieldType |= FieldType::BOOLEAN;
537 break;
538 }
539 case AstNodeType::TS_TYPE_REFERENCE: {
540 AddFieldTypeForTypeReference(typeAnnotation->AsTSTypeReference(), fieldType, pg);
541 break;
542 }
543 case AstNodeType::TS_BIGINT_KEYWORD: {
544 fieldType |= FieldType::BIGINT;
545 break;
546 }
547 case AstNodeType::TS_NULL_KEYWORD: {
548 fieldType |= FieldType::TS_NULL;
549 break;
550 }
551 case AstNodeType::TS_UNDEFINED_KEYWORD: {
552 fieldType |= FieldType::TS_UNDEFINED;
553 break;
554 }
555 default: {
556 UNREACHABLE();
557 }
558 }
559 }
560
AddFieldTypeForTypeReference(const TSTypeReference * typeReference,FieldType & fieldType,compiler::PandaGen * pg) const561 void ClassDefinition::AddFieldTypeForTypeReference(const TSTypeReference *typeReference, FieldType &fieldType,
562 compiler::PandaGen *pg) const
563 {
564 auto typeName = typeReference->TypeName();
565 ASSERT(typeName != nullptr);
566 if (!typeName->IsIdentifier()) {
567 fieldType |= FieldType::GENERIC;
568 return;
569 }
570
571 util::StringView propertyName = typeName->AsIdentifier()->Name();
572 binder::ScopeFindResult result = scope_->Find(propertyName);
573
574 // identify import type
575 parser::SourceTextModuleRecord *moduleRecord = pg->Binder()->Program()->TypeModuleRecord();
576 const auto ®ularImportEntries = moduleRecord->GetRegularImportEntries();
577 if (regularImportEntries.find(propertyName) != regularImportEntries.end()) {
578 fieldType |= FieldType::GENERIC;
579 return;
580 }
581
582 if (IsTypeParam(propertyName) || (result.variable != nullptr && result.variable->IsModuleVariable())) {
583 fieldType |= FieldType::GENERIC;
584 return;
585 }
586
587 // ts enum type
588 const ir::AstNode *declNode = GetDeclNodeFromIdentifier(typeName->AsIdentifier());
589 if (declNode != nullptr && declNode->IsTSEnumDeclaration()) {
590 fieldType |= FieldType::STRING | FieldType::NUMBER;
591 return;
592 }
593
594 // sendable class and sendable fuction
595 fieldType |= FieldType::TS_TYPE_REF;
596 }
597
GetDeclNodeFromIdentifier(const ir::Identifier * identifier) const598 const ir::AstNode *ClassDefinition::GetDeclNodeFromIdentifier(const ir::Identifier *identifier) const
599 {
600 if (identifier == nullptr) {
601 return nullptr;
602 }
603
604 for (const auto &v : identifier->TSVariables()) {
605 if (v == nullptr || v->Declaration() == nullptr || v->Declaration()->Node() == nullptr) {
606 continue;
607 }
608
609 auto res = v->Declaration()->Node();
610 return res;
611 }
612
613 // In general, the declaration node of a type node could alaways be found in ts ast.
614 // The following branch, finding declaration node in js ast, is added for some unexpected corner cases.
615 if (identifier->Variable() && identifier->Variable()->Declaration() &&
616 identifier->Variable()->Declaration()->Node()) {
617 return identifier->Variable()->Declaration()->Node()->Original();
618 }
619
620 return nullptr;
621 }
622
IsTypeParam(const util::StringView & propertyName) const623 bool ClassDefinition::IsTypeParam(const util::StringView &propertyName) const
624 {
625 if (typeParams_ == nullptr) {
626 return false;
627 }
628
629 for (auto param : typeParams_->Params()) {
630 util::StringView paramName = param->Name()->AsIdentifier()->Name();
631 if (paramName == propertyName) {
632 return true;
633 }
634 }
635 return false;
636 }
637
CreateFieldTypeBuffer(compiler::PandaGen * pg) const638 int32_t ClassDefinition::CreateFieldTypeBuffer(compiler::PandaGen *pg) const
639 {
640 ASSERT(IsSendable());
641 auto *instanceBuf = pg->NewLiteralBuffer();
642 compiler::LiteralBuffer staticBuf(pg->Allocator());
643 uint32_t instanceFieldCnt {0};
644
645 for (auto *prop : body_) {
646 if (!prop->IsClassProperty()) {
647 continue;
648 }
649
650 auto *classProp = prop->AsClassProperty();
651 auto *buf = classProp->IsStatic() ? &staticBuf : (++instanceFieldCnt, instanceBuf);
652 auto name = util::Helpers::LiteralToPropName(pg->Allocator(), classProp->Key());
653 buf->Add(pg->Allocator()->New<StringLiteral>(name));
654
655 FieldType fieldType = FieldType::NONE;
656 const auto *typeAnnotation = classProp->TypeAnnotation();
657 if (typeAnnotation == nullptr) {
658 util::Helpers::ThrowError(ErrorType::GENERIC, pg->Binder()->Program(), prop->Start(),
659 "Field in sendable class must have type annotation");
660 }
661 if (typeAnnotation->IsTSUnionType()) {
662 for (const auto *type : typeAnnotation->AsTSUnionType()->Types()) {
663 AddFieldType(fieldType, type, pg);
664 }
665 } else {
666 AddFieldType(fieldType, typeAnnotation, pg);
667 }
668 buf->Add(pg->Allocator()->New<NumberLiteral>(static_cast<uint8_t>(fieldType)));
669 }
670
671 instanceBuf->Insert(&staticBuf);
672 instanceBuf->Add(pg->Allocator()->New<NumberLiteral>(instanceFieldCnt));
673 return pg->AddLiteralBuffer(instanceBuf);
674 }
675
CompileSendableClass(compiler::PandaGen * pg) const676 void ClassDefinition::CompileSendableClass(compiler::PandaGen *pg) const
677 {
678 compiler::RegScope rs(pg);
679 compiler::VReg classReg = pg->AllocReg();
680
681 compiler::LocalRegScope lrs(pg, scope_);
682
683 compiler::VReg baseReg = CompileHeritageClause(pg);
684 util::StringView ctorId = ctor_->Function()->Scope()->InternalName();
685 util::BitSet compiled(body_.size());
686
687 int32_t fieldTypeBufIdx = CreateFieldTypeBuffer(pg);
688 int32_t bufIdx = CreateClassPublicBuffer(pg, compiled, fieldTypeBufIdx);
689 pg->DefineSendableClass(this, ctorId, bufIdx, baseReg);
690
691 pg->StoreAccumulator(this, classReg);
692
693 InitializeClassName(pg);
694
695 if (NeedStaticInitializer()) {
696 StaticInitialize(pg, classReg);
697 }
698 }
699
CompileGetterOrSetter(compiler::PandaGen * pg,compiler::VReg dest,const MethodDefinition * prop) const700 void ClassDefinition::CompileGetterOrSetter(compiler::PandaGen *pg, compiler::VReg dest,
701 const MethodDefinition *prop) const
702 {
703 compiler::VReg keyReg = prop->Computed() ? prop->KeyReg() : pg->LoadPropertyKey(prop->Key(), false);
704
705 compiler::VReg undef = pg->AllocReg();
706 pg->LoadConst(this, compiler::Constant::JS_UNDEFINED);
707 pg->StoreAccumulator(this, undef);
708
709 compiler::VReg getter = undef;
710 compiler::VReg setter = undef;
711
712 pg->LoadAccumulator(this, dest);
713
714 compiler::VReg accessor = pg->AllocReg();
715 prop->Value()->Compile(pg);
716 pg->StoreAccumulator(prop->Value(), accessor);
717
718 if (prop->Kind() == ir::MethodDefinitionKind::GET) {
719 getter = accessor;
720 } else {
721 setter = accessor;
722 }
723
724 pg->DefineGetterSetterByValue(this, dest, keyReg, getter, setter, prop->Computed());
725 }
726
CalculateClassExpectedPropertyCount()727 void ClassDefinition::CalculateClassExpectedPropertyCount()
728 {
729 // Counting more or less than the actual number does not affect execution.
730 std::unordered_set<util::StringView> propertyNames;
731 auto addPropertyName = [this, &propertyNames](const util::StringView &name) {
732 if (propertyNames.find(name) == propertyNames.end()) {
733 propertyNames.insert(name);
734 IncreasePropertyCount();
735 }
736 };
737
738 for (const auto &stmt : Body()) {
739 if (stmt->IsClassProperty() && !stmt->AsClassProperty()->IsStatic()) {
740 ProcessClassProperty(stmt->AsClassProperty(), addPropertyName);
741 }
742 }
743
744 auto *ctorFunc = ctor_->Function();
745 if (ctorFunc && ctorFunc->Body()) {
746 ProcessConstructorBody(ctorFunc->Body()->AsBlockStatement(), addPropertyName);
747 }
748 }
749
ProcessClassProperty(const ClassProperty * prop,const std::function<void (const util::StringView &)> & addPropertyName)750 void ClassDefinition::ProcessClassProperty(const ClassProperty *prop,
751 const std::function<void(const util::StringView&)>& addPropertyName)
752 {
753 /**
754 * Private fields must be explicitly declared in the class and cannot be directly initialized in the constructor,
755 * so they only need to be accounted for in the class properties.
756 */
757 if (prop->IsPrivate()) {
758 IncreasePropertyCount();
759 } else {
760 ProcessPropertyKey(prop->Key(), addPropertyName);
761 }
762 }
763
ProcessConstructorBody(const BlockStatement * body,const std::function<void (const util::StringView &)> & addPropertyName)764 void ClassDefinition::ProcessConstructorBody(const BlockStatement *body,
765 const std::function<void(const util::StringView&)>& addPropertyName)
766 {
767 for (const auto &stmt : body->Statements()) {
768 if (!stmt->IsExpressionStatement()) {
769 continue;
770 }
771
772 auto *expr = stmt->AsExpressionStatement()->GetExpression();
773 if (!expr->IsAssignmentExpression()) {
774 continue;
775 }
776
777 auto *assignExpr = expr->AsAssignmentExpression();
778 if (!assignExpr->Left()->IsMemberExpression()) {
779 continue;
780 }
781
782 auto *memberExpr = assignExpr->Left()->AsMemberExpression();
783 if (memberExpr->Object()->IsThisExpression()) {
784 ProcessPropertyKey(memberExpr->Property(), addPropertyName);
785 }
786 }
787 }
788
ProcessPropertyKey(const Expression * key,const std::function<void (const util::StringView &)> & addPropertyName)789 void ClassDefinition::ProcessPropertyKey(const Expression* key,
790 const std::function<void(const util::StringView&)>& addPropertyName)
791 {
792 if (key->IsIdentifier()) {
793 addPropertyName(key->AsIdentifier()->Name());
794 } else if (key->IsStringLiteral()) {
795 addPropertyName(key->AsStringLiteral()->Str());
796 } else if (key->IsNumberLiteral()) {
797 addPropertyName(key->AsNumberLiteral()->Str());
798 } else {
799 /**
800 * Currently, other situations of property keys are not counted.
801 * 1. Private properties (PrivateIdentifier)
802 * 2. Template literals (TemplateLiteral)
803 * 3. Binary expressions (BinaryExpression)
804 * 4. Other expressions that can be evaluated to a property key
805 */
806 }
807 }
808 } // namespace panda::es2panda::ir
809