1/** 2 * Copyright (c) 2023-2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "interfacePropertyDeclarations.h" 17 18#include "checker/ETSchecker.h" 19#include "checker/types/type.h" 20#include "compiler/lowering/util.h" 21#include "ir/astNode.h" 22#include "ir/expression.h" 23#include "ir/expressions/identifier.h" 24#include "ir/opaqueTypeNode.h" 25#include "ir/statements/blockStatement.h" 26#include "ir/ts/tsInterfaceBody.h" 27#include "ir/base/classProperty.h" 28#include "ir/ets/etsUnionType.h" 29#include "ir/ets/etsNullishTypes.h" 30 31namespace ark::es2panda::compiler { 32 33namespace { 34 35void TransformOptionalFieldTypeAnnotation(checker::ETSChecker *const checker, ir::ClassProperty *const field) 36{ 37 if (!field->IsOptionalDeclaration()) { 38 return; 39 } 40 41 if (field->IsETSUnionType()) { 42 bool alreadyHasUndefined = false; 43 auto unionTypes = field->AsETSUnionType()->Types(); 44 for (const auto &type : unionTypes) { 45 if (type->IsETSUndefinedType()) { 46 alreadyHasUndefined = true; 47 break; 48 } 49 } 50 if (!alreadyHasUndefined) { 51 ArenaVector<ir::TypeNode *> types(field->AsETSUnionType()->Types(), checker->Allocator()->Adapter()); 52 types.push_back(checker->AllocNode<ir::ETSUndefinedType>()); 53 auto *const unionType = checker->AllocNode<ir::ETSUnionType>(std::move(types)); 54 field->SetTypeAnnotation(unionType); 55 } 56 } else { 57 ArenaVector<ir::TypeNode *> types(checker->Allocator()->Adapter()); 58 types.push_back(field->TypeAnnotation()); 59 types.push_back(checker->AllocNode<ir::ETSUndefinedType>()); 60 auto *const unionType = checker->AllocNode<ir::ETSUnionType>(std::move(types)); 61 field->SetTypeAnnotation(unionType); 62 } 63 field->ClearModifier(ir::ModifierFlags::OPTIONAL); 64} 65 66} // namespace 67 68static ir::MethodDefinition *GenerateGetterOrSetter(checker::ETSChecker *const checker, ir::ClassProperty *const field, 69 bool isSetter) 70{ 71 auto classScope = NearestScope(field); 72 auto *paramScope = checker->Allocator()->New<varbinder::FunctionParamScope>(checker->Allocator(), classScope); 73 auto *functionScope = checker->Allocator()->New<varbinder::FunctionScope>(checker->Allocator(), paramScope); 74 75 functionScope->BindParamScope(paramScope); 76 paramScope->BindFunctionScope(functionScope); 77 78 auto flags = ir::ModifierFlags::PUBLIC; 79 flags |= ir::ModifierFlags::ABSTRACT; 80 81 TransformOptionalFieldTypeAnnotation(checker, field); 82 ArenaVector<ir::Expression *> params(checker->Allocator()->Adapter()); 83 84 if (isSetter) { 85 auto paramIdent = field->Key()->AsIdentifier()->Clone(checker->Allocator(), nullptr); 86 paramIdent->SetTsTypeAnnotation(field->TypeAnnotation()->Clone(checker->Allocator(), nullptr)); 87 paramIdent->TypeAnnotation()->SetParent(paramIdent); 88 89 auto *const paramExpression = checker->AllocNode<ir::ETSParameterExpression>(paramIdent, nullptr); 90 paramExpression->SetRange(paramIdent->Range()); 91 auto *const paramVar = std::get<2>(paramScope->AddParamDecl(checker->Allocator(), paramExpression)); 92 93 paramIdent->SetVariable(paramVar); 94 paramExpression->SetVariable(paramVar); 95 96 params.push_back(paramExpression); 97 } 98 99 auto signature = ir::FunctionSignature(nullptr, std::move(params), isSetter ? nullptr : field->TypeAnnotation()); 100 101 auto *func = checker->AllocNode<ir::ScriptFunction>( 102 checker->Allocator(), ir::ScriptFunction::ScriptFunctionData {nullptr, std::move(signature), 103 isSetter ? ir::ScriptFunctionFlags::SETTER 104 : ir::ScriptFunctionFlags::GETTER, 105 flags, true}); 106 107 func->SetRange(field->Range()); 108 109 func->SetScope(functionScope); 110 111 auto const &name = field->Key()->AsIdentifier()->Name(); 112 auto methodIdent = checker->AllocNode<ir::Identifier>(name, checker->Allocator()); 113 auto *decl = checker->Allocator()->New<varbinder::VarDecl>(name); 114 auto var = functionScope->AddDecl(checker->Allocator(), decl, ScriptExtension::ETS); 115 116 methodIdent->SetVariable(var); 117 118 auto *funcExpr = checker->AllocNode<ir::FunctionExpression>(func); 119 funcExpr->SetRange(func->Range()); 120 func->AddFlag(ir::ScriptFunctionFlags::METHOD); 121 122 auto *method = checker->AllocNode<ir::MethodDefinition>(ir::MethodDefinitionKind::METHOD, methodIdent, funcExpr, 123 flags, checker->Allocator(), false); 124 125 method->Id()->SetMutator(); 126 method->SetRange(field->Range()); 127 method->Function()->SetIdent(method->Id()->Clone(checker->Allocator(), nullptr)); 128 method->Function()->AddModifier(method->Modifiers()); 129 paramScope->BindNode(func); 130 functionScope->BindNode(func); 131 132 return method; 133} 134 135static ir::Expression *UpdateInterfacePropertys(checker::ETSChecker *const checker, 136 ir::TSInterfaceBody *const interface) 137{ 138 if (interface->Body().empty()) { 139 return interface; 140 } 141 142 auto propertyList = interface->Body(); 143 ArenaVector<ir::AstNode *> newPropertyList(checker->Allocator()->Adapter()); 144 145 auto scope = NearestScope(interface); 146 ASSERT(scope->IsClassScope()); 147 148 for (const auto &prop : propertyList) { 149 if (!prop->IsClassProperty()) { 150 newPropertyList.emplace_back(prop); 151 continue; 152 } 153 auto getter = GenerateGetterOrSetter(checker, prop->AsClassProperty(), false); 154 newPropertyList.emplace_back(getter); 155 156 auto methodScope = scope->AsClassScope()->InstanceMethodScope(); 157 auto name = getter->Key()->AsIdentifier()->Name(); 158 159 auto *decl = checker->Allocator()->New<varbinder::FunctionDecl>(checker->Allocator(), name, getter); 160 161 if (methodScope->AddDecl(checker->Allocator(), decl, ScriptExtension::ETS) == nullptr) { 162 auto prevDecl = methodScope->FindDecl(name); 163 ASSERT(prevDecl->IsFunctionDecl()); 164 prevDecl->Node()->AsMethodDefinition()->AddOverload(getter); 165 166 if (!prop->AsClassProperty()->IsReadonly()) { 167 auto setter = GenerateGetterOrSetter(checker, prop->AsClassProperty(), true); 168 newPropertyList.emplace_back(setter); 169 prevDecl->Node()->AsMethodDefinition()->AddOverload(setter); 170 } 171 172 getter->Function()->Id()->SetVariable( 173 methodScope->FindLocal(name, varbinder::ResolveBindingOptions::BINDINGS)); 174 continue; 175 } 176 177 if (!prop->AsClassProperty()->IsReadonly()) { 178 auto setter = GenerateGetterOrSetter(checker, prop->AsClassProperty(), true); 179 newPropertyList.emplace_back(setter); 180 getter->AddOverload(setter); 181 } 182 scope->AsClassScope()->InstanceFieldScope()->EraseBinding(name); 183 } 184 185 auto newInterface = checker->AllocNode<ir::TSInterfaceBody>(std::move(newPropertyList)); 186 newInterface->SetRange(interface->Range()); 187 newInterface->SetParent(interface->Parent()); 188 189 return newInterface; 190} 191 192bool InterfacePropertyDeclarationsPhase::Perform(public_lib::Context *ctx, parser::Program *program) 193{ 194 for (const auto &[_, ext_programs] : program->ExternalSources()) { 195 (void)_; 196 for (auto *const extProg : ext_programs) { 197 Perform(ctx, extProg); 198 } 199 } 200 201 checker::ETSChecker *const checker = ctx->checker->AsETSChecker(); 202 203 program->Ast()->TransformChildrenRecursively( 204 [checker](ir::AstNode *const ast) -> ir::AstNode * { 205 return ast->IsTSInterfaceBody() ? UpdateInterfacePropertys(checker, ast->AsTSInterfaceBody()) : ast; 206 }, 207 Name()); 208 209 return true; 210} 211 212} // namespace ark::es2panda::compiler 213