* Copyright (c) 2025-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 "lateInitialization.h"
#include <sstream>
#include "checker/ETSchecker.h"
#include "compiler/lowering/util.h"
#include "ir/ets/etsUnionType.h"
namespace ark::es2panda::compiler {
using AstNodePtr = ir::AstNode *;
static ir::ClassProperty *TransformerClassProperty(public_lib::Context *ctx, ir::ClassProperty *property)
{
auto checker = ctx->GetChecker()->AsETSChecker();
auto allocator = ctx->allocator;
auto annotationType = checker->CreateETSUnionType({property->TsType(), checker->GlobalETSUndefinedType()});
auto typeAnnotation = allocator->New<ir::OpaqueTypeNode>(annotationType, allocator);
ES2PANDA_ASSERT(typeAnnotation);
typeAnnotation->SetParent(property);
typeAnnotation->SetTsType(annotationType);
property->SetTypeAnnotation(typeAnnotation);
property->SetTsType(annotationType);
property->Key()->Variable()->SetTsType(annotationType);
property->ClearModifier(ir::ModifierFlags::DEFINITE);
return property;
}
static ir::AstNode *TransformerMemberExpression(ir::MemberExpression *memberExpr, public_lib::Context *ctx)
{
auto checker = ctx->GetChecker()->AsETSChecker();
auto parser = ctx->parser->AsETSParser();
auto varbinder = ctx->GetChecker()->VarBinder()->AsETSBinder();
auto allocator = ctx->Allocator();
auto originalType = memberExpr->TsType();
auto parent = memberExpr->Parent();
if (parent->IsAssignmentExpression() && parent->AsAssignmentExpression()->Left() == memberExpr &&
parent->AsAssignmentExpression()->OperatorType() == lexer::TokenType::PUNCTUATOR_SUBSTITUTION) {
memberExpr->SetTsType(originalType);
parent->AsAssignmentExpression()->SetTsType(originalType);
return memberExpr;
}
ArenaVector<ir::Statement *> blockStatements(allocator->Adapter());
std::stringstream ss;
auto name = GenName(allocator);
auto *typeNode = allocator->New<ir::OpaqueTypeNode>(originalType, allocator);
blockStatements.push_back(parser->CreateFormattedStatement("let @@I1 = @@E2", name, memberExpr));
ss << "if (@@I1 === undefined) { throw new NullPointerError(); }";
blockStatements.push_back(parser->CreateFormattedStatement(ss.str(), name));
blockStatements.push_back(parser->CreateFormattedStatement("@@I1 as @@T2", name, typeNode));
auto *res = util::NodeAllocator::ForceSetParent<ir::BlockExpression>(allocator, std::move(blockStatements));
ES2PANDA_ASSERT(res);
res->SetParent(parent);
Recheck(ctx->phaseManager, varbinder, checker, res);
return res;
}
bool LateInitializationConvert::PerformForProgram(parser::Program *program)
{
program->Ast()->TransformChildrenRecursively(
[ctx = Context()](ir::AstNode *node) -> AstNodePtr {
if (node->IsMemberExpression()) {
auto property = node->AsMemberExpression()->Property();
if (!(property->IsIdentifier() && property->AsIdentifier()->Variable() != nullptr)) {
return node;
}
auto decl = property->AsIdentifier()->Variable()->Declaration();
if (decl != nullptr && decl->Node() != nullptr && decl->Node()->IsClassProperty() &&
decl->Node()->IsDefinite()) {
return TransformerMemberExpression(node->AsMemberExpression(), ctx);
}
}
return node;
},
Name());
program->Ast()->TransformChildrenRecursively(
[ctx = Context()](ir::AstNode *node) -> AstNodePtr {
if (node->IsClassProperty() && node->IsDefinite()) {
return TransformerClassProperty(ctx, node->AsClassProperty());
}
return node;
},
Name());
return true;
}
}