* Copyright (c) 2022-2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "transformer.h"
#include "ir/base/catchClause.h"
#include "ir/base/classStaticBlock.h"
#include "ir/base/decorator.h"
#include "ir/base/scriptFunction.h"
#include "ir/expressions/assignmentExpression.h"
#include "ir/expressions/binaryExpression.h"
#include "ir/expressions/callExpression.h"
#include "ir/expressions/classExpression.h"
#include "ir/expressions/functionExpression.h"
#include "ir/expressions/literals/bigIntLiteral.h"
#include "ir/expressions/literals/numberLiteral.h"
#include "ir/expressions/literals/stringLiteral.h"
#include "ir/expressions/memberExpression.h"
#include "ir/expressions/objectExpression.h"
#include "ir/expressions/sequenceExpression.h"
#include "ir/expressions/templateLiteral.h"
#include "ir/expressions/thisExpression.h"
#include "ir/module/exportDefaultDeclaration.h"
#include "ir/module/exportNamedDeclaration.h"
#include "ir/statements/blockStatement.h"
#include "ir/statements/classDeclaration.h"
#include "ir/statements/doWhileStatement.h"
#include "ir/statements/expressionStatement.h"
#include "ir/statements/forInStatement.h"
#include "ir/statements/forOfStatement.h"
#include "ir/statements/forUpdateStatement.h"
#include "ir/statements/functionDeclaration.h"
#include "ir/statements/returnStatement.h"
#include "ir/statements/switchStatement.h"
#include "ir/statements/variableDeclaration.h"
#include "ir/statements/variableDeclarator.h"
#include "ir/statements/whileStatement.h"
#include "ir/ts/tsConstructorType.h"
#include "ir/ts/tsEnumDeclaration.h"
#include "ir/ts/tsEnumMember.h"
#include "ir/ts/tsFunctionType.h"
#include "ir/ts/tsImportEqualsDeclaration.h"
#include "ir/ts/tsInterfaceDeclaration.h"
#include "ir/ts/tsMethodSignature.h"
#include "ir/ts/tsModuleBlock.h"
#include "ir/ts/tsModuleDeclaration.h"
#include "ir/ts/tsParameterProperty.h"
#include "ir/ts/tsPrivateIdentifier.h"
#include "ir/ts/tsQualifiedName.h"
#include "ir/ts/tsSignatureDeclaration.h"
#include "ir/ts/tsTypeParameterDeclaration.h"
namespace panda::es2panda::parser {
void Transformer::Transform(Program *program)
{
program_ = program;
if (Extension() == ScriptExtension::TS) {
TransformFromTS();
}
}
void Transformer::TransformFromTS()
{
ASSERT(Extension() == ScriptExtension::TS);
VisitTSNodes(program_->Ast());
PushVariablesToNearestStatements(program_->Ast());
}
ir::AstNode *Transformer::VisitTSNodes(ir::AstNode *parent)
{
if (!parent) {
return nullptr;
}
CHECK_NOT_NULL(Binder());
if (UNLIKELY(Binder()->CheckStackOverFlow())) {
ThrowStackOverflow(parent->Start());
return nullptr;
}
parent->UpdateSelf([this](auto *childNode) { return VisitTSNode(childNode); }, Binder());
return parent;
}
void Transformer::AddVariableToNearestStatements(util::StringView name)
{
* Add variable declare like 'var ##var_1;' to nearest statements in namespace function or top level scope
* Record the variable name and scope in tempVarDeclStatements_ and will push the VariableDeclaration nodes
* to statements in PushVariablesToNearestStatements
*/
auto currentScope = Scope();
while (currentScope != nullptr) {
if (currentScope->IsTSModuleScope()) {
auto node = currentScope->Node();
ASSERT(node->IsTSModuleDeclaration());
if (node->AsTSModuleDeclaration()->Body()->IsTSModuleBlock()) {
break;
}
}
if (currentScope->IsFunctionScope()) {
auto node = currentScope->Node();
ASSERT(node->IsScriptFunction());
if (!node->AsScriptFunction()->FunctionBodyIsExpression()) {
break;
}
}
currentScope = currentScope->Parent();
}
tempVarDeclStatements_.insert({name, currentScope});
}
void Transformer::PushVariablesToNearestStatements(ir::BlockStatement *ast)
{
* Push the VariableDeclaration nodes to nearest statements
* For example, transform:
* namespace ns {
* ...
* }
*
* To:
* namespace ns {
* var ##var_1;
* ...
* }
*/
if (tempVarDeclStatements_.empty()) {
return;
}
for (auto it : tempVarDeclStatements_) {
auto *scope = it.second;
if (scope == nullptr) {
auto scopeCtx = binder::LexicalScope<binder::Scope>::Enter(Binder(), ast->Scope());
ast->AddStatementAtPos(0, CreateVariableDeclarationWithIdentify(it.first, VariableParsingFlags::VAR,
nullptr, false));
} else if (scope->IsFunctionScope() || scope->IsTSModuleScope()) {
auto *body = scope->Node()->AsScriptFunction()->Body();
ASSERT(body->IsBlockStatement());
auto scopeCtx = binder::LexicalScope<binder::Scope>::Enter(Binder(), scope);
body->AsBlockStatement()->AddStatementAtPos(0, CreateVariableDeclarationWithIdentify(it.first,
VariableParsingFlags::VAR, nullptr, false));
}
}
}
binder::Scope *Transformer::FindExportVariableInTsModuleScope(util::StringView name) const
{
bool isExport = false;
auto currentScope = Scope();
while (currentScope != nullptr) {
binder::Variable *v = currentScope->FindLocal(name, binder::ResolveBindingOptions::BINDINGS);
bool isTSModuleScope = currentScope->IsTSModuleScope();
if (v != nullptr) {
if (!v->Declaration()->IsVarDecl() && !v->Declaration()->IsLetDecl() && !v->Declaration()->IsConstDecl()) {
break;
}
if (isTSModuleScope && v->Declaration()->IsExportDeclInTsModule()) {
isExport = true;
}
break;
}
if (currentScope->InLocalTSBindings(name) &&
!currentScope->FindLocalTSVariable<binder::TSBindingType::IMPORT_EQUALS>(name)) {
break;
}
if (isTSModuleScope && currentScope->AsTSModuleScope()->InExportBindings(name)) {
isExport = true;
break;
}
currentScope = currentScope->Parent();
}
if (!isExport) {
return nullptr;
}
return currentScope;
}
ir::UpdateNodes Transformer::VisitTSNode(ir::AstNode *childNode)
{
ASSERT(childNode != nullptr);
CHECK_NOT_NULL(Binder());
if (UNLIKELY(Binder()->CheckStackOverFlow())) {
ThrowStackOverflow(childNode->Start());
return nullptr;
}
switch (childNode->Type()) {
case ir::AstNodeType::IDENTIFIER: {
auto *ident = childNode->AsIdentifier();
if (!ident->IsReference() || (!IsTsModule() && !IsTsEnum() && !InClass())) {
return VisitTSNodes(childNode);
}
auto name = ident->Name();
if (InClass()) {
auto *classDefinition = GetClassReference(name);
auto aliasName = GetClassAliasName(name, classDefinition);
if (classDefinition != nullptr && aliasName != name) {
ident->SetName(aliasName);
}
}
if (IsTsEnum()) {
auto scope = FindEnumMemberScope(name);
if (scope) {
return CreateMemberExpressionFromIdentifier(scope, ident);
}
}
if (IsTsModule()) {
auto scope = FindExportVariableInTsModuleScope(name);
if (scope) {
return CreateMemberExpressionFromIdentifier(scope, ident);
}
}
return VisitTSNodes(childNode);
}
case ir::AstNodeType::TS_MODULE_DECLARATION: {
auto *node = childNode->AsTSModuleDeclaration();
if (node->Declare() || !node->IsInstantiated()) {
return childNode;
}
auto res = VisitTsModuleDeclaration(node);
SetOriginalNode(res, childNode);
return res;
}
case ir::AstNodeType::TS_ENUM_DECLARATION: {
auto *node = childNode->AsTSEnumDeclaration();
if (node->IsDeclare()) {
return childNode;
}
auto res = VisitTsEnumDeclaration(node);
SetOriginalNode(res, childNode);
return res;
}
case ir::AstNodeType::EXPORT_NAMED_DECLARATION: {
auto *node = childNode->AsExportNamedDeclaration();
auto *decl = node->Decl();
if (!decl) {
return VisitTSNodes(childNode);
}
if (decl->IsTSModuleDeclaration()) {
auto *tsModuleDeclaration = decl->AsTSModuleDeclaration();
if (tsModuleDeclaration->Declare() || !tsModuleDeclaration->IsInstantiated()) {
return childNode;
}
auto res = VisitTsModuleDeclaration(tsModuleDeclaration, true);
SetOriginalNode(res, childNode);
return res;
}
if (decl->IsTSEnumDeclaration()) {
if (decl->AsTSEnumDeclaration()->IsDeclare() ||
(decl->AsTSEnumDeclaration()->IsConst() && program_->IsShared())) {
return childNode;
}
auto res = VisitTsEnumDeclaration(decl->AsTSEnumDeclaration(), true);
SetOriginalNode(res, childNode);
return res;
}
if (IsTsModule()) {
auto res = VisitExportNamedVariable(decl);
SetOriginalNode(res, node);
return res;
}
if (decl->IsClassDeclaration()) {
return VisitExportClassDeclaration<ir::ExportNamedDeclaration>(node);
}
return VisitTSNodes(node);
}
case ir::AstNodeType::EXPORT_DEFAULT_DECLARATION: {
auto *node = childNode->AsExportDefaultDeclaration();
auto *decl = node->Decl();
ASSERT(decl != nullptr);
if (decl->IsClassDeclaration()) {
return VisitExportClassDeclaration<ir::ExportDefaultDeclaration>(node);
}
if (decl->IsIdentifier() && !IsValueReference(decl->AsIdentifier())) {
RemoveDefaultLocalExportEntry();
return nullptr;
}
return VisitTSNodes(childNode);
}
case ir::AstNodeType::TS_IMPORT_EQUALS_DECLARATION: {
auto *node = childNode->AsTSImportEqualsDeclaration();
auto *express = node->ModuleReference();
if (express->IsTSExternalModuleReference()) {
return VisitTSNodes(childNode);
}
auto *res = VisitTsImportEqualsDeclaration(node);
SetOriginalNode(res, childNode);
return res;
}
case ir::AstNodeType::CLASS_DECLARATION: {
auto *node = childNode->AsClassDeclaration();
if (node->Definition()->Declare() || node->IsAnnotationDecl()) {
return node;
}
DuringClass duringClass(&classList_, node->Definition()->GetName(),
CreateClassAliasName(node), node->Definition());
auto *classNode = VisitTSNodes(node);
CHECK_NOT_NULL(classNode);
node = classNode->AsClassDeclaration();
auto res = VisitClassDeclaration(node);
SetOriginalNode(res, childNode);
ResetParentScope(res, Scope());
return res;
}
case ir::AstNodeType::CLASS_EXPRESSION: {
auto *node = childNode->AsClassExpression();
DuringClass duringClass(&classList_, node->Definition()->GetName(),
node->Definition()->GetName(), node->Definition());
auto *classNode = VisitTSNodes(node);
CHECK_NOT_NULL(classNode);
node = classNode->AsClassExpression();
auto res = VisitClassExpression(node);
SetOriginalNode(res, childNode);
return res;
}
case ir::AstNodeType::CLASS_DEFINITION: {
auto *node = childNode->AsClassDefinition();
VisitPrivateElement(node);
VisitComputedProperty(node);
VisitAutoAccessorProperty(node);
auto res = VisitTSNodes(childNode);
SetOriginalNode(res, childNode);
return res;
}
case ir::AstNodeType::TS_PRIVATE_IDENTIFIER: {
auto id = childNode->AsTSPrivateIdentifier()->Key()->AsIdentifier();
auto name = FindPrivateElementBindName(id->Name());
auto res = CreateReferenceIdentifier(name);
SetOriginalNode(res, childNode);
return res;
}
default: {
return VisitTSNodes(childNode);
}
}
}
template <typename T>
ir::UpdateNodes Transformer::VisitExportClassDeclaration(T *node)
{
auto *decl = node->Decl();
auto newDecl = VisitTSNode(decl);
if (std::holds_alternative<ir::AstNode *>(newDecl)) {
auto statement = std::get<ir::AstNode *>(newDecl);
ASSERT(statement->IsClassDeclaration());
node->SetDecl(statement->AsClassDeclaration());
return node;
} else {
auto statements = std::get<std::vector<ir::AstNode *>>(newDecl);
std::vector<ir::AstNode *> res;
auto firstStatement = statements.front();
statements.erase(statements.begin());
if (firstStatement->IsVariableDeclaration()) {
node->SetDecl(firstStatement->AsVariableDeclaration());
} else {
ASSERT(firstStatement->IsClassDeclaration());
node->SetDecl(firstStatement->AsClassDeclaration());
}
res.push_back(node);
res.insert(res.end(), statements.begin(), statements.end());
return res;
}
}
util::StringView Transformer::CreateNewVariable(bool needAddToStatements)
{
util::StringView name = CreateNewVariableName();
if (needAddToStatements) {
AddVariableToNearestStatements(name);
}
return name;
}
util::StringView Transformer::CreateUniqueName(const std::string &head, size_t *index) const
{
util::StringView name;
size_t idx = 0;
if (index != nullptr) {
idx = *index;
}
do {
idx++;
std::stringstream ss;
ss << head << std::to_string(idx);
auto s = ss.str();
if (!Binder()->HasVariableName(util::StringView(s))) {
name = util::UString(s, Allocator()).View();
break;
}
} while (true);
if (index != nullptr) {
*index = idx;
}
Binder()->AddDeclarationName(name);
return name;
}
util::StringView Transformer::CreateNewVariableName() const
{
auto name = CreateUniqueName(std::string(NEW_VAR_PREFIX) + std::string(NEW_VAR_HEAD));
return name;
}
ir::UpdateNodes Transformer::VisitClassExpression(ir::ClassExpression *node)
{
* Transform:
* var c = class C {
* static a = 1
* }
*
* To:
* var ##var_1;
* var c = (##var_1 = class C {},
* ##var_1.a = 1,
* ##var_1)
*/
auto instanceComputedProperty = VisitInstanceProperty(node->Definition());
VisitTSParameterProperty(node->Definition());
auto varName = CreateNewVariable(false);
auto staticProperty = VisitStaticProperty(node->Definition(), varName);
if (instanceComputedProperty.empty() && staticProperty.empty()) {
return node;
}
AddVariableToNearestStatements(varName);
auto assignment = AllocNode<ir::AssignmentExpression>(CreateReferenceIdentifier(varName),
node->AsExpression(), lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
ArenaVector<ir::Expression *> sequence(Allocator()->Adapter());
sequence.push_back(assignment);
for (auto *it : instanceComputedProperty) {
sequence.push_back(it->GetExpression());
}
for (auto *it : staticProperty) {
sequence.push_back(it->GetExpression());
}
sequence.push_back(CreateReferenceIdentifier(varName));
return AllocNode<ir::SequenceExpression>(std::move(sequence));
}
void Transformer::VisitComputedProperty(ir::ClassDefinition *node)
{
* Only create variable for the computed members with decorators or static class property
* The new value will be used in the decorators or static property initialize
* Transform:
* class C {
* @f
* [a](){}
* static [b] = 1
* [c] = 1
* }
*
* To:
* var ##var_1;
* var ##var_2;
* var ##var_3;
* class C {
* @f
* [##var_1 = a](){}
* static [##var_2 = b] = 1
* [##var_3 = c] = 1
* }
*/
for (auto *it : node->Body()) {
if (it->IsClassProperty()) {
auto *classProperty = it->AsClassProperty();
if (!classProperty->IsComputed()) {
continue;
}
if (classProperty->IsAutoAccessor()) {
continue;
}
auto *key = classProperty->Key();
auto name = CreateNewVariable();
auto *newKey = AllocNode<ir::AssignmentExpression>(CreateReferenceIdentifier(name),
key, lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
classProperty->SetKey(newKey);
AddComputedPropertyBinding(it, name);
} else if (it->IsMethodDefinition()) {
auto *methodDefinition = it->AsMethodDefinition();
if (!methodDefinition->Computed() ||
(!methodDefinition->HasDecorators() && !methodDefinition->HasParamDecorators())) {
continue;
}
auto *key = methodDefinition->Key();
auto name = CreateNewVariable();
auto *newKey = AllocNode<ir::AssignmentExpression>(CreateReferenceIdentifier(name),
key, lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
methodDefinition->SetKey(newKey);
AddComputedPropertyBinding(it, name);
}
}
}
const ir::ClassDefinition *Transformer::GetClassReference(util::StringView name) const
{
auto *scope = Scope();
while (scope != nullptr) {
auto *v = scope->FindLocal(name, binder::ResolveBindingOptions::BINDINGS);
if (v != nullptr) {
if (v->Declaration() != nullptr && v->Declaration()->Node() != nullptr &&
v->Declaration()->Node()->IsClassDefinition()) {
ASSERT(v->Declaration()->Node()->AsClassDefinition()->GetName() == name);
return v->Declaration()->Node()->AsClassDefinition();
} else {
return nullptr;
}
}
scope = scope->Parent();
}
return nullptr;
}
util::StringView Transformer::CreateClassAliasName(ir::ClassDeclaration *node)
{
if (node->HasDecorators()) {
return CreateUniqueName(std::string(NEW_VAR_PREFIX) + std::string(NEW_VAR_HEAD));
}
return node->Definition()->GetName();
}
void Transformer::VisitPrivateElement(ir::ClassDefinition *node)
{
* Create an unique variable name for private property member in class
* Transform:
* class C {
* #a = 1
* }
*
* To:
* class C {
* ##${RecordName}#C#a#1 = 1
* }
*/
for (auto *it : node->Body()) {
if (!it->IsClassProperty() && !it->IsMethodDefinition()) {
continue;
}
auto *key = it->IsClassProperty() ? it->AsClassProperty()->Key() : it->AsMethodDefinition()->Key();
if (!key->IsTSPrivateIdentifier()) {
continue;
}
auto name = key->AsTSPrivateIdentifier()->Key()->AsIdentifier()->Name();
auto bindName = CreatePrivateElementBindName(name);
AddPrivateElementBinding(name, bindName);
}
}
util::StringView Transformer::FindPrivateElementBindName(util::StringView name)
{
for (int i = static_cast<int>(classList_.size() - 1); i >= 0; i--) {
auto res = classList_[i].bindNameMap->find(name);
if (res != classList_[i].bindNameMap->end()) {
return res->second;
}
}
UNREACHABLE();
}
util::StringView Transformer::CreatePrivateElementBindName(util::StringView name)
{
std::stringstream head;
head << NEW_VAR_PREFIX << std::string(RecordName());
for (auto it : classList_) {
head << PRIVATE_PROPERTY_SIGN << std::string(it.name);
}
head << PRIVATE_PROPERTY_SIGN << std::string(name) << PRIVATE_PROPERTY_SIGN;
size_t index = GetCurrentClassInfoPropertyIndex();
auto uniqueName = CreateUniqueName(head.str(), &index);
SetCurrentClassInfoPropertyIndex(index);
return uniqueName;
}
size_t Transformer::GetInsertPosForConstructor(ir::ClassDefinition *node)
{
size_t insertPos = 0;
ir::BlockStatement *blockStat = node->Ctor()->Function()->Body()->AsBlockStatement();
auto iter = blockStat->Statements().begin();
for (; iter != blockStat->Statements().end();) {
if ((*iter)->IsExpressionStatement() &&
(*iter)->AsExpressionStatement()->GetExpression()->IsStringLiteral()) {
iter++;
insertPos++;
} else {
break;
}
}
if (node->Super() == nullptr || node->Super()->IsNullLiteral()) {
return insertPos;
}
for (; iter != blockStat->Statements().end(); iter++) {
insertPos++;
bool hasSuperCall = false;
FindSuperCallInCtorChildNode(*iter, &hasSuperCall);
if (hasSuperCall) {
break;
}
}
return insertPos;
}
void Transformer::FindSuperCall(const ir::AstNode *parent, bool *hasSuperCall)
{
parent->Iterate([this, hasSuperCall](auto *childNode) {
FindSuperCallInCtorChildNode(childNode, hasSuperCall);
});
}
void Transformer::FindSuperCallInCtorChildNode(const ir::AstNode *childNode, bool *hasSuperCall)
{
if (*hasSuperCall) {
return;
}
switch (childNode->Type()) {
case ir::AstNodeType::CALL_EXPRESSION: {
if (childNode->AsCallExpression()->Callee()->IsSuperExpression()) {
*hasSuperCall = true;
return;
}
break;
}
case ir::AstNodeType::CLASS_DEFINITION:
case ir::AstNodeType::FUNCTION_DECLARATION:
case ir::AstNodeType::FUNCTION_EXPRESSION:
case ir::AstNodeType::ARROW_FUNCTION_EXPRESSION: {
break;
}
default: {
FindSuperCall(childNode, hasSuperCall);
break;
}
}
}
std::vector<ir::ExpressionStatement *> Transformer::VisitInstanceProperty(ir::ClassDefinition *node)
{
* Move class InstanceProperty to constructor.
* Transform:
* class C {
* "prop" = 1;
* }
*
* To:
* class C {
* constructor () {
* this["prop"] = 1;
* }
* }
*/
std::vector<ir::ClassProperty *> addToCtor;
std::vector<ir::ExpressionStatement *> computedProps;
for (auto *it : node->Body()) {
if (it->IsClassProperty() && !it->AsClassProperty()->IsStatic() &&
!it->AsClassProperty()->Key()->IsPrivateIdentifier() && it->AsClassProperty()->Value() != nullptr) {
addToCtor.push_back(it->AsClassProperty());
}
}
if (addToCtor.empty()) {
return {};
}
auto ctorScopeCtx = binder::LexicalScope<binder::FunctionScope>::Enter(Binder(), node->Ctor()->Function()->Scope());
ir::BlockStatement *blockStat = node->Ctor()->Function()->Body()->AsBlockStatement();
size_t insertPos = GetInsertPosForConstructor(node);
for (auto *it : addToCtor) {
if (it->IsComputed()) {
computedProps.push_back(AllocNode<ir::ExpressionStatement>(it->Key()));
}
ir::MemberExpression *left = nullptr;
auto *member = GetClassMemberName(it->Key(), it->IsComputed(), it, false);
auto thisExpression = AllocNode<ir::ThisExpression>();
thisExpression->SetRange(member->Range());
if (member->IsIdentifier() && !it->IsComputed()) {
left = AllocNode<ir::MemberExpression>(thisExpression, member,
ir::MemberExpression::MemberExpressionKind::PROPERTY_ACCESS,
false, false);
} else {
left = AllocNode<ir::MemberExpression>(thisExpression, member,
ir::MemberExpression::MemberExpressionKind::ELEMENT_ACCESS,
true, false);
}
left->SetRange(member->Range());
auto assignment = AllocNode<ir::AssignmentExpression>(left, it->Value(),
lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
ResetParentScopeForAstNode(assignment, Scope());
blockStat->AddStatementAtPos(insertPos, AllocNode<ir::ExpressionStatement>(assignment));
insertPos++;
}
return computedProps;
}
void Transformer::VisitTSParameterProperty(ir::ClassDefinition *node)
{
* Add class property for the parameter property declaration in constructor
* Transform:
* class C {
* constructor(public a = 1) {}
* }
*
* To:
* class C {
* constructor(public a = 1) {
* this.a = a;
* }
* }
*/
auto *func = node->Ctor()->Function();
auto *body = func->Body();
if (body == nullptr) {
return;
}
auto blockStatement = body->AsBlockStatement();
size_t insertPos = GetInsertPosForConstructor(node);
for (auto *it : func->Params()) {
if (!it->IsTSParameterProperty()) {
continue;
}
auto *parameter = it->AsTSParameterProperty()->Parameter();
util::StringView name;
if (parameter->IsIdentifier()) {
name = parameter->AsIdentifier()->Name();
} else {
ASSERT(parameter->IsAssignmentPattern());
auto *left = parameter->AsAssignmentPattern()->Left();
ASSERT(left->IsIdentifier());
name = left->AsIdentifier()->Name();
}
auto left = AllocNode<ir::MemberExpression>(AllocNode<ir::ThisExpression>(),
AllocNode<ir::Identifier>(name),
ir::MemberExpression::MemberExpressionKind::PROPERTY_ACCESS, false, false);
auto right = CreateReferenceIdentifier(name);
auto assignment = AllocNode<ir::AssignmentExpression>(left, right,
lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
blockStatement->AddStatementAtPos(insertPos, AllocNode<ir::ExpressionStatement>(assignment));
insertPos++;
}
}
void Transformer::VisitAutoAccessorProperty(ir::ClassDefinition *node)
{
ASSERT(node != nullptr);
std::vector<ir::ClassProperty *> autoAccessorPropertyList;
for (auto *it : node->Body()) {
if (!it->IsClassProperty()) {
continue;
}
auto* prop = it->AsClassProperty();
if (prop->IsAutoAccessor()) {
autoAccessorPropertyList.push_back(prop);
}
}
for (auto *prop : autoAccessorPropertyList) {
ProcessAutoAccessorProperty(prop, node);
}
}
ir::Expression *Transformer::CopyClassKeyExpression(ir::Expression *orginalExpr)
{
ir::Expression *newExpr = nullptr;
switch (orginalExpr->Type()) {
case ir::AstNodeType::IDENTIFIER: {
ir::Identifier *ident = orginalExpr->AsIdentifier();
newExpr = AllocNode<ir::Identifier>(ident->Name());
break;
}
case ir::AstNodeType::PRIVATE_IDENTIFIER: {
ir::PrivateIdentifier *ident = orginalExpr->AsPrivateIdentifier();
newExpr = AllocNode<ir::PrivateIdentifier>(ident->Name());
break;
}
case ir::AstNodeType::TS_PRIVATE_IDENTIFIER: {
ir::Identifier *ident = orginalExpr->AsTSPrivateIdentifier()->Key()->AsIdentifier();
newExpr = AllocNode<ir::Identifier>(ident->Name());
break;
}
case ir::AstNodeType::STRING_LITERAL: {
ir::StringLiteral *stringLiteral = orginalExpr->AsStringLiteral();
newExpr = AllocNode<ir::StringLiteral>(stringLiteral->Str());
break;
}
case ir::AstNodeType::BIGINT_LITERAL: {
ir::BigIntLiteral *bigIntLiteral = orginalExpr->AsBigIntLiteral();
newExpr = AllocNode<ir::BigIntLiteral>(bigIntLiteral->Str());
break;
}
case ir::AstNodeType::NUMBER_LITERAL: {
auto *numberLiteral = orginalExpr->AsNumberLiteral();
newExpr = AllocNode<ir::NumberLiteral>(numberLiteral->Number(), numberLiteral->Str());
break;
}
default: {
UNREACHABLE();
}
}
newExpr->SetRange(orginalExpr->Range());
return newExpr;
}
void Transformer::ProcessAutoAccessorProperty(ir::ClassProperty *node, ir::ClassDefinition *classDefinition)
{
* Transform for auto accessor
* class A {
* accessor name:string;
* }
*
* To:
*
* class A {
* #__name:string;
* get name() {
* return this.#__name;
* }
* set name(value: string) {
* this.#__name == value;
* }
* }
*
* For computed auto accessor property:
* class A {
* accessor [name]:string;
* }
*
* To:
* var #var_1; // unique name
* class A {
* #__name:string;
* get [#var_1 = name]() {
* return this.#__name;
* }
* set [#var_1](value: string) {
* this.#__name == value;
* }
* }
*/
ASSERT(node->Key() != nullptr);
auto *originKeyNode = node->Key();
auto transformedName = CreatePrivateElementBindName(AUTO_ACCESSOR_STORAGE_NAME);
auto *internalIdentifier = AllocNode<ir::Identifier>(transformedName);
internalIdentifier->SetRange(originKeyNode->Range());
internalIdentifier->SetParent(originKeyNode->Parent());
node->SetKey(internalIdentifier);
util::StringView backupVarName;
bool computed = node->IsComputed();
if (computed) {
backupVarName = CreateNewVariable(true);
node->SetComputed(false);
}
ir::ModifierFlags modifierMask = ir::ModifierFlags::ACCESS | ir::ModifierFlags::STATIC;
ir::ModifierFlags modifiers = static_cast<ir::ModifierFlags>(node->Modifiers() & modifierMask);
MethodInfo getMethodInfo = {CopyClassKeyExpression(originKeyNode), backupVarName, ir::MethodDefinitionKind::GET,
modifiers, computed};
AddGeneratedSetOrGetMethodToClass(classDefinition, node, getMethodInfo);
MethodInfo setMethodInfo = {CopyClassKeyExpression(originKeyNode), backupVarName, ir::MethodDefinitionKind::SET,
modifiers, computed};
AddGeneratedSetOrGetMethodToClass(classDefinition, node, setMethodInfo);
}
ir::MethodDefinition* Transformer::AddMethodToClass(ir::ClassDefinition *classDefinition,
const MethodInfo& methodInfo,
ArenaVector<ir::Expression *> ¶ms,
ArenaVector<ir::Statement *> &statements)
{
CHECK_NOT_NULL(classDefinition);
ASSERT((methodInfo.kind == ir::MethodDefinitionKind::GET) || (methodInfo.kind == ir::MethodDefinitionKind::SET));
auto *paramScope = Binder()->Allocator()->New<binder::FunctionParamScope>(Allocator(), Binder()->GetScope());
CHECK_NOT_NULL(paramScope);
for (auto ¶m : params) {
paramScope->AddParamDecl(Allocator(), param);
}
auto *scope = Binder()->Allocator()->New<binder::FunctionScope>(Allocator(), paramScope);
CHECK_NOT_NULL(scope);
paramScope->BindFunctionScope(scope);
auto *body = AllocNode<ir::BlockStatement>(scope, std::move(statements));
auto *func = AllocNode<ir::ScriptFunction>(scope, std::move(params), nullptr, body, nullptr,
ir::ScriptFunctionFlags::METHOD, false, false);
scope->BindNode(func);
scope->BindParamScope(paramScope);
paramScope->BindNode(func);
auto *funcExpr = AllocNode<ir::FunctionExpression>(func);
ir::Expression *keyNode = nullptr;
if (!methodInfo.isComputed) {
keyNode = methodInfo.key;
} else {
if (methodInfo.kind == ir::MethodDefinitionKind::GET) {
auto *backupNode = CreateReferenceIdentifier(methodInfo.backupName);
keyNode = AllocNode<ir::AssignmentExpression>(backupNode, methodInfo.key,
lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
} else {
auto *backupNode = CreateReferenceIdentifier(methodInfo.backupName);
keyNode = backupNode;
}
}
ArenaVector<ir::Decorator *> decorators(Allocator()->Adapter());
ArenaVector<ir::Annotation *> annotations(Allocator()->Adapter());
ArenaVector<ir::ParamDecorators> paramDecorators(Allocator()->Adapter());
auto *method = AllocNode<ir::MethodDefinition>(methodInfo.kind, keyNode, funcExpr,
methodInfo.modifiers, Allocator(), std::move(decorators),
std::move(annotations), std::move(paramDecorators),
methodInfo.isComputed);
classDefinition->AddToBody(method);
if (methodInfo.isComputed) {
AddComputedPropertyBinding(method, methodInfo.backupName);
}
return method;
}
ir::MethodDefinition* Transformer::AddGeneratedMethodToClass(ir::ClassDefinition *classDefinition,
const MethodInfo &methodInfo,
util::StringView propName)
{
ASSERT(classDefinition != nullptr);
ArenaVector<ir::Expression *> params(Allocator()->Adapter());
ArenaVector<ir::Statement *> statements(Allocator()->Adapter());
if (methodInfo.kind == ir::MethodDefinitionKind::GET) {
* Add `return this.prop` to function body.
*/
auto *identNode = AllocNode<ir::Identifier>(propName);
auto *returnExpr = AllocNode<ir::MemberExpression>(AllocNode<ir::ThisExpression>(), identNode,
ir::MemberExpression::MemberExpressionKind::PROPERTY_ACCESS, false, false);
ir::ReturnStatement *returnStatement = AllocNode<ir::ReturnStatement>(returnExpr);
statements.push_back(returnStatement);
} else if (methodInfo.kind == ir::MethodDefinitionKind::SET) {
* 1. Add `value` to params
* 2. Add `this.prop = value` to function body
*/
util::StringView paramName = util::UString("value", Allocator()).View();
auto *identNode = AllocNode<ir::Identifier>(paramName);
params.push_back(identNode);
auto *identNodeProp = AllocNode<ir::Identifier>(propName);
auto *propAccessExpr = AllocNode<ir::MemberExpression>(AllocNode<ir::ThisExpression>(), identNodeProp,
ir::MemberExpression::MemberExpressionKind::PROPERTY_ACCESS, false, false);
auto *identNodeRight = AllocNode<ir::Identifier>(paramName);
auto *setExpr = AllocNode<ir::AssignmentExpression>(propAccessExpr, identNodeRight,
lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
auto *exprStatementNode = AllocNode<ir::ExpressionStatement>(setExpr);
statements.push_back(exprStatementNode);
} else {
UNREACHABLE();
}
auto *method = AddMethodToClass(classDefinition, methodInfo, params, statements);
return method;
}
void Transformer::AddGeneratedSetOrGetMethodToClass(ir::ClassDefinition *classDefinition,
ir::ClassProperty *propertyNode,
const MethodInfo &methodInfo)
{
ASSERT(classDefinition != nullptr);
ASSERT(propertyNode != nullptr);
auto propName = propertyNode->Key()->AsIdentifier()->Name();
auto *method = AddGeneratedMethodToClass(classDefinition, methodInfo, propName);
method->SetOriginal(propertyNode);
method->SetRange(propertyNode->Range());
}
std::vector<ir::ExpressionStatement *> Transformer::VisitStaticProperty(ir::ClassDefinition *node,
util::StringView name)
{
* Create statement for static class property
* If it's a conputed property, we should initialize it's new variable first.
* Transform:
* var ##var_1;
* class C {
* static a = 1
* static [##var_1 = s] = 1
* }
*
* To:
* var ##var_1;
* class C {
* }
* C.a = 1;
* ##var_1 = s;
* C[##var_1] = 1;
*
* TODO(xucheng): should support static private property
*/
if (program_->TargetApiVersion() > 10 && !(node->IsClassDecoratorPresent())) {
return {};
}
std::vector<ir::ExpressionStatement *> res;
auto classDefinitionBody = node->Body();
for (auto *it : classDefinitionBody) {
if (!it->IsClassProperty()) {
continue;
}
auto *classProperty = it->AsClassProperty();
if (!classProperty->IsStatic()) {
continue;
}
if (classProperty->IsPrivate()) {
continue;
}
if (classProperty->IsComputed()) {
res.push_back(AllocNode<ir::ExpressionStatement>(classProperty->Key()));
}
auto right = classProperty->Value();
if (right == nullptr) {
continue;
}
ir::MemberExpression *left = nullptr;
auto *member = GetClassMemberName(classProperty->Key(), classProperty->IsComputed(), classProperty, false);
if (member->IsIdentifier() && !classProperty->IsComputed()) {
left = AllocNode<ir::MemberExpression>(CreateReferenceIdentifier(name), member,
ir::MemberExpression::MemberExpressionKind::PROPERTY_ACCESS,
false, false);
} else {
left = AllocNode<ir::MemberExpression>(CreateReferenceIdentifier(name), member,
ir::MemberExpression::MemberExpressionKind::ELEMENT_ACCESS,
true, false);
}
auto assignment = AllocNode<ir::AssignmentExpression>(left, right, lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
if (program_->TargetApiVersion() > 10) {
classProperty->RemoveValue();
}
res.push_back(AllocNode<ir::ExpressionStatement>(assignment));
}
return res;
}
ir::UpdateNodes Transformer::VisitClassDeclaration(ir::ClassDeclaration *node)
{
auto name = node->Definition()->GetName();
std::vector<ir::AstNode *> res;
bool hasClassDecorators = node->HasDecorators();
if (hasClassDecorators) {
auto aliasName = GetClassAliasName();
res.push_back(CreateVariableDeclarationWithIdentify(aliasName, VariableParsingFlags::VAR,
node->Definition()->Ident(), false));
auto *clsExpression = AllocNode<ir::ClassExpression>(node->Definition());
auto *assignExpr = AllocNode<ir::AssignmentExpression>(
CreateReferenceIdentifier(aliasName, node->Definition()->Ident()), clsExpression,
lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
res.push_back(CreateVariableDeclarationWithIdentify(name, VariableParsingFlags::LET, node, false,
assignExpr, false));
} else {
res.push_back(node);
}
auto instanceComputedProperty = VisitInstanceProperty(node->Definition());
if (!instanceComputedProperty.empty()) {
res.insert(res.end(), instanceComputedProperty.begin(), instanceComputedProperty.end());
}
VisitTSParameterProperty(node->Definition());
auto staticProperty = VisitStaticProperty(node->Definition(), name);
if (!staticProperty.empty()) {
res.insert(res.end(), staticProperty.begin(), staticProperty.end());
}
auto classDefinitionBody = node->Definition()->Body();
bool hasPrivateIdentifier = HasPrivateIdentifierInDecorators(node->Definition());
ir::ClassStaticBlock *staticBlock = CreateClassStaticBlock(node, hasPrivateIdentifier);
std::vector<ir::AstNode *> staticMemberDecorators;
for (auto *it : classDefinitionBody) {
auto scopeCtx = binder::LexicalScope<binder::Scope>::Enter(Binder(),
hasPrivateIdentifier ? staticBlock->GetBody()->Scope() : Scope());
if (it->IsMethodDefinition()) {
auto *definition = it->AsMethodDefinition();
bool isStatic = definition->IsStatic();
auto variableDeclarations = CreateVariableDeclarationForDecorators(definition);
if (isStatic) {
staticMemberDecorators.insert(staticMemberDecorators.end(),
variableDeclarations.begin(), variableDeclarations.end());
} else if (!hasPrivateIdentifier) {
res.insert(res.end(), variableDeclarations.begin(), variableDeclarations.end());
}
auto paramDecorators = CreateParamDecorators(name, definition, variableDeclarations, false, isStatic);
if (isStatic) {
staticMemberDecorators.insert(staticMemberDecorators.end(),
paramDecorators.begin(), paramDecorators.end());
} else if (!hasPrivateIdentifier) {
res.insert(res.end(), paramDecorators.begin(), paramDecorators.end());
}
if (!definition->HasDecorators()) {
continue;
}
auto methodDecorators = CreateMethodDecorators(name, definition, variableDeclarations, isStatic);
if (isStatic) {
staticMemberDecorators.insert(staticMemberDecorators.end(),
methodDecorators.begin(), methodDecorators.end());
} else if (!hasPrivateIdentifier) {
res.insert(res.end(), methodDecorators.begin(), methodDecorators.end());
}
if (hasPrivateIdentifier && !isStatic) {
for (auto *it : variableDeclarations) {
staticBlock->GetBody()->AddStatementAtPos(
staticBlock->GetBody()->Statements().size(), it->AsStatement());
}
for (auto *it : paramDecorators) {
staticBlock->GetBody()->AddStatementAtPos(
staticBlock->GetBody()->Statements().size(), it->AsStatement());
}
for (auto *it : methodDecorators) {
staticBlock->GetBody()->AddStatementAtPos(
staticBlock->GetBody()->Statements().size(), it->AsStatement());
}
}
} else if (it->IsClassProperty()) {
auto *classProperty = it->AsClassProperty();
bool isStatic = classProperty->IsStatic();
if (!classProperty->HasDecorators()) {
continue;
}
if (classProperty->IsComputed() && !isStatic && classProperty->Value() == nullptr) {
res.push_back(AllocNode<ir::ExpressionStatement>(classProperty->Key()));
}
auto variableDeclarations = CreateVariableDeclarationForDecorators(classProperty);
if (isStatic) {
staticMemberDecorators.insert(staticMemberDecorators.end(),
variableDeclarations.begin(), variableDeclarations.end());
} else if (!hasPrivateIdentifier) {
res.insert(res.end(), variableDeclarations.begin(), variableDeclarations.end());
}
auto propertyDecorators = CreatePropertyDecorators(name, classProperty, variableDeclarations, isStatic);
if (isStatic) {
staticMemberDecorators.insert(staticMemberDecorators.end(),
propertyDecorators.begin(), propertyDecorators.end());
} else if (!hasPrivateIdentifier) {
res.insert(res.end(), propertyDecorators.begin(), propertyDecorators.end());
}
if (hasPrivateIdentifier && !isStatic) {
for (auto *it : variableDeclarations) {
staticBlock->GetBody()->AddStatementAtPos(
staticBlock->GetBody()->Statements().size(), it->AsStatement());
}
for (auto *it : propertyDecorators) {
staticBlock->GetBody()->AddStatementAtPos(
staticBlock->GetBody()->Statements().size(), it->AsStatement());
}
}
}
}
if (!staticMemberDecorators.empty()) {
if (hasPrivateIdentifier) {
for (auto *it : staticMemberDecorators) {
staticBlock->GetBody()->AddStatementAtPos(
staticBlock->GetBody()->Statements().size(), it->AsStatement());
}
} else {
res.insert(res.end(), staticMemberDecorators.begin(), staticMemberDecorators.end());
}
}
auto variableDeclarationsForCtorOrClass = CreateVariableDeclarationForDecorators(node);
res.insert(res.end(), variableDeclarationsForCtorOrClass.begin(), variableDeclarationsForCtorOrClass.end());
auto *ctor = node->Definition()->Ctor();
auto ctorParamDecorators = CreateParamDecorators(name, ctor, variableDeclarationsForCtorOrClass, true, false);
res.insert(res.end(), ctorParamDecorators.begin(), ctorParamDecorators.end());
if (hasClassDecorators) {
auto classDecorators = CreateClassDecorators(node, variableDeclarationsForCtorOrClass);
res.insert(res.end(), classDecorators.begin(), classDecorators.end());
}
if (res.size() == 1) {
return res.front();
}
return res;
}
ir::ClassStaticBlock *Transformer::CreateClassStaticBlock(ir::ClassDeclaration *node, bool hasPrivateIdentifer)
{
if (!hasPrivateIdentifer) {
return nullptr;
}
ir::MethodDefinition *staticInitializer = node->Definition()->StaticInitializer();
auto scopeCtx = binder::LexicalScope<binder::FunctionScope>::Enter(Binder(),
staticInitializer->Function()->Scope());
auto lexScope = binder::LexicalScope<binder::StaticBlockScope>(Binder());
ir::BlockStatement *blockStatement = nullptr;
{
auto localCtx = binder::LexicalScope<binder::LocalScope>(Binder());
ArenaVector<ir::Statement *> statements(Allocator()->Adapter());
blockStatement = AllocNode<ir::BlockStatement>(localCtx.GetScope(), std::move(statements));
localCtx.GetScope()->BindNode(blockStatement);
}
ir::ClassStaticBlock *staticBlock = AllocNode<ir::ClassStaticBlock>(lexScope.GetScope(), blockStatement);
lexScope.GetScope()->BindNode(staticBlock);
node->Definition()->AddToBody(staticBlock);
return staticBlock;
}
bool Transformer::HasPrivateIdentifierInDecorators(const ir::ClassDefinition *classDefinition)
{
bool hasPrivateIdentifer = false;
for (auto *it : classDefinition->Body()) {
if (it->IsMethodDefinition()) {
auto methodDecorators = it->AsMethodDefinition()->Decorators();
for (size_t i = 0; i < methodDecorators.size(); i++) {
FindPrivateIdentifierInDecorator(methodDecorators[i]->Expr(), &hasPrivateIdentifer);
if (hasPrivateIdentifer) {
return true;
}
}
auto paramsDecorators = it->AsMethodDefinition()->GetParamDecorators();
for (size_t i = 0; i < paramsDecorators.size(); i++) {
auto paramDecorators = paramsDecorators[i].decorators;
for (size_t j = 0; j < paramDecorators.size(); j++) {
FindPrivateIdentifierInDecorator(paramDecorators[j]->Expr(), &hasPrivateIdentifer);
if (hasPrivateIdentifer) {
return true;
}
}
}
} else if (it->IsClassProperty()) {
auto propDecorators = it->AsClassProperty()->Decorators();
for (size_t i = 0; i < propDecorators.size(); i++) {
FindPrivateIdentifierInDecorator(propDecorators[i]->Expr(), &hasPrivateIdentifer);
if (hasPrivateIdentifer) {
return true;
}
}
}
}
return hasPrivateIdentifer;
}
void Transformer::FindPrivateIdentifierInDecorator(const ir::AstNode *parent, bool *hasprivateIdentifier)
{
parent->Iterate([this, hasprivateIdentifier](auto *childNode) {
FindPrivateIdentifierInChildNode(childNode, hasprivateIdentifier);
});
}
void Transformer::FindPrivateIdentifierInChildNode(const ir::AstNode *childNode, bool *hasprivateIdentifier)
{
if (*hasprivateIdentifier) {
return;
}
switch (childNode->Type()) {
case ir::AstNodeType::MEMBER_EXPRESSION: {
if (childNode->AsMemberExpression()->Property()->IsPrivateIdentifier()) {
*hasprivateIdentifier = true;
return;
}
break;
}
default: {
FindPrivateIdentifierInDecorator(childNode, hasprivateIdentifier);
break;
}
}
}
std::vector<ir::AstNode *> Transformer::CreateVariableDeclarationForDecorators(ir::AstNode *node)
{
std::vector<ir::AstNode *> res;
switch (node->Type()) {
case ir::AstNodeType::METHOD_DEFINITION: {
auto methodDecorators = node->AsMethodDefinition()->Decorators();
for (size_t i = 0; i < methodDecorators.size(); i++) {
util::StringView varName = CreateNewVariable(false);
auto *varDecl = CreateVariableDeclarationWithIdentify(varName, VariableParsingFlags::LET, nullptr,
false, methodDecorators[i]->Expr(), true);
SetRangeRecursively(varDecl, methodDecorators[i]);
res.push_back(varDecl);
}
auto paramsDecorators = node->AsMethodDefinition()->GetParamDecorators();
for (size_t i = 0; i < paramsDecorators.size(); i++) {
auto paramDecorators = paramsDecorators[i].decorators;
for (size_t j = 0; j < paramDecorators.size(); j++) {
util::StringView varName = CreateNewVariable(false);
auto *varDecl = CreateVariableDeclarationWithIdentify(varName, VariableParsingFlags::LET, nullptr,
false, paramDecorators[j]->Expr(), true);
SetRangeRecursively(varDecl, paramDecorators[j]);
res.push_back(varDecl);
}
}
return res;
}
case ir::AstNodeType::CLASS_PROPERTY: {
auto propDecorators = node->AsClassProperty()->Decorators();
for (size_t i = 0; i < propDecorators.size(); i++) {
util::StringView varName = CreateNewVariable(false);
res.push_back(CreateVariableDeclarationWithIdentify(varName, VariableParsingFlags::LET, nullptr,
false, propDecorators[i]->Expr(), true));
}
return res;
}
case ir::AstNodeType::CLASS_DECLARATION: {
auto classDecorators = node->AsClassDeclaration()->Decorators();
for (size_t i = 0; i < classDecorators.size(); i++) {
util::StringView varName = CreateNewVariable(false);
res.push_back(CreateVariableDeclarationWithIdentify(varName, VariableParsingFlags::LET, nullptr,
false, classDecorators[i]->Expr(), true));
}
auto ctorParamsDecorators = node->AsClassDeclaration()->Definition()->Ctor()->GetParamDecorators();
for (size_t i = 0; i < ctorParamsDecorators.size(); i++) {
auto ctorParamDecorators = ctorParamsDecorators[i].decorators;
for (size_t j = 0; j < ctorParamDecorators.size(); j++) {
util::StringView varName = CreateNewVariable(false);
res.push_back(CreateVariableDeclarationWithIdentify(varName, VariableParsingFlags::LET, nullptr,
false, ctorParamDecorators[j]->Expr(), true));
}
}
return res;
}
default: {
UNREACHABLE();
}
}
}
std::vector<ir::AstNode *> Transformer::CreateParamDecorators(util::StringView className,
ir::MethodDefinition *node,
const std::vector<ir::AstNode *> &variableDeclarations,
bool isConstructor,
bool isStatic)
{
* Param decorators
* Transform:
* class C {
* f(@g a){}
* }
*
* To:
* class C {
* f(a){}
* }
* g(C.prototype, "f", 0)
*
* Static method or constructor will use constructor function of the class instead of prototype of class
*/
std::vector<ir::AstNode *> res;
size_t pos = variableDeclarations.size();
auto paramsDecorators = node->GetParamDecorators();
for (int i = static_cast<int>(paramsDecorators.size()) - 1; i >= 0; i--) {
auto paramIndex = paramsDecorators[i].paramIndex;
auto decorators = paramsDecorators[i].decorators;
for (int j = static_cast<int>(decorators.size()) - 1; j >= 0; j--) {
ArenaVector<ir::Expression *> arguments(Allocator()->Adapter());
arguments.push_back(CreateDecoratorTarget(className, isConstructor || isStatic, decorators[j]));
arguments.push_back(isConstructor ?
CreateReferenceIdentifier(CONSTRUCTOR_NAME) :
GetClassMemberName(node->Key(), node->Computed(), node));
arguments.push_back(AllocNode<ir::NumberLiteral>(paramIndex));
auto *callExpr = AllocNode<ir::CallExpression>(
variableDeclarations[--pos]->AsVariableDeclaration()->Declarators().front()->Id(),
std::move(arguments), nullptr, false);
auto *exprStmt = AllocNode<ir::ExpressionStatement>(callExpr);
SetRangeRecursively(exprStmt, decorators[j]);
res.push_back(exprStmt);
}
}
return res;
}
std::vector<ir::AstNode *> Transformer::CreatePropertyDecorators(util::StringView className,
ir::ClassProperty *node,
const std::vector<ir::AstNode *> &variableDeclarations,
bool isStatic)
{
* Property decorators
* Transform:
* class C {
* @f a = 1
* }
*
* To:
* class C {
* a = 1
* }
* f(C.prototype, "a")
*
* Static property will use constructor function of the class instead of prototype of class
*/
std::vector<ir::AstNode *> res;
auto decorators = node->Decorators();
for (int i = static_cast<int>(decorators.size() - 1); i >= 0; i--) {
ArenaVector<ir::Expression *> arguments(Allocator()->Adapter());
arguments.push_back(CreateDecoratorTarget(className, isStatic, decorators[i]));
arguments.push_back(GetClassMemberName(node->Key(), node->IsComputed(), node));
auto *callExpr = AllocNode<ir::CallExpression>(
variableDeclarations[i]->AsVariableDeclaration()->Declarators().front()->Id(),
std::move(arguments), nullptr, false);
auto *exprStmt = AllocNode<ir::ExpressionStatement>(callExpr);
SetRangeRecursively(exprStmt, decorators[i]);
res.push_back(exprStmt);
}
return res;
}
std::vector<ir::AstNode *> Transformer::CreateMethodDecorators(util::StringView className,
ir::MethodDefinition *node,
const std::vector<ir::AstNode *> &variableDeclarations,
bool isStatic)
{
* Method decorators and accessor decorators
* Transform:
* class C {
* @g
* f(){}
* }
*
* To:
* class C {
* f(){}
* }
* var ###a = Object.getOwnPropertyDescriptor(C.prototype, "f");
* Object.defineProperty(C.prototype, "f",
* g(C.prototype, "f", ###a) || ###a);
*
* static method will use constructor function of the class instead of prototype of class
* If the decorator has a return value, it will be set as the new property of the method
*/
std::vector<ir::AstNode *> res;
size_t pos = node->Decorators().size();
auto decorators = node->Decorators();
for (int i = static_cast<int>(decorators.size() - 1); i >= 0; i--) {
ArenaVector<ir::Expression *> arguments(Allocator()->Adapter());
arguments.push_back(CreateDecoratorTarget(className, isStatic, decorators[i]));
arguments.push_back(GetClassMemberName(node->Key(), node->Computed(), node));
util::StringView varName = CreateNewVariable(false);
auto getOwnPropertyDescriptorCall = CreateGetOwnPropertyDescriptorCall(
CreateDecoratorTarget(className, isStatic, decorators[i]),
GetClassMemberName(node->Key(), node->Computed(), node), decorators[i]);
auto *varDecl = CreateVariableDeclarationWithIdentify(varName, VariableParsingFlags::LET, nullptr,
false, getOwnPropertyDescriptorCall, true);
SetRangeRecursively(varDecl, decorators[i]);
res.push_back(varDecl);
arguments.push_back(AllocNode<ir::Identifier>(varName));
auto *callExpr = AllocNode<ir::CallExpression>(
variableDeclarations[--pos]->AsVariableDeclaration()->Declarators().front()->Id(),
std::move(arguments), nullptr, false);
auto newValue = AllocNode<ir::BinaryExpression>(callExpr, AllocNode<ir::Identifier>(varName),
lexer::TokenType::PUNCTUATOR_LOGICAL_OR);
auto *defineProperty = CreateDefinePropertyCall(CreateDecoratorTarget(className, isStatic, decorators[i]),
GetClassMemberName(node->Key(), node->Computed(), node), newValue, decorators[i]);
auto *exprStmt = AllocNode<ir::ExpressionStatement>(defineProperty);
SetRangeRecursively(exprStmt, decorators[i]);
res.push_back(exprStmt);
}
return res;
}
ir::Expression *Transformer::CreateDecoratorTarget(util::StringView className, bool isStatic,
const ir::AstNode *originalNode)
{
if (isStatic) {
return CreateReferenceIdentifier(className);
}
return CreateClassPrototype(className, originalNode);
}
ir::MemberExpression *Transformer::CreateClassPrototype(util::StringView className, const ir::AstNode *originalNode)
{
auto *cls = CreateReferenceIdentifier(className);
auto *protoIdent = AllocNode<ir::Identifier>(CLASS_PROTOTYPE);
auto *memberExpr = AllocNode<ir::MemberExpression>(cls, protoIdent,
ir::MemberExpression::MemberExpressionKind::PROPERTY_ACCESS, false, false);
if (originalNode != nullptr) {
SetRangeRecursively(memberExpr, originalNode);
}
return memberExpr;
}
ir::CallExpression *Transformer::CreateDefinePropertyCall(ir::Expression *target,
ir::Expression *key,
ir::Expression *value,
const ir::AstNode *originalNode)
{
auto *id = CreateReferenceIdentifier(OBJECT_VAR_NAME);
auto *caller = AllocNode<ir::MemberExpression>(id, AllocNode<ir::Identifier>(FUNC_NAME_OF_DEFINE_PROPERTY),
ir::MemberExpression::MemberExpressionKind::PROPERTY_ACCESS, false, false);
ArenaVector<ir::Expression *> arguments(Allocator()->Adapter());
arguments.push_back(target);
arguments.push_back(key);
arguments.push_back(value);
auto *callExpr = AllocNode<ir::CallExpression>(caller, std::move(arguments), nullptr, false);
if (originalNode != nullptr) {
SetRangeRecursively(callExpr, originalNode);
}
return callExpr;
}
ir::CallExpression *Transformer::CreateGetOwnPropertyDescriptorCall(ir::Expression *target, ir::Expression *key,
const ir::AstNode *originalNode)
{
auto *id = CreateReferenceIdentifier(OBJECT_VAR_NAME);
auto *caller = AllocNode<ir::MemberExpression>(id,
AllocNode<ir::Identifier>(FUNC_NAME_OF_GET_OWN_PROPERTY_DESCRIPTOR),
ir::MemberExpression::MemberExpressionKind::PROPERTY_ACCESS, false, false);
ArenaVector<ir::Expression *> arguments(Allocator()->Adapter());
arguments.push_back(target);
arguments.push_back(key);
auto *callExpr = AllocNode<ir::CallExpression>(caller, std::move(arguments), nullptr, false);
if (originalNode != nullptr) {
SetRangeRecursively(callExpr, originalNode);
}
return callExpr;
}
ir::Expression *Transformer::GetClassMemberName(ir::Expression *key, bool isComputed,
ir::Statement *node, bool inDecorator)
{
if (isComputed) {
auto name = GetComputedPropertyBinding(node);
auto *ident = AllocNode<ir::Identifier>(name);
ident->SetRange(key->Range());
return ident;
}
if (key->IsIdentifier()) {
if (inDecorator) {
auto *strLiteral = AllocNode<ir::StringLiteral>(key->AsIdentifier()->Name());
strLiteral->SetRange(key->AsIdentifier()->Range());
return strLiteral;
} else {
auto *ident = AllocNode<ir::Identifier>(key->AsIdentifier()->Name());
ident->SetRange(key->AsIdentifier()->Range());
return ident;
}
} else if (key->IsStringLiteral()) {
auto *strLiteral = AllocNode<ir::StringLiteral>(key->AsStringLiteral()->Str());
strLiteral->SetRange(key->AsStringLiteral()->Range());
return strLiteral;
} else if (key->IsNumberLiteral()) {
auto *numLiteral = AllocNode<ir::NumberLiteral>(key->AsNumberLiteral()->Number(),
key->AsNumberLiteral()->Str());
numLiteral->SetRange(key->AsNumberLiteral()->Range());
return numLiteral;
} else if (key->IsBigIntLiteral()) {
auto *bigIntLiteral = AllocNode<ir::BigIntLiteral>(key->AsBigIntLiteral()->Str());
bigIntLiteral->SetRange(key->AsBigIntLiteral()->Range());
return bigIntLiteral;
}
UNREACHABLE();
return nullptr;
}
std::vector<ir::AstNode *> Transformer::CreateClassDecorators(ir::ClassDeclaration *node,
const std::vector<ir::AstNode *> &variableDeclarations)
{
* Class decorators
* Transform:
* @f
* class C {
* }
*
* To:
* class C {
* }
* C = f(C) || C;
*
* If the decorator has a return value, it will be used as the new declaration of the class
*/
auto name = node->Definition()->GetName();
auto decorators = node->Decorators();
auto size = decorators.size();
size_t pos = size;
std::vector<ir::AstNode *> res;
for (int i = static_cast<int>(size - 1); i >= 0; i--) {
ArenaVector<ir::Expression *> arguments(Allocator()->Adapter());
arguments.push_back(CreateReferenceIdentifier(name));
auto *callExpr = AllocNode<ir::CallExpression>(
variableDeclarations[--pos]->AsVariableDeclaration()->Declarators().front()->Id(),
std::move(arguments), nullptr, false);
auto left = CreateReferenceIdentifier(name);
auto id = CreateReferenceIdentifier(name);
auto right = AllocNode<ir::BinaryExpression>(callExpr, id, lexer::TokenType::PUNCTUATOR_LOGICAL_OR);
auto middle = CreateReferenceIdentifier(GetClassAliasName());
auto innerAssignExpr = AllocNode<ir::AssignmentExpression>(middle, right,
lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
auto *assignExpr = AllocNode<ir::AssignmentExpression>(left, innerAssignExpr,
lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
auto *exprStmt = AllocNode<ir::ExpressionStatement>(assignExpr);
SetRangeRecursively(exprStmt, decorators[i]);
res.push_back(exprStmt);
}
return res;
}
ir::AstNode *Transformer::VisitTsImportEqualsDeclaration(ir::TSImportEqualsDeclaration *node)
{
if (!IsInstantiatedImportEquals(node, Scope())) {
return node;
}
auto *express = node->ModuleReference();
auto name = node->Id()->Name();
if (IsTsModule() && node->IsExport()) {
auto moduleName = GetCurrentTSModuleName();
auto *id = CreateReferenceIdentifier(moduleName);
auto *left = AllocNode<ir::MemberExpression>(id, AllocNode<ir::Identifier>(name),
ir::MemberExpression::MemberExpressionKind::PROPERTY_ACCESS, false, false);
ir::Expression *right = CreateMemberExpressionFromQualified(express);
auto *assignExpr = AllocNode<ir::AssignmentExpression>(left, right,
lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
auto *res = AllocNode<ir::ExpressionStatement>(assignExpr);
return res;
}
ir::Expression *init = CreateMemberExpressionFromQualified(express);
ir::Statement *res = CreateVariableDeclarationWithIdentify(name, VariableParsingFlags::VAR, node,
node->IsExport(), init);
if (node->IsExport()) {
ArenaVector<ir::ExportSpecifier *> specifiers(Allocator()->Adapter());
res = AllocNode<ir::ExportNamedDeclaration>(res, std::move(specifiers));
AddExportLocalEntryItem(name, node->Id());
}
return res;
}
bool Transformer::IsInstantiatedImportEquals(const ir::TSImportEqualsDeclaration *node, binder::Scope *scope) const
{
if (!node) {
return false;
}
bool isType = true;
auto *var = FindTSModuleVariable(node->ModuleReference(), scope, &isType);
if (var == nullptr) {
return !isType;
}
auto *decl = var->Declaration();
ASSERT(decl->IsNamespaceDecl());
return decl->AsNamespaceDecl()->IsInstantiated();
return false;
}
binder::Variable *Transformer::FindTSModuleVariable(const ir::Expression *node,
const binder::Scope *scope,
bool *isType) const
{
if (node == nullptr || !(node->IsTSQualifiedName() || node->IsIdentifier())) {
return nullptr;
}
if (node->IsTSQualifiedName()) {
auto *tsQualifiedName = node->AsTSQualifiedName();
auto *var = FindTSModuleVariable(tsQualifiedName->Left(), scope, isType);
if (var == nullptr) {
return nullptr;
}
auto *exportTSBindings = var->AsNamespaceVariable()->GetExportBindings();
auto name = tsQualifiedName->Right()->Name();
binder::Variable *res = exportTSBindings->FindExportTSVariable<binder::TSBindingType::NAMESPACE>(name);
if (res != nullptr) {
return res;
}
res = exportTSBindings->FindExportTSVariable<binder::TSBindingType::IMPORT_EQUALS>(name);
if (res != nullptr) {
auto *node = res->Declaration()->Node();
return FindTSModuleVariable(node->Parent()->AsTSImportEqualsDeclaration()->ModuleReference(),
res->AsImportEqualsVariable()->GetScope(), isType);
}
*isType = exportTSBindings->FindExportVariable(name) == nullptr &&
exportTSBindings->FindExportTSVariable<binder::TSBindingType::ENUMLITERAL>(name) == nullptr;
return nullptr;
}
auto name = node->AsIdentifier()->Name();
auto *currentScope = scope;
while (currentScope != nullptr) {
auto *res = FindTSVariable<binder::TSBindingType::NAMESPACE>(currentScope, name);
if (res != nullptr) {
return res;
}
res = FindTSVariable<binder::TSBindingType::IMPORT_EQUALS>(currentScope, name);
if (res != nullptr) {
auto *node = res->Declaration()->Node();
return FindTSModuleVariable(node->Parent()->AsTSImportEqualsDeclaration()->ModuleReference(),
res->AsImportEqualsVariable()->GetScope(), isType);
}
res = FindTSVariable<binder::TSBindingType::ENUMLITERAL>(currentScope, name);
if (res != nullptr) {
*isType = false;
return nullptr;
}
res = currentScope->FindLocal(name, binder::ResolveBindingOptions::BINDINGS);
if (res != nullptr) {
*isType = false;
return nullptr;
}
currentScope = currentScope->Parent();
}
*isType = true;
return nullptr;
}
template <binder::TSBindingType type>
binder::Variable *Transformer::FindTSVariable(const binder::Scope *scope, const util::StringView &name) const
{
binder::Variable *res = scope->FindLocalTSVariable<type>(name);
if (res == nullptr && scope->IsTSModuleScope()) {
res = scope->AsTSModuleScope()->FindExportTSVariable<type>(name);
}
return res;
}
std::vector<ir::AstNode *> Transformer::VisitExportNamedVariable(ir::Statement *decl)
{
std::vector<ir::AstNode *> res;
if (decl->IsVariableDeclaration()) {
auto declarators = decl->AsVariableDeclaration()->Declarators();
for (auto *it : declarators) {
if (it->Init()) {
auto *left = std::get<ir::AstNode *>(VisitTSNode(it->Id()))->AsExpression();
auto *right = std::get<ir::AstNode *>(VisitTSNode(it->Init()))->AsExpression();
auto *assignExpr = AllocNode<ir::AssignmentExpression>(left, right,
lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
res.push_back(AllocNode<ir::ExpressionStatement>(assignExpr));
}
}
} else if (decl->IsFunctionDeclaration() || decl->IsClassDeclaration()) {
auto newDecl = VisitTSNode(decl);
if (std::holds_alternative<ir::AstNode *>(newDecl)) {
res.push_back(std::get<ir::AstNode *>(newDecl));
} else {
auto statements = std::get<std::vector<ir::AstNode *>>(newDecl);
res.insert(res.end(), statements.begin(), statements.end());
}
auto name = decl->IsFunctionDeclaration() ?
decl->AsFunctionDeclaration()->Function()->Id() :
decl->AsClassDeclaration()->Definition()->Ident();
ASSERT(name != nullptr);
res.push_back(CreateTsModuleAssignment(name->Name()));
}
return res;
}
ir::Expression *Transformer::CreateMemberExpressionFromQualified(ir::Expression *node)
{
if (node->IsTSQualifiedName()) {
auto *tsQualifiedName = node->AsTSQualifiedName();
auto *left = CreateMemberExpressionFromQualified(tsQualifiedName->Left());
auto *right = AllocNode<ir::Identifier>(tsQualifiedName->Right()->Name());
return AllocNode<ir::MemberExpression>(left, right,
ir::MemberExpression::MemberExpressionKind::PROPERTY_ACCESS, false, false);
}
ASSERT(node->IsIdentifier());
auto *id = CreateReferenceIdentifier(node->AsIdentifier()->Name());
return id;
}
void Transformer::SetOriginalNode(ir::UpdateNodes res, ir::AstNode *originalNode) const
{
if (std::holds_alternative<ir::AstNode *>(res)) {
auto *node = std::get<ir::AstNode *>(res);
if (node == nullptr || node == originalNode) {
return;
}
node->SetOriginal(originalNode);
node->SetRange(originalNode->Range());
} else {
auto nodes = std::get<std::vector<ir::AstNode *>>(res);
for (auto *it : nodes) {
it->SetOriginal(originalNode);
it->SetRange(originalNode->Range());
}
}
}
void Transformer::ResetParentScope(ir::UpdateNodes res, binder::Scope *parentScope) const
{
if (std::holds_alternative<ir::AstNode *>(res)) {
auto *node = std::get<ir::AstNode *>(res);
if (node == nullptr) {
return;
}
ResetParentScopeForAstNode(node, parentScope);
} else {
auto nodes = std::get<std::vector<ir::AstNode *>>(res);
for (auto *it : nodes) {
ResetParentScopeForAstNode(it, parentScope);
}
}
}
ir::ExpressionStatement *Transformer::CreateTsModuleAssignment(util::StringView name)
{
auto moduleName = GetCurrentTSModuleName();
auto *id = CreateReferenceIdentifier(moduleName);
auto *left = AllocNode<ir::MemberExpression>(id, AllocNode<ir::Identifier>(name),
ir::MemberExpression::MemberExpressionKind::PROPERTY_ACCESS, false, false);
auto *right = CreateReferenceIdentifier(name);
auto *assignExpr = AllocNode<ir::AssignmentExpression>(left, right, lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
return AllocNode<ir::ExpressionStatement>(assignExpr);
}
util::StringView Transformer::GetNameFromModuleDeclaration(ir::TSModuleDeclaration *node) const
{
return node->Name()->AsIdentifier()->Name();
}
ir::VariableDeclaration *Transformer::CreateVariableDeclarationWithIdentify(util::StringView name,
VariableParsingFlags flags,
ir::AstNode *node,
bool isExport,
ir::Expression *init,
bool needBinding)
{
auto *ident = CreateReferenceIdentifier(name, node);
auto *declarator = AllocNode<ir::VariableDeclarator>(ident, init);
ArenaVector<ir::VariableDeclarator *> declarators(Allocator()->Adapter());
declarators.push_back(declarator);
auto varKind = ir::VariableDeclaration::VariableDeclarationKind::VAR;
if (flags & VariableParsingFlags::VAR) {
} else if (flags & VariableParsingFlags::LET) {
varKind = ir::VariableDeclaration::VariableDeclarationKind::LET;
} else {
varKind = ir::VariableDeclaration::VariableDeclarationKind::CONST;
}
auto *declaration = AllocNode<ir::VariableDeclaration>(varKind, std::move(declarators), false);
lexer::SourcePosition startPos(0, 0);
if (node != nullptr) {
startPos = node->Start();
}
if (needBinding) {
binder::Decl *decl = nullptr;
binder::DeclarationFlags declflag = isExport ?
binder::DeclarationFlags::EXPORT :
binder::DeclarationFlags::NONE;
if (flags & VariableParsingFlags::VAR) {
decl = Binder()->AddDecl<binder::VarDecl>(startPos, declflag, false, name);
} else if (flags & VariableParsingFlags::LET) {
decl = Binder()->AddDecl<binder::LetDecl>(startPos, declflag, false, name);
} else {
decl = Binder()->AddDecl<binder::ConstDecl>(startPos, declflag, false, name);
}
decl->BindNode(declaration);
}
return declaration;
}
util::StringView Transformer::GetParamName(ir::AstNode *node, util::StringView name) const
{
if (node->IsTSModuleDeclaration()) {
auto scope = node->AsTSModuleDeclaration()->Scope();
if (scope && !scope->HasVariableName(name)) {
return name;
}
}
if (node->IsTSEnumDeclaration()) {
auto scope = node->AsTSEnumDeclaration()->Scope();
if (scope && !scope->HasDeclarationName(name)) {
return name;
}
}
auto uniqueName = CreateUniqueName(std::string(name) + std::string(INDEX_DIVISION));
return uniqueName;
}
ir::CallExpression *Transformer::CreateCallExpressionForTsModule(ir::TSModuleDeclaration *node,
util::StringView name,
bool isExport)
{
ir::ScriptFunction *funcNode = nullptr;
binder::FunctionScope *funcScope = node->Scope();
binder::FunctionParamScope *funcParamScope = funcScope->ParamScope();
auto paramName = GetParamName(node, name);
{
auto paramScopeCtx = binder::LexicalScope<binder::FunctionParamScope>::Enter(Binder(), funcParamScope);
ArenaVector<ir::Expression *> params(Allocator()->Adapter());
auto *parameter = CreateReferenceIdentifier(paramName);
Binder()->AddParamDecl(parameter);
params.push_back(parameter);
ir::BlockStatement *blockNode = nullptr;
{
auto scopeCtx = binder::LexicalScope<binder::FunctionScope>::Enter(Binder(), funcScope);
tsModuleList_.push_back({paramName, funcScope});
if (node->Body()->IsTSModuleDeclaration()) {
auto *tsModule = node->Body()->AsTSModuleDeclaration();
auto body = std::get<std::vector<ir::AstNode *>>(VisitTsModuleDeclaration(tsModule, true));
ArenaVector<ir::Statement *> statements(Allocator()->Adapter());
for (auto *it : body) {
statements.push_back(static_cast<ir::Statement *>(it));
}
blockNode = AllocNode<ir::BlockStatement>(funcScope, std::move(statements));
} else {
auto body = VisitTSNodes(node->Body());
CHECK_NOT_NULL(body);
blockNode = AllocNode<ir::BlockStatement>(funcScope,
std::move(body->AsTSModuleBlock()->Statements()));
}
tsModuleList_.pop_back();
funcScope->AddBindsFromParam();
}
funcNode = AllocNode<ir::ScriptFunction>(funcScope, std::move(params), nullptr, blockNode, nullptr,
ir::ScriptFunctionFlags::NONE, false, Extension() == ScriptExtension::TS);
funcScope->BindNode(funcNode);
funcParamScope->BindNode(funcNode);
}
auto *funcExpr = AllocNode<ir::FunctionExpression>(funcNode);
ArenaVector<ir::Expression *> arguments = CreateCallExpressionArguments(name, isExport);
auto *callExpr = AllocNode<ir::CallExpression>(funcExpr, std::move(arguments), nullptr, false);
return callExpr;
}
ir::Expression *Transformer::CreateTsModuleParam(util::StringView paramName, bool isExport)
{
if (isExport) {
auto moduleName = GetCurrentTSModuleName();
auto *id = CreateReferenceIdentifier(moduleName);
return AllocNode<ir::MemberExpression>(id, AllocNode<ir::Identifier>(paramName),
ir::MemberExpression::MemberExpressionKind::PROPERTY_ACCESS, false, false);
}
auto *id = CreateReferenceIdentifier(paramName);
return id;
}
void Transformer::AddExportLocalEntryItem(util::StringView name, const ir::Identifier *identifier)
{
auto moduleRecord = GetSourceTextModuleRecord();
auto *entry = moduleRecord->NewEntry<SourceTextModuleRecord::ExportEntry>(name, name, identifier, identifier);
[[maybe_unused]] bool res = moduleRecord->AddLocalExportEntry(entry);
ASSERT(res);
}
ir::UpdateNodes Transformer::VisitTsModuleDeclaration(ir::TSModuleDeclaration *node, bool isExport)
{
std::vector<ir::AstNode *> res;
util::StringView name = GetNameFromModuleDeclaration(node);
auto findRes = Scope()->FindLocal(name, binder::ResolveBindingOptions::BINDINGS);
if (findRes == nullptr) {
res.push_back(CreateVariableDeclarationForTSEnumOrTSModule(name, node, isExport));
}
auto *callExpr = CreateCallExpressionForTsModule(node, name, isExport && IsTsModule());
auto *exprStatementNode = AllocNode<ir::ExpressionStatement>(callExpr);
res.push_back(exprStatementNode);
return res;
}
ir::Identifier *Transformer::CreateReferenceIdentifier(util::StringView name, ir::AstNode *originalNode)
{
auto *node = AllocNode<ir::Identifier>(name);
node->AsIdentifier()->SetReference();
if (originalNode != nullptr) {
SetOriginalNode(node, originalNode);
}
return node;
}
ir::UpdateNodes Transformer::VisitTsEnumDeclaration(ir::TSEnumDeclaration *node, bool isExport)
{
std::vector<ir::AstNode *> res;
util::StringView name = GetNameFromTsEnumDeclaration(node);
auto findRes = Scope()->FindLocal(name);
if (findRes == nullptr) {
res.push_back(CreateVariableDeclarationForTSEnumOrTSModule(name, node, isExport));
}
auto *callExpr = CreateCallExpressionForTsEnum(node, name, isExport && IsTsModule());
auto *exprStatementNode = AllocNode<ir::ExpressionStatement>(callExpr);
SetRangeRecursively(exprStatementNode, node);
res.push_back(exprStatementNode);
return res;
}
ir::AstNode *Transformer::CreateVariableDeclarationForTSEnumOrTSModule(util::StringView name,
ir::AstNode *node, bool isExport)
{
auto flag = Scope()->Parent() == nullptr ? VariableParsingFlags::VAR : VariableParsingFlags::LET;
auto *variableDeclaration = CreateVariableDeclarationWithIdentify(name, flag, node, isExport);
if (node->IsTSEnumDeclaration()) {
SetRangeRecursively(variableDeclaration, node);
}
bool doExport = isExport && !IsTsModule();
if (doExport) {
ArenaVector<ir::ExportSpecifier *> specifiers(Allocator()->Adapter());
auto *exportDeclaration = AllocNode<ir::ExportNamedDeclaration>(variableDeclaration, std::move(specifiers));
auto *ident = node->IsTSEnumDeclaration() ?
node->AsTSEnumDeclaration()->Key()->AsIdentifier() : node->AsTSModuleDeclaration()->Name()->AsIdentifier();
AddExportLocalEntryItem(name, ident);
return exportDeclaration;
}
return variableDeclaration;
}
util::StringView Transformer::GetNameFromTsEnumDeclaration(const ir::TSEnumDeclaration *node) const
{
auto *name = node->AsTSEnumDeclaration()->Key();
return name->AsIdentifier()->Name();
}
ir::CallExpression *Transformer::CreateCallExpressionForTsEnum(ir::TSEnumDeclaration *node, util::StringView name,
bool isExport)
{
ir::ScriptFunction *funcNode = nullptr;
binder::FunctionScope *funcScope = node->Scope();
binder::FunctionParamScope *funcParamScope = funcScope->ParamScope();
util::StringView paramName = GetParamName(node, name);
{
auto paramScopeCtx = binder::LexicalScope<binder::FunctionParamScope>::Enter(Binder(), funcParamScope);
ArenaVector<ir::Expression *> params(Allocator()->Adapter());
auto *parameter = CreateReferenceIdentifier(paramName);
Binder()->AddParamDecl(parameter);
params.push_back(parameter);
ir::BlockStatement *blockNode = nullptr;
{
auto scopeCtx = binder::LexicalScope<binder::FunctionScope>::Enter(Binder(), funcScope);
tsEnumList_.push_back({paramName, funcScope});
ArenaVector<ir::TSEnumMember *> members = node->Members();
ArenaVector<ir::Statement *> statements(Allocator()->Adapter());
ir::TSEnumMember *preTsEnumMember = nullptr;
for (auto member : members) {
auto *currTsEnumMember = member->AsTSEnumMember();
auto statement = CreateTsEnumMember(currTsEnumMember, preTsEnumMember, paramName);
preTsEnumMember = currTsEnumMember;
SetRangeRecursively(statement, currTsEnumMember);
statements.push_back(statement);
}
blockNode = AllocNode<ir::BlockStatement>(funcScope, std::move(statements));
tsEnumList_.pop_back();
funcScope->AddBindsFromParam();
}
funcNode = AllocNode<ir::ScriptFunction>(funcScope, std::move(params), nullptr, blockNode, nullptr,
ir::ScriptFunctionFlags::NONE, false, Extension() == ScriptExtension::TS);
funcScope->BindNode(funcNode);
funcParamScope->BindNode(funcNode);
}
auto *funcExpr = AllocNode<ir::FunctionExpression>(funcNode);
ArenaVector<ir::Expression *> arguments = CreateCallExpressionArguments(name, isExport);
auto *callExpr = AllocNode<ir::CallExpression>(funcExpr, std::move(arguments), nullptr, false);
return callExpr;
}
void Transformer::SetRangeRecursively(ir::AstNode *node, const ir::AstNode *originalNode, bool replaceAll)
{
if (node == nullptr || originalNode == nullptr) {
return;
}
bool isEmptyRange = (node->Range().start.line == 0 && node->Range().end.line == 0 &&
node->Range().start.index == 0 && node->Range().end.index == 0);
if (!replaceAll && !isEmptyRange) {
return;
}
node->SetRange(originalNode->Range());
node->Iterate([this, originalNode, replaceAll](auto *child) {
SetRangeRecursively(child, originalNode, replaceAll);
});
}
ArenaVector<ir::Expression *> Transformer::CreateCallExpressionArguments(util::StringView name, bool isExport)
{
ArenaVector<ir::Expression *> arguments(Allocator()->Adapter());
ArenaVector<ir::Expression *> properties(Allocator()->Adapter());
auto *objectExpression = AllocNode<ir::ObjectExpression>(ir::AstNodeType::OBJECT_EXPRESSION,
std::move(properties),
false);
auto assignExpr = AllocNode<ir::AssignmentExpression>(CreateTsModuleParam(name, isExport),
objectExpression,
lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
auto argument = AllocNode<ir::BinaryExpression>(CreateTsModuleParam(name, isExport),
assignExpr,
lexer::TokenType::PUNCTUATOR_LOGICAL_OR);
if (isExport) {
auto *id = CreateReferenceIdentifier(name);
arguments.push_back(AllocNode<ir::AssignmentExpression>(id, argument,
lexer::TokenType::PUNCTUATOR_SUBSTITUTION));
} else {
arguments.push_back(argument);
}
return arguments;
}
ir::ExpressionStatement *Transformer::CreateTsEnumMember(ir::TSEnumMember *node, ir::TSEnumMember *preNode,
util::StringView enumLiteralName)
{
util::StringView enumMemberName = GetNameFromEnumMember(node);
binder::Variable *enumVar = Scope()->AsTSEnumScope()->FindEnumMemberVariable(enumMemberName);
CHECK_NOT_NULL(enumVar);
if (node->Init() != nullptr) {
bool isStringInit = enumVar->AsEnumVariable()->StringInit();
if (!enumVar->AsEnumVariable()->IsVisited()) {
isStringInit = IsStringInitForEnumMember(node->Init(), Scope());
if (isStringInit) {
enumVar->AsEnumVariable()->SetStringInit();
}
enumVar->AsEnumVariable()->SetVisited();
}
return isStringInit ? CreateTsEnumMemberWithStringInit(node, enumLiteralName, enumMemberName) :
CreateTsEnumMemberWithNumberInit(node, enumLiteralName, enumMemberName);
}
enumVar->AsEnumVariable()->SetVisited();
return CreateTsEnumMemberWithoutInit(node, preNode, enumLiteralName, enumMemberName);
}
ir::ExpressionStatement *Transformer::CreateTsEnumMemberWithStringInit(ir::TSEnumMember *node,
util::StringView enumLiteralName,
util::StringView enumMemberName)
{
auto *object = CreateReferenceIdentifier(enumLiteralName);
auto *property = AllocNode<ir::StringLiteral>(enumMemberName);
auto *left = AllocNode<ir::MemberExpression>(object, property,
ir::MemberExpression::MemberExpressionKind::ELEMENT_ACCESS,
true, false);
auto *right = std::get<ir::AstNode *>(VisitTSNode(node->Init()))->AsExpression();
SetRangeRecursively(right, node, true);
auto *assignExpr = AllocNode<ir::AssignmentExpression>(left, right, lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
auto *exprStatementNode = AllocNode<ir::ExpressionStatement>(assignExpr);
return exprStatementNode;
}
ir::ExpressionStatement *Transformer::CreateTsEnumMemberWithNumberInit(ir::TSEnumMember *node,
util::StringView enumLiteralName,
util::StringView enumMemberName)
{
auto *innerObject = CreateReferenceIdentifier(enumLiteralName);
auto *innerProperty = AllocNode<ir::StringLiteral>(enumMemberName);
auto *innerLeft = AllocNode<ir::MemberExpression>(innerObject, innerProperty,
ir::MemberExpression::MemberExpressionKind::ELEMENT_ACCESS,
true, false);
auto *innerRight = std::get<ir::AstNode *>(VisitTSNode(node->Init()))->AsExpression();
SetRangeRecursively(innerRight, node, true);
auto *object = CreateReferenceIdentifier(enumLiteralName);
auto *property = AllocNode<ir::AssignmentExpression>(innerLeft, innerRight,
lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
auto *left = AllocNode<ir::MemberExpression>(object, property,
ir::MemberExpression::MemberExpressionKind::ELEMENT_ACCESS,
true, false);
auto *right = AllocNode<ir::StringLiteral>(enumMemberName);
auto *assignExpr = AllocNode<ir::AssignmentExpression>(left, right, lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
auto *exprStatementNode = AllocNode<ir::ExpressionStatement>(assignExpr);
return exprStatementNode;
}
ir::ExpressionStatement *Transformer::CreateTsEnumMemberWithoutInit(ir::TSEnumMember *node,
ir::TSEnumMember *preNode,
util::StringView enumLiteralName,
util::StringView enumMemberName)
{
auto *innerObject = CreateReferenceIdentifier(enumLiteralName);
auto *innerProperty = AllocNode<ir::StringLiteral>(enumMemberName);
auto *innerLeft = AllocNode<ir::MemberExpression>(innerObject, innerProperty,
ir::MemberExpression::MemberExpressionKind::ELEMENT_ACCESS,
true, false);
ir::AssignmentExpression *property = nullptr;
if (preNode == nullptr) {
auto *innerRight = AllocNode<ir::NumberLiteral>(0);
property = AllocNode<ir::AssignmentExpression>(innerLeft, innerRight,
lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
} else {
auto *innerRightObject = CreateReferenceIdentifier(enumLiteralName);
auto *innerPropertyForMemberExpr = AllocNode<ir::Identifier>(GetNameFromEnumMember(preNode));
auto *innerMemberExpr = AllocNode<ir::MemberExpression>(innerRightObject, innerPropertyForMemberExpr,
ir::MemberExpression::MemberExpressionKind::PROPERTY_ACCESS, false, false);
auto *innerRight = AllocNode<ir::BinaryExpression>(innerMemberExpr, AllocNode<ir::NumberLiteral>(1),
lexer::TokenType::PUNCTUATOR_PLUS);
property = AllocNode<ir::AssignmentExpression>(innerLeft, innerRight,
lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
}
auto *object = CreateReferenceIdentifier(enumLiteralName);
auto *left = AllocNode<ir::MemberExpression>(object, property,
ir::MemberExpression::MemberExpressionKind::ELEMENT_ACCESS,
true, false);
auto *right = AllocNode<ir::StringLiteral>(enumMemberName);
auto *assignExpr = AllocNode<ir::AssignmentExpression>(left, right, lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
auto *exprStatementNode = AllocNode<ir::ExpressionStatement>(assignExpr);
return exprStatementNode;
}
bool Transformer::IsStringInitForEnumMember(const ir::Expression *expr, binder::Scope *scope) const
{
if (expr == nullptr) {
return false;
}
switch (expr->Type()) {
case ir::AstNodeType::STRING_LITERAL:
case ir::AstNodeType::TEMPLATE_LITERAL: {
return true;
}
case ir::AstNodeType::IDENTIFIER: {
util::StringView identName = expr->AsIdentifier()->Name();
ASSERT(scope && scope->IsTSEnumScope());
binder::Variable *v = scope->AsTSEnumScope()->FindEnumMemberVariable(identName);
if (v == nullptr) {
return false;
}
if (!v->AsEnumVariable()->IsVisited()) {
auto *initExpr = v->AsEnumVariable()->Declaration()->Node()->AsTSEnumMember()->Init();
if (IsStringInitForEnumMember(initExpr, scope)) {
v->AsEnumVariable()->SetStringInit();
}
v->AsEnumVariable()->SetVisited();
}
if (v->AsEnumVariable()->IsVisited() && v->AsEnumVariable()->StringInit()) {
return true;
}
return false;
}
case ir::AstNodeType::MEMBER_EXPRESSION: {
return IsStringForMemberExpression(expr->AsMemberExpression(), scope);
}
case ir::AstNodeType::BINARY_EXPRESSION: {
auto *left = expr->AsBinaryExpression()->Left();
auto *right = expr->AsBinaryExpression()->Right();
if (expr->AsBinaryExpression()->OperatorType() == lexer::TokenType::PUNCTUATOR_PLUS &&
IsStringInitForEnumMember(right, scope) && IsStringInitForEnumMember(left, scope)) {
return true;
}
return false;
}
default:
return false;
}
return false;
}
bool Transformer::IsStringForMemberExpression(const ir::MemberExpression *memberExpr, binder::Scope *scope) const
{
const ir::Expression *expr = memberExpr;
ArenaDeque<const ir::Expression *> members(Allocator()->Adapter());
while (expr->IsMemberExpression()) {
if (expr->AsMemberExpression()->Property()->IsIdentifier() ||
expr->AsMemberExpression()->Property()->IsStringLiteral() ||
expr->AsMemberExpression()->Property()->IsTemplateLiteral()) {
members.push_front(expr->AsMemberExpression()->Property());
expr = expr->AsMemberExpression()->Object();
} else {
return false;
}
}
if (!expr->IsIdentifier()) {
return false;
}
members.push_front(expr->AsIdentifier());
ArenaVector<binder::Variable *> findRes = FindFrontIdentifierTSVariables(members.front()->AsIdentifier(), scope);
members.pop_front();
for (auto currVar : findRes) {
if (VerifyMemberExpressionDeque(currVar, members)) {
return true;
}
}
return false;
}
ArenaVector<binder::Variable *> Transformer::FindFrontIdentifierTSVariables(const ir::Identifier *ident,
binder::Scope *scope) const
{
util::StringView name = ident->Name();
binder::Variable *v = nullptr;
ArenaVector<binder::Variable *> findRes(Allocator()->Adapter());
while (scope != nullptr) {
if (scope->IsTSEnumScope()) {
v = scope->AsTSEnumScope()->FindEnumMemberVariable(name);
if (v != nullptr) {
break;
}
}
const std::vector<binder::TSBindingType> types = {binder::TSBindingType::NAMESPACE,
binder::TSBindingType::ENUMLITERAL,
binder::TSBindingType::IMPORT_EQUALS};
FindLocalTSVariables(scope, name, types, findRes);
if (scope->IsTSModuleScope()) {
FindExportTSVariables(scope, name, types, findRes);
}
if (!findRes.empty()) {
break;
}
v = scope->FindLocal(name);
if (v != nullptr) {
if (scope->IsTSModuleScope() || scope->IsTSEnumScope()) {
v = scope->Parent()->FindLocal(name);
if (v == nullptr) {
break;
}
} else {
break;
}
}
if (scope->IsTSModuleScope()) {
v = scope->AsTSModuleScope()->FindExportVariable(name);
if (v != nullptr) {
break;
}
}
if (scope->IsTSModuleScope() || scope->IsTSEnumScope()) {
scope = scope->Parent();
}
scope = scope->Parent();
}
return findRes;
}
bool Transformer::IsInstantiatedNamespaceVariable(binder::Variable *var) const
{
ASSERT(var->IsNamespaceVariable());
auto *decl = var->AsNamespaceVariable()->Declaration();
ASSERT(decl->IsNamespaceDecl());
ArenaVector<ir::TSModuleDeclaration *> nodes = decl->AsNamespaceDecl()->Decls();
for (ir::TSModuleDeclaration *node : nodes) {
if (node->IsInstantiated()) {
return true;
}
}
return false;
}
void Transformer::FindLocalTSVariables(binder::Scope *scope, const util::StringView name,
const std::vector<binder::TSBindingType> &types,
ArenaVector<binder::Variable *> &findRes) const
{
for (binder::TSBindingType type : types) {
binder::Variable *v = nullptr;
switch (type) {
case binder::TSBindingType::NAMESPACE: {
v = scope->FindLocalTSVariable<binder::TSBindingType::NAMESPACE>(name);
if (v != nullptr && !IsInstantiatedNamespaceVariable(v)) {
v = nullptr;
}
break;
}
case binder::TSBindingType::ENUMLITERAL: {
v = scope->FindLocalTSVariable<binder::TSBindingType::ENUMLITERAL>(name);
break;
}
case binder::TSBindingType::IMPORT_EQUALS: {
v = scope->FindLocalTSVariable<binder::TSBindingType::IMPORT_EQUALS>(name);
if (v != nullptr &&
!IsInstantiatedImportEquals(v->AsImportEqualsVariable()->Declaration()->Node()->
Parent()->AsTSImportEqualsDeclaration(), scope)) {
v = nullptr;
}
break;
}
default:
continue;
}
if (v != nullptr) {
findRes.push_back(v);
}
}
}
void Transformer::FindExportTSVariables(binder::Scope *scope, const util::StringView name,
const std::vector<binder::TSBindingType> &types,
ArenaVector<binder::Variable *> &findRes) const
{
for (binder::TSBindingType type : types) {
binder::Variable *v = nullptr;
switch (type) {
case binder::TSBindingType::NAMESPACE: {
v = scope->AsTSModuleScope()->FindExportTSVariable<binder::TSBindingType::NAMESPACE>(name);
if (v != nullptr && !IsInstantiatedNamespaceVariable(v)) {
v = nullptr;
}
break;
}
case binder::TSBindingType::ENUMLITERAL: {
v = scope->AsTSModuleScope()->FindExportTSVariable<binder::TSBindingType::ENUMLITERAL>(name);
break;
}
case binder::TSBindingType::IMPORT_EQUALS: {
v = scope->AsTSModuleScope()->FindExportTSVariable<binder::TSBindingType::IMPORT_EQUALS>(name);
if (v != nullptr &&
!IsInstantiatedImportEquals(v->AsImportEqualsVariable()->Declaration()->Node()->
Parent()->AsTSImportEqualsDeclaration(), scope)) {
v = nullptr;
}
break;
}
default:
continue;
}
if (v != nullptr) {
findRes.push_back(v);
}
}
}
bool Transformer::VerifyMemberExpressionDeque(binder::Variable *currVar,
ArenaDeque<const ir::Expression *> members) const
{
ASSERT(!members.empty());
switch (currVar->Flags()) {
case binder::VariableFlags::ENUM_LITERAL: {
util::StringView enumMemberName = GetNameForMemberExpressionItem(members.front());
members.pop_front();
if (!members.empty()) {
return false;
}
binder::Variable *enumMemberVar = currVar->AsEnumLiteralVariable()->FindEnumMemberVariable(enumMemberName);
if (enumMemberVar == nullptr) {
return false;
}
if (!enumMemberVar->AsEnumVariable()->IsVisited()) {
auto *scope = enumMemberVar->AsEnumVariable()->Declaration()->
Node()->Parent()->AsTSEnumDeclaration()->Scope();
auto *initExpr = enumMemberVar->AsEnumVariable()->Declaration()->Node()->AsTSEnumMember()->Init();
if (IsStringInitForEnumMember(initExpr, scope)) {
enumMemberVar->AsEnumVariable()->SetStringInit();
}
enumMemberVar->AsEnumVariable()->SetVisited();
}
if (enumMemberVar->AsEnumVariable()->IsVisited() && enumMemberVar->AsEnumVariable()->StringInit()) {
return true;
}
return false;
}
case binder::VariableFlags::NAMESPACE: {
auto *exportTSBindings = currVar->AsNamespaceVariable()->GetExportBindings();
if (exportTSBindings != nullptr) {
ArenaVector<binder::Variable *> findRes(Allocator()->Adapter());
util::StringView name = GetNameForMemberExpressionItem(members.front());
binder::Variable *v = exportTSBindings->FindExportTSVariable<binder::TSBindingType::NAMESPACE>(name);
if (v != nullptr && IsInstantiatedNamespaceVariable(v)) {
findRes.push_back(v);
}
v = exportTSBindings->FindExportTSVariable<binder::TSBindingType::ENUMLITERAL>(name);
if (v != nullptr) {
findRes.push_back(v);
}
v = exportTSBindings->FindExportTSVariable<binder::TSBindingType::IMPORT_EQUALS>(name);
if (v != nullptr) {
findRes.push_back(v);
}
members.pop_front();
for (auto itemVar : findRes) {
if (VerifyMemberExpressionDeque(itemVar, members)) {
return true;
}
}
return false;
}
return false;
}
case binder::VariableFlags::IMPORT_EQUALS: {
auto *node = currVar->Declaration()->Node()->Parent()->AsTSImportEqualsDeclaration()->ModuleReference();
while (node->IsTSQualifiedName()) {
members.push_front(node->AsTSQualifiedName()->Right()->AsIdentifier());
node = node->AsTSQualifiedName()->Left();
}
members.push_front(node->AsIdentifier());
ArenaVector<binder::Variable *> findRes = FindFrontIdentifierTSVariables(
members.front()->AsIdentifier(), currVar->AsImportEqualsVariable()->GetScope());
members.pop_front();
for (auto itemVar : findRes) {
if (VerifyMemberExpressionDeque(itemVar, members)) {
return true;
}
}
return false;
}
default:
return false;
}
return false;
}
util::StringView Transformer::GetNameForMemberExpressionItem(const ir::Expression *node) const
{
util::StringView name {};
if (node->IsIdentifier()) {
name = node->AsIdentifier()->Name();
} else if (node->IsStringLiteral()) {
name = node->AsStringLiteral()->Str();
} else if (node->IsTemplateLiteral()) {
name = node->AsTemplateLiteral()->Quasis().front()->Raw();
}
return name;
}
util::StringView Transformer::GetNameFromEnumMember(const ir::TSEnumMember *node) const
{
util::StringView name {};
if (node->Key()->IsIdentifier()) {
name = node->Key()->AsIdentifier()->Name();
} else if (node->Key()->IsStringLiteral()) {
name = node->Key()->AsStringLiteral()->Str();
} else if (node->Key()->IsTemplateLiteral()) {
name = node->Key()->AsTemplateLiteral()->Quasis().front()->Cooked();
}
return name;
}
binder::Scope *Transformer::FindEnumMemberScope(const util::StringView name) const
{
auto scope = Scope();
while (scope != nullptr) {
if (scope->InLocalTSBindings(name)) {
return nullptr;
}
if (scope->IsTSModuleScope() && scope->AsTSModuleScope()->InExportBindings(name)) {
return nullptr;
}
if (scope->IsTSEnumScope() && scope->AsTSEnumScope()->FindEnumMemberVariable(name)) {
return scope;
}
if (scope->FindLocal(name)) {
return nullptr;
}
if (scope->IsTSModuleScope() || scope->IsTSEnumScope()) {
scope = scope->Parent();
}
scope = scope->Parent();
}
return nullptr;
}
ir::MemberExpression *Transformer::CreateMemberExpressionFromIdentifier(binder::Scope *scope, ir::Identifier *node)
{
auto identName = node->Name();
auto moduleName = scope->IsTSEnumScope() ? FindTSEnumNameByScope(scope) : FindTSModuleNameByScope(scope);
auto *id = CreateReferenceIdentifier(moduleName);
auto *res = AllocNode<ir::MemberExpression>(id, AllocNode<ir::Identifier>(identName),
ir::MemberExpression::MemberExpressionKind::PROPERTY_ACCESS,
false, false);
SetOriginalNode(res, node);
return res;
}
void Transformer::CheckTransformedAstStructure(const Program *program) const
{
bool passed = true;
CheckTransformedAstNodes(program->Ast(), &passed);
if (passed) {
std::cout << "Transformed AST structure check passed." << std::endl;
}
}
void Transformer::CheckTransformedAstNodes(const ir::AstNode *parent, bool *passed) const
{
parent->Iterate([this, parent, passed](auto *childNode) { CheckTransformedAstNode(parent, childNode, passed); });
}
void Transformer::CheckTransformedAstNode(const ir::AstNode *parent, ir::AstNode *childNode, bool *passed) const
{
if (!(*passed)) {
return;
}
if (childNode->IsClassProperty() &&
(childNode->AsClassProperty()->IsStatic() || childNode->AsClassProperty()->Value() != nullptr)) {
return;
}
if (childNode->IsMethodDefinition() &&
childNode->AsMethodDefinition()->Kind() == ir::MethodDefinitionKind::CONSTRUCTOR) {
return;
}
if (childNode->IsDecorator()) {
return;
}
if (childNode->Parent() != parent) {
std::cout << "Illegal ast structure after transform." << std::endl;
*passed = false;
return;
}
CheckTransformedAstNodes(childNode, passed);
}
void Transformer::ResetParentScopeForAstNodes(const ir::AstNode *parent, binder::Scope *parentScope) const
{
parent->Iterate([this, parentScope](auto *childNode) { ResetParentScopeForAstNode(childNode, parentScope); });
}
void Transformer::ResetParentScopeForAstNode(ir::AstNode *childNode, binder::Scope *parentScope) const
{
switch (childNode->Type()) {
case ir::AstNodeType::SCRIPT_FUNCTION: {
auto scope = childNode->AsScriptFunction()->Scope();
ASSERT(scope != nullptr);
scope->SetParent(parentScope);
break;
}
case ir::AstNodeType::CATCH_CLAUSE: {
auto scope = childNode->AsCatchClause()->Scope();
ASSERT(scope != nullptr);
scope->SetParent(parentScope);
break;
}
case ir::AstNodeType::CLASS_DEFINITION: {
auto scope = childNode->AsClassDefinition()->Scope();
ASSERT(scope != nullptr);
scope->SetParent(parentScope);
break;
}
case ir::AstNodeType::BLOCK_STATEMENT: {
auto scope = childNode->AsBlockStatement()->Scope();
ASSERT(scope != nullptr);
scope->SetParent(parentScope);
break;
}
case ir::AstNodeType::DO_WHILE_STATEMENT: {
auto scope = childNode->AsDoWhileStatement()->Scope();
ASSERT(scope != nullptr);
scope->SetParent(parentScope);
break;
}
case ir::AstNodeType::WHILE_STATEMENT: {
auto scope = childNode->AsWhileStatement()->Scope();
ASSERT(scope != nullptr);
scope->SetParent(parentScope);
break;
}
case ir::AstNodeType::FOR_IN_STATEMENT: {
auto scope = childNode->AsForInStatement()->Scope();
ASSERT(scope != nullptr);
scope->SetParent(parentScope);
break;
}
case ir::AstNodeType::FOR_OF_STATEMENT: {
auto scope = childNode->AsForOfStatement()->Scope();
ASSERT(scope != nullptr);
scope->SetParent(parentScope);
break;
}
case ir::AstNodeType::FOR_UPDATE_STATEMENT: {
auto scope = childNode->AsForUpdateStatement()->Scope();
ASSERT(scope != nullptr);
scope->SetParent(parentScope);
break;
}
case ir::AstNodeType::SWITCH_STATEMENT: {
auto scope = childNode->AsSwitchStatement()->Scope();
ASSERT(scope != nullptr);
scope->SetParent(parentScope);
break;
}
case ir::AstNodeType::TS_ENUM_DECLARATION: {
auto scope = childNode->AsTSEnumDeclaration()->Scope();
ASSERT(scope != nullptr);
scope->SetParent(parentScope);
break;
}
case ir::AstNodeType::TS_INTERFACE_DECLARATION: {
auto scope = childNode->AsTSInterfaceDeclaration()->Scope();
ASSERT(scope != nullptr);
scope->SetParent(parentScope);
break;
}
case ir::AstNodeType::TS_METHOD_SIGNATURE: {
auto scope = childNode->AsTSMethodSignature()->Scope();
ASSERT(scope != nullptr);
scope->SetParent(parentScope);
break;
}
case ir::AstNodeType::TS_MODULE_DECLARATION: {
auto scope = childNode->AsTSModuleDeclaration()->Scope();
ASSERT(scope != nullptr);
scope->SetParent(parentScope);
break;
}
case ir::AstNodeType::TS_SIGNATURE_DECLARATION: {
auto scope = childNode->AsTSSignatureDeclaration()->Scope();
ASSERT(scope != nullptr);
scope->SetParent(parentScope);
break;
}
case ir::AstNodeType::TS_TYPE_PARAMETER_DECLARATION: {
auto scope = childNode->AsTSTypeParameterDeclaration()->Scope();
ASSERT(scope != nullptr);
scope->SetParent(parentScope);
break;
}
case ir::AstNodeType::TS_CONSTRUCTOR_TYPE: {
auto scope = childNode->AsTSConstructorType()->Scope();
ASSERT(scope != nullptr);
scope->SetParent(parentScope);
break;
}
case ir::AstNodeType::TS_FUNCTION_TYPE: {
auto scope = childNode->AsTSFunctionType()->Scope();
ASSERT(scope != nullptr);
scope->SetParent(parentScope);
break;
}
default: {
ResetParentScopeForAstNodes(childNode, parentScope);
break;
}
}
}
bool Transformer::IsValueReference(ir::Identifier *node)
{
auto scope = Scope();
ASSERT(scope != nullptr);
auto name = node->Name();
if (scope->FindLocal(name, binder::ResolveBindingOptions::BINDINGS) != nullptr ||
scope->FindLocalTSVariable<binder::TSBindingType::ENUMLITERAL>(name) != nullptr) {
return true;
}
binder::Variable *var = nullptr;
var = scope->FindLocalTSVariable<binder::TSBindingType::NAMESPACE>(name);
if (var != nullptr) {
auto *decl = var->Declaration()->AsNamespaceDecl();
return decl->IsInstantiated();
}
var = scope->FindLocalTSVariable<binder::TSBindingType::IMPORT_EQUALS>(name);
if (var != nullptr) {
auto *node = var->Declaration()->Node()->AsTSImportEqualsDeclaration();
return IsInstantiatedImportEquals(node, scope);
}
return false;
}
void Transformer::RemoveDefaultLocalExportEntry()
{
auto *moduleRecord = GetSourceTextModuleRecord();
moduleRecord->RemoveDefaultLocalExportEntry();
}
void Transformer::ThrowStackOverflow(const lexer::SourcePosition &pos)
{
lexer::LineIndex index(program_->SourceCode());
lexer::SourceLocation loc = index.GetLocation(pos);
throw Error(ErrorType::GENERIC, "Transformer stack overflow", loc.line, loc.col);
}
}