• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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