1/* 2 * Copyright (c) 2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import * as arkts from '@koalaui/libarkts'; 17import { getInteropPath } from '../../path'; 18const interop = require(getInteropPath()); 19const nullptr = interop.nullptr; 20import { EntryWrapperNames } from './utils'; 21import { annotation } from '../../common/arkts-utils'; 22import { factory as uiFactory } from '../ui-factory'; 23 24export class factory { 25 /** 26 * insert an 'entry' method to an entry wrapper class. 27 * 28 * @param node entry wrapper class declaration node. 29 */ 30 static registerEntryFunction(node: arkts.ClassDeclaration): arkts.AstNode { 31 const definition: arkts.ClassDefinition | undefined = node.definition; 32 const classname = node?.definition?.ident?.name; 33 if (!definition || !classname) { 34 throw new Error('Node definition is undefined'); 35 } 36 const updateClassDef: arkts.ClassDefinition = arkts.factory.updateClassDefinition( 37 definition, 38 definition.ident, 39 definition.typeParams, 40 definition.superTypeParams, 41 definition.implements, 42 undefined, 43 definition.super, 44 [...definition.body, factory.generateEntryFunction(classname)], 45 definition.modifiers, 46 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE 47 ); 48 return arkts.factory.updateClassDeclaration(node, updateClassDef); 49 } 50 51 /** 52 * insert an 'entry' property to an entry wrapper class. 53 * 54 * @param node entry wrapper class declaration node. 55 * @deprecated 56 */ 57 static registerEntryProperty(node: arkts.ClassDeclaration): arkts.AstNode { 58 const definition: arkts.ClassDefinition | undefined = node.definition; 59 const classname = node?.definition?.ident?.name; 60 if (!definition || !classname) { 61 throw new Error('Node definition is undefined'); 62 } 63 const updateClassDef: arkts.ClassDefinition = arkts.factory.updateClassDefinition( 64 definition, 65 definition.ident, 66 definition.typeParams, 67 definition.superTypeParams, 68 definition.implements, 69 undefined, 70 definition.super, 71 [...definition.body, factory.generateEntryProperty(classname)], 72 definition.modifiers, 73 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE 74 ); 75 return arkts.factory.updateClassDeclaration(node, updateClassDef); 76 } 77 78 /** 79 * create `entry(): void { <name>(); }` class method for the entry wrapper class, 80 * which calls the struct within the method. 81 * 82 * @param name class/struct name that has `@Entry` annotation. 83 */ 84 static generateEntryFunction(name: string): arkts.MethodDefinition { 85 const exp = arkts.factory.createExpressionStatement( 86 arkts.factory.createCallExpression( 87 arkts.factory.createIdentifier(name), 88 undefined, 89 [arkts.factory.createUndefinedLiteral()] // TODO: Add this undefined later 90 ) 91 ); 92 const key: arkts.Identifier = arkts.factory.createIdentifier(EntryWrapperNames.ENTRY_FUNC); 93 const block = arkts.factory.createBlock([exp]); 94 const entryScript = arkts.factory 95 .createScriptFunction( 96 block, 97 arkts.FunctionSignature.createFunctionSignature( 98 undefined, 99 [], 100 arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), 101 false 102 ), 103 arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, 104 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC 105 ) 106 .setIdent(key); 107 108 const def = arkts.factory.createMethodDefinition( 109 arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, 110 key, 111 entryScript, 112 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, 113 false 114 ); 115 116 return def; 117 } 118 119 static generateConstructor(): arkts.MethodDefinition { 120 const key: arkts.Identifier = arkts.factory.createIdentifier('constructor'); 121 const block = arkts.factory.createBlock([]); 122 const entryScript = arkts.factory 123 .createScriptFunction( 124 block, 125 arkts.FunctionSignature.createFunctionSignature(undefined, [], undefined, false), 126 arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_CONSTRUCTOR | 127 arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_IMPLICIT_SUPER_CALL_NEEDED, 128 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | 129 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_CONSTRUCTOR 130 ) 131 .setIdent(key); 132 const def = arkts.factory.createMethodDefinition( 133 arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR, 134 key, 135 entryScript, 136 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, 137 false 138 ); 139 return def; 140 } 141 142 /** 143 * create `entry = (): void => { <name>(); }` class property for the entry wrapper class, 144 * which calls the struct within the arrow function. 145 * 146 * @param name class/struct name that has `@Entry` annotation. 147 * @deprecated 148 */ 149 static generateEntryProperty(name: string): arkts.ClassProperty { 150 const exp = arkts.factory.createExpressionStatement( 151 arkts.factory.createCallExpression( 152 arkts.factory.createIdentifier(name), 153 undefined, 154 [arkts.factory.createUndefinedLiteral()] // TODO: Add this undefined later 155 ) 156 ); 157 const key: arkts.Identifier = arkts.factory.createIdentifier(EntryWrapperNames.ENTRY_FUNC); 158 const block: arkts.BlockStatement = arkts.factory.createBlock([exp]); 159 const signature: arkts.FunctionSignature = arkts.FunctionSignature.createFunctionSignature( 160 undefined, 161 [], 162 arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), 163 false 164 ); 165 const entryScript: arkts.ScriptFunction = arkts.factory 166 .createScriptFunction( 167 block, 168 signature, 169 arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, 170 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC 171 ) 172 .setIdent(key); 173 174 const def = arkts.factory.createClassProperty( 175 key, 176 arkts.factory.createArrowFunction(entryScript), 177 arkts.factory.createFunctionType(signature, arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW), 178 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, 179 false 180 ); 181 182 return def; 183 } 184 185 /** 186 * create `__EntryWrapper_Entry` entry wrapper class that contains an 'entry' method that 187 * calls the struct within the method. 188 * 189 * @param name class/struct name that has `@Entry` annotation. 190 */ 191 static generateEntryWrapper(name: string): arkts.ClassDeclaration { 192 const ctor = factory.generateConstructor(); 193 const definition: arkts.ClassDefinition = arkts.factory 194 .createClassDefinition( 195 arkts.factory.createIdentifier(EntryWrapperNames.WRAPPER_CLASS_NAME), 196 undefined, 197 undefined, 198 [], 199 undefined, 200 arkts.factory.createTypeReference( 201 arkts.factory.createTypeReferencePart( 202 arkts.factory.createIdentifier(EntryWrapperNames.ENTRY_POINT_CLASS_NAME) 203 ) 204 ), 205 [factory.generateEntryFunction(name), ctor], 206 arkts.Es2pandaClassDefinitionModifiers.CLASS_DEFINITION_MODIFIERS_CLASS_DECL | 207 arkts.Es2pandaClassDefinitionModifiers.CLASS_DEFINITION_MODIFIERS_DECLARATION | 208 arkts.Es2pandaClassDefinitionModifiers.CLASS_DEFINITION_MODIFIERS_ID_REQUIRED, 209 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE 210 ) 211 .setCtor(ctor as any); 212 const newClass = arkts.factory.createClassDeclaration(definition); 213 newClass.modifiers = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE; 214 return newClass; 215 } 216 217 /** 218 * add `@memo` to all class methods that are named 'entry'. 219 * 220 * @param node class declaration node 221 */ 222 static addMemoToEntryWrapperClassMethods(node: arkts.ClassDeclaration): void { 223 node.definition?.body.forEach((member) => { 224 if ( 225 arkts.isMethodDefinition(member) && 226 !!member.scriptFunction.id && 227 member.scriptFunction.id.name === EntryWrapperNames.ENTRY_FUNC 228 ) { 229 member.scriptFunction.setAnnotations([annotation('memo')]); 230 } 231 }); 232 } 233 234 /** 235 * add `@memo` to the class property's value (expecting an arrow function), where the property is named 'entry'. 236 * 237 * @param node class declaration node 238 * @deprecated 239 */ 240 static addMemoToEntryWrapperPropertyValue(node: arkts.ClassDeclaration): void { 241 node.definition?.body.forEach((member) => { 242 if ( 243 arkts.isClassProperty(member) && 244 !!member.value && 245 arkts.isArrowFunctionExpression(member.value) && 246 !!member.key && 247 arkts.isIdentifier(member.key) && 248 member.key.name === EntryWrapperNames.ENTRY_FUNC 249 ) { 250 member.setAnnotations([annotation('memo')]); 251 } 252 }); 253 } 254 255 /** 256 * create `private _entry_local_storage_ = <name>;` class property 257 * from `{storage: "<name>"}` in `@Entry`'s properties. 258 * 259 * @param annotation `@Entry` annotation. 260 */ 261 static createEntryLocalStorageInClass(property: arkts.ClassProperty) { 262 const value = property.value as arkts.StringLiteral; 263 return arkts.factory.createClassProperty( 264 arkts.factory.createIdentifier(EntryWrapperNames.ENTRY_STORAGE_LOCAL_STORAGE_PROPERTY_NAME), 265 arkts.factory.createIdentifier(value.str), 266 undefined, 267 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, 268 false 269 ); 270 } 271 272 /** 273 * create and insert `import { EntryPoint as EntryPoint } from "@ohos.arkui.UserView";` 274 * to the top of script's statements. 275 */ 276 static createAndInsertEntryPointImport(program?: arkts.Program) { 277 const source: arkts.StringLiteral = arkts.factory.create1StringLiteral(EntryWrapperNames.ENTRY_DEFAULT_IMPORT); 278 const imported: arkts.Identifier = arkts.factory.createIdentifier(EntryWrapperNames.ENTRY_POINT_CLASS_NAME); 279 // Insert this import at the top of the script's statements. 280 if (!program) { 281 throw Error('Failed to insert import: Transformer has no program'); 282 } 283 uiFactory.createAndInsertImportDeclaration( 284 source, 285 imported, 286 imported, 287 arkts.Es2pandaImportKinds.IMPORT_KINDS_VALUE, 288 program 289 ); 290 } 291} 292