1/* 2 * Copyright (c) 2022-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 { 18 fixGensymParams, 19 buildeParamInfos, 20 isUnmemoizedInFunction, 21 mayAddLastReturn, 22 ParamInfo, 23 ReturnTypeInfo, 24 RuntimeNames, 25 parametrizedNodeHasReceiver 26} from './utils'; 27import { moveToFront } from '../common/arkts-utils'; 28 29export class factory { 30 // Importing 31 static createContextTypeImportSpecifier(): arkts.ImportSpecifier { 32 return arkts.factory.createImportSpecifier( 33 arkts.factory.createIdentifier(RuntimeNames.CONTEXT_TYPE), 34 arkts.factory.createIdentifier(RuntimeNames.CONTEXT_TYPE) 35 ); 36 } 37 static createIdTypeImportSpecifier(): arkts.ImportSpecifier { 38 return arkts.factory.createImportSpecifier( 39 arkts.factory.createIdentifier(RuntimeNames.ID_TYPE), 40 arkts.factory.createIdentifier(RuntimeNames.ID_TYPE) 41 ); 42 } 43 44 static createContextTypesImportDeclaration(program?: arkts.Program): void { 45 const source: arkts.StringLiteral = arkts.factory.createStringLiteral(RuntimeNames.MEMO_IMPORT_NAME); 46 const importDecl: arkts.ETSImportDeclaration = arkts.factory.createImportDeclaration( 47 source, 48 [factory.createContextTypeImportSpecifier(), factory.createIdTypeImportSpecifier()], 49 arkts.Es2pandaImportKinds.IMPORT_KINDS_TYPE, 50 program!, 51 arkts.Es2pandaImportFlags.IMPORT_FLAGS_NONE 52 ); 53 // Insert this import at the top of the script's statements. 54 if (!program) { 55 throw Error('Failed to insert import: Transformer has no program'); 56 } 57 arkts.importDeclarationInsert(importDecl, program); 58 return; 59 } 60 61 // Parameters 62 static createContextParameter(): arkts.ETSParameterExpression { 63 return arkts.factory.createParameterDeclaration( 64 arkts.factory.createIdentifier( 65 RuntimeNames.CONTEXT, 66 arkts.factory.createTypeReference( 67 arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier(RuntimeNames.CONTEXT_TYPE)) 68 ) 69 ), 70 undefined 71 ); 72 } 73 static createIdParameter(): arkts.ETSParameterExpression { 74 return arkts.factory.createParameterDeclaration( 75 arkts.factory.createIdentifier( 76 RuntimeNames.ID, 77 arkts.factory.createTypeReference( 78 arkts.factory.createTypeReferencePart(arkts.factory.createIdentifier(RuntimeNames.ID_TYPE)) 79 ) 80 ), 81 undefined 82 ); 83 } 84 static createHiddenParameters(): arkts.ETSParameterExpression[] { 85 return [factory.createContextParameter(), factory.createIdParameter()]; 86 } 87 static createHiddenParameterIfNotAdded( 88 params: readonly arkts.Expression[], 89 hasReceiver: boolean = false 90 ): readonly arkts.Expression[] { 91 const _params = params ?? []; 92 if (isUnmemoizedInFunction(_params)) { 93 return _params; 94 } 95 let newParams: arkts.Expression[] = [...factory.createHiddenParameters(), ..._params]; 96 if (hasReceiver) { 97 newParams = moveToFront(newParams, 2); 98 } 99 return newParams; 100 } 101 static updateFunctionTypeWithMemoParameters(type: arkts.ETSFunctionType): arkts.ETSFunctionType { 102 return arkts.factory.updateFunctionType( 103 type, 104 arkts.factory.createFunctionSignature( 105 undefined, 106 factory.createHiddenParameterIfNotAdded(type.params), 107 type.returnType, 108 false 109 ), 110 arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW 111 ); 112 } 113 static updateScriptFunctionWithMemoParameters( 114 func: arkts.ScriptFunction, 115 newBody?: arkts.AstNode | undefined, 116 returnType?: arkts.TypeNode | undefined 117 ): arkts.ScriptFunction { 118 return arkts.factory.updateScriptFunction( 119 func, 120 newBody ?? func.body, 121 arkts.factory.createFunctionSignature( 122 func.typeParams, 123 factory.createHiddenParameterIfNotAdded(func.params, parametrizedNodeHasReceiver(func)), 124 returnType ?? func.returnTypeAnnotation, 125 func.hasReceiver 126 ), 127 func.flags, 128 func.modifiers 129 ); 130 } 131 132 // Arguments 133 static createContextArgument(): arkts.AstNode { 134 return arkts.factory.createIdentifier(RuntimeNames.CONTEXT); 135 } 136 static createIdArgument(hash: arkts.NumberLiteral | arkts.StringLiteral): arkts.AstNode { 137 return arkts.factory.createBinaryExpression( 138 arkts.factory.createIdentifier(RuntimeNames.ID), 139 hash, 140 arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_PLUS 141 ); 142 } 143 static createHiddenArguments(hash: arkts.NumberLiteral | arkts.StringLiteral): arkts.AstNode[] { 144 return [factory.createContextArgument(), factory.createIdArgument(hash)]; 145 } 146 147 // Memo parameters 148 static createMemoParameterIdentifier(name: string): arkts.Identifier { 149 if (name === RuntimeNames.EQUAL_T) { 150 return arkts.factory.createIdentifier(`${RuntimeNames.PARAMETER}_${RuntimeNames.THIS}`, undefined); 151 } 152 return arkts.factory.createIdentifier(`${RuntimeNames.PARAMETER}_${name}`); 153 } 154 static createMemoParameterDeclarator(id: number, name: string): arkts.VariableDeclarator { 155 const originalIdent = 156 name === RuntimeNames.THIS || name === RuntimeNames.EQUAL_T 157 ? arkts.factory.createThisExpression() 158 : arkts.factory.createIdentifier(name, undefined); 159 return arkts.factory.createVariableDeclarator( 160 arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_CONST, 161 factory.createMemoParameterIdentifier(name), 162 arkts.factory.createCallExpression( 163 arkts.factory.createMemberExpression( 164 arkts.factory.createIdentifier(RuntimeNames.SCOPE), 165 arkts.factory.createIdentifier(RuntimeNames.INTERNAL_PARAMETER_STATE), 166 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 167 false, 168 false 169 ), 170 undefined, 171 [arkts.factory.createNumericLiteral(id), originalIdent] 172 ) 173 ); 174 } 175 static createMemoParameterDeclaration(parameters: string[]): arkts.VariableDeclaration { 176 return arkts.factory.createVariableDeclaration( 177 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, 178 arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_CONST, 179 parameters.map((name, id) => { 180 return factory.createMemoParameterDeclarator(id, name); 181 }) 182 ); 183 } 184 static createMemoParameterAccess(name: string): arkts.MemberExpression { 185 return arkts.factory.createMemberExpression( 186 factory.createMemoParameterIdentifier(name), 187 arkts.factory.createIdentifier(RuntimeNames.VALUE), 188 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_GETTER, 189 false, 190 false 191 ); 192 } 193 static createMemoParameterAccessCall(name: string, passArgs?: arkts.AstNode[]): arkts.CallExpression { 194 const updatedArgs = passArgs ? passArgs : []; 195 return arkts.factory.createCallExpression( 196 arkts.factory.createMemberExpression( 197 factory.createMemoParameterIdentifier(name), 198 arkts.factory.createIdentifier(RuntimeNames.VALUE), 199 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_GETTER, 200 false, 201 false 202 ), 203 undefined, 204 [...updatedArgs] 205 ); 206 } 207 208 // Recache 209 static createScopeDeclaration( 210 returnTypeAnnotation: arkts.TypeNode | undefined, 211 hash: arkts.NumberLiteral | arkts.StringLiteral, 212 cnt: number 213 ): arkts.VariableDeclaration { 214 return arkts.factory.createVariableDeclaration( 215 0, 216 arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_CONST, 217 [ 218 arkts.factory.createVariableDeclarator( 219 arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_CONST, 220 arkts.factory.createIdentifier(RuntimeNames.SCOPE), 221 arkts.factory.createCallExpression( 222 arkts.factory.createMemberExpression( 223 arkts.factory.createIdentifier(RuntimeNames.CONTEXT), 224 arkts.factory.createIdentifier(RuntimeNames.INTERNAL_SCOPE), 225 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 226 false, 227 false 228 ), 229 returnTypeAnnotation 230 ? [returnTypeAnnotation] 231 : [arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID)], 232 [factory.createIdArgument(hash), arkts.factory.createNumericLiteral(cnt)] 233 ) 234 ), 235 ] 236 ); 237 } 238 static createRecacheCall(arg?: arkts.AstNode): arkts.CallExpression { 239 return arkts.factory.createCallExpression( 240 arkts.factory.createMemberExpression( 241 arkts.factory.createIdentifier(RuntimeNames.SCOPE), 242 arkts.factory.createIdentifier(RuntimeNames.INTERNAL_VALUE_NEW), 243 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 244 false, 245 false 246 ), 247 undefined, 248 arg ? [arg] : undefined 249 ); 250 } 251 static createReturnThis(): arkts.BlockStatement { 252 return arkts.factory.createBlock([ 253 arkts.factory.createExpressionStatement(factory.createRecacheCall()), 254 arkts.factory.createReturnStatement(arkts.factory.createThisExpression()), 255 ]); 256 } 257 static createSyntheticReturnStatement(stableThis: boolean): arkts.ReturnStatement | arkts.BlockStatement { 258 if (!stableThis) { 259 return arkts.factory.createReturnStatement( 260 arkts.factory.createMemberExpression( 261 arkts.factory.createIdentifier(RuntimeNames.SCOPE), 262 arkts.factory.createIdentifier(RuntimeNames.INTERNAL_VALUE), 263 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_NONE, 264 false, 265 false 266 ) 267 ); 268 } 269 return arkts.factory.createBlock([ 270 arkts.factory.createMemberExpression( 271 arkts.factory.createIdentifier(RuntimeNames.SCOPE), 272 arkts.factory.createIdentifier(RuntimeNames.INTERNAL_VALUE), 273 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_NONE, 274 false, 275 false 276 ), 277 arkts.factory.createReturnStatement(arkts.factory.createThisExpression()), 278 ]); 279 } 280 static createIfStatementWithSyntheticReturnStatement( 281 syntheticReturnStatement: arkts.ReturnStatement | arkts.BlockStatement, 282 isVoidValue: boolean 283 ): arkts.IfStatement { 284 let returnStatement = syntheticReturnStatement; 285 if (isVoidValue && arkts.isReturnStatement(syntheticReturnStatement)) { 286 returnStatement = arkts.factory.createBlock([ 287 arkts.factory.createExpressionStatement(syntheticReturnStatement.argument!), 288 arkts.factory.createReturnStatement(), 289 ]); 290 } 291 return arkts.factory.createIfStatement( 292 arkts.factory.createMemberExpression( 293 arkts.factory.createIdentifier(RuntimeNames.SCOPE), 294 arkts.factory.createIdentifier(RuntimeNames.INTERNAL_VALUE_OK), 295 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_NONE, 296 false, 297 false 298 ), 299 returnStatement 300 ); 301 } 302 303 // Compute 304 static createLambdaWrapper(node: arkts.Expression): arkts.ArrowFunctionExpression { 305 return arkts.factory.createArrowFunction( 306 arkts.factory.createScriptFunction( 307 arkts.factory.createBlock([arkts.factory.createReturnStatement(node)]), 308 arkts.factory.createFunctionSignature(undefined, [], undefined, false), 309 arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, 310 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE 311 ) 312 ); 313 } 314 static createComputeExpression( 315 hash: arkts.NumberLiteral | arkts.StringLiteral, 316 node: arkts.Expression 317 ): arkts.CallExpression { 318 return arkts.factory.createCallExpression( 319 arkts.factory.createMemberExpression( 320 arkts.factory.createIdentifier(RuntimeNames.CONTEXT), 321 arkts.factory.createIdentifier(RuntimeNames.COMPUTE), 322 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 323 false, 324 false 325 ), 326 undefined, 327 [factory.createIdArgument(hash), factory.createLambdaWrapper(node)] 328 ); 329 } 330 331 static updateFunctionBody( 332 node: arkts.BlockStatement, 333 parameters: arkts.ETSParameterExpression[], 334 returnTypeInfo: ReturnTypeInfo, 335 hash: arkts.NumberLiteral | arkts.StringLiteral 336 ): [ 337 arkts.BlockStatement, 338 ParamInfo[], 339 arkts.VariableDeclaration | undefined, 340 arkts.ReturnStatement | arkts.BlockStatement | undefined 341 ] { 342 const paramInfos = buildeParamInfos(parameters); 343 const gensymParamsCount = fixGensymParams(paramInfos, node); 344 const parameterNames = paramInfos.map((it) => it.ident.name); 345 const scopeDeclaration = factory.createScopeDeclaration(returnTypeInfo.node, hash, parameterNames.length); 346 const memoParametersDeclaration = parameterNames.length 347 ? factory.createMemoParameterDeclaration(parameterNames) 348 : undefined; 349 const syntheticReturnStatement = factory.createSyntheticReturnStatement(!!returnTypeInfo.isStableThis); 350 const isVoidValue = !!returnTypeInfo.isVoid; 351 const unchangedCheck = factory.createIfStatementWithSyntheticReturnStatement( 352 syntheticReturnStatement, 353 isVoidValue 354 ); 355 return [ 356 arkts.factory.updateBlock(node, [ 357 ...node.statements.slice(0, gensymParamsCount), 358 scopeDeclaration, 359 ...(memoParametersDeclaration ? [memoParametersDeclaration] : []), 360 unchangedCheck, 361 ...node.statements.slice(gensymParamsCount), 362 ...(mayAddLastReturn(node) ? [arkts.factory.createReturnStatement()] : []), 363 ]), 364 paramInfos, 365 memoParametersDeclaration, 366 syntheticReturnStatement, 367 ]; 368 } 369 370 static updateMemoTypeAnnotation(typeAnnotation: arkts.AstNode | undefined): arkts.TypeNode | undefined { 371 if (!typeAnnotation || !arkts.isTypeNode(typeAnnotation)) { 372 return undefined; 373 } 374 375 if (arkts.isETSFunctionType(typeAnnotation)) { 376 return factory.updateFunctionTypeWithMemoParameters(typeAnnotation); 377 } else if (arkts.isETSUnionType(typeAnnotation)) { 378 return arkts.factory.updateUnionType( 379 typeAnnotation, 380 typeAnnotation.types.map((it) => { 381 if (arkts.isETSFunctionType(it)) { 382 return factory.updateFunctionTypeWithMemoParameters(it); 383 } 384 return it; 385 }) 386 ); 387 } 388 return typeAnnotation; 389 } 390 391 static insertHiddenArgumentsToCall( 392 node: arkts.CallExpression, 393 hash: arkts.NumberLiteral | arkts.StringLiteral 394 ): arkts.CallExpression { 395 return arkts.factory.updateCallExpression(node, node.expression, node.typeArguments, [ 396 ...factory.createHiddenArguments(hash), 397 ...node.arguments, 398 ]); 399 } 400} 401