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 { factory } from './memo-factory'; 18import { 19 hasMemoAnnotation, 20 hasMemoIntrinsicAnnotation, 21 parametrizedNodeHasReceiver, 22 isMemoTSTypeAliasDeclaration, 23} from './utils'; 24import { AbstractVisitor } from '../common/abstract-visitor'; 25 26function isScriptFunctionFromGetter(node: arkts.ScriptFunction): boolean { 27 return ( 28 !!node.parent && 29 !!node.parent.parent && 30 arkts.isFunctionExpression(node.parent) && 31 arkts.isMethodDefinition(node.parent.parent) && 32 node.parent.parent.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET 33 ); 34} 35 36function isScriptFunctionFromSetter(node: arkts.ScriptFunction): boolean { 37 return ( 38 !!node.parent && 39 !!node.parent.parent && 40 arkts.isFunctionExpression(node.parent) && 41 arkts.isMethodDefinition(node.parent.parent) && 42 node.parent.parent.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET 43 ); 44} 45 46export class SignatureTransformer extends AbstractVisitor { 47 /* Tracking whether should import `__memo_context_type` and `__memo_id_type` */ 48 public modified = false; 49 50 reset(): void { 51 super.reset(); 52 this.modified = false; 53 } 54 55 visitor<T extends arkts.AstNode>(node: T, applyMemo: boolean = false): T { 56 if (arkts.isScriptFunction(node)) { 57 const memo = hasMemoAnnotation(node) || hasMemoIntrinsicAnnotation(node) || applyMemo; 58 if (memo) { 59 this.modified = true; 60 } 61 const isFromGetter = isScriptFunctionFromGetter(node); 62 const isFromSetter = isScriptFunctionFromSetter(node); 63 const shouldAddMemoParam = memo && !isFromGetter && !isFromSetter; 64 const shouldApplyMemoToParamExpr = memo && isFromSetter; 65 const shouldApplyMemoToReturnType = memo && isFromGetter; 66 const newParams = node.params.map((it) => this.visitor(it, shouldApplyMemoToParamExpr)); 67 return arkts.factory.updateScriptFunction( 68 node, 69 node.body, 70 arkts.factory.createFunctionSignature( 71 node.typeParams, 72 shouldAddMemoParam 73 ? factory.createHiddenParameterIfNotAdded(newParams, parametrizedNodeHasReceiver(node)) 74 : newParams, 75 node.returnTypeAnnotation 76 ? this.visitor(node.returnTypeAnnotation, shouldApplyMemoToReturnType) 77 : memo 78 ? arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID) 79 : undefined, 80 node.hasReceiver 81 ), 82 node.flags, 83 node.modifiers 84 ) as any as T; 85 } 86 if (arkts.isEtsParameterExpression(node)) { 87 const memo = hasMemoAnnotation(node) || hasMemoIntrinsicAnnotation(node) || applyMemo; 88 if (!node.type) { 89 if (memo) { 90 console.error(`@memo parameter ${node.identifier.name} without type annotatation`); 91 throw 'Invalid @memo usage'; 92 } 93 return node; 94 } 95 node.type = this.visitor(node.type, memo); 96 return node as any as T; 97 } 98 if (arkts.isETSFunctionType(node)) { 99 const memo = hasMemoAnnotation(node) || hasMemoIntrinsicAnnotation(node) || applyMemo; 100 if (memo) { 101 this.modified = true; 102 } 103 const newParams = node.params.map((it) => this.visitor(it)); 104 return arkts.factory.updateFunctionType( 105 node, 106 arkts.factory.createFunctionSignature( 107 undefined, 108 memo ? factory.createHiddenParameterIfNotAdded(newParams) : newParams, 109 this.visitor(node.returnType!), 110 false 111 ), 112 arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW 113 ) as any as T; 114 } 115 if (arkts.isETSUnionType(node)) { 116 return arkts.factory.updateUnionType( 117 node, 118 node.types.map((it) => this.visitor(it, applyMemo)) 119 ) as any as T; 120 } 121 if (arkts.isETSUndefinedType(node)) { 122 return node as any as T; 123 } 124 if (arkts.isETSTypeReference(node) && applyMemo) { 125 if (!node.part || !node.part.name) { 126 console.error(`@memo parameter has no type reference`); 127 throw 'Invalid @memo usage'; 128 } 129 const expr = node.part.name; 130 const decl = arkts.getDecl(expr); 131 if (!decl || !arkts.isTSTypeAliasDeclaration(decl)) { 132 console.error(`@memo parameter's type has not been declared`); 133 throw 'Invalid @memo usage'; 134 } 135 const memoDecl = isMemoTSTypeAliasDeclaration(decl); 136 if (memoDecl) { 137 return node as any as T; 138 } 139 console.error(`@memo parameter type reference has no @memo type declaration`); 140 throw 'Invalid @memo usage'; 141 } 142 if (applyMemo) { 143 console.error(`@memo parameter's signature has invalid type`); 144 throw 'Invalid @memo usage'; 145 } 146 return node; 147 } 148} 149