• 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 { AbstractVisitor, VisitorOptions } from '../common/abstract-visitor';
19import {
20    MemoInfo,
21    PositionalIdTracker,
22    ReturnTypeInfo,
23    buildReturnTypeInfo,
24    castArrowFunctionExpression,
25    castIdentifier,
26    castOverloadsToMethods,
27    castParameters,
28    findMemoFromTypeAnnotation,
29    findThisAttribute,
30    getDeclResolveAlias,
31    hasMemoAnnotation,
32    hasMemoEntryAnnotation,
33    hasMemoIntrinsicAnnotation,
34    hasMemoStableAnnotation,
35    isDeclaredMethodWithMemoParams,
36    isFunctionProperty,
37    isMemoArrowFunction,
38    isMemoClassProperty,
39    isMemoDeclaredClassProperty,
40    isMemoDeclaredIdentifier,
41    isMemoDeclaredMethod,
42    isMemoETSParameterExpression,
43    isMemoMethodDefinition,
44    isMemoProperty,
45    isMemoTSTypeAliasDeclaration,
46    isMemoThisAttribute,
47    isMemoVariableDeclarator,
48    isStandaloneArrowFunction,
49    isThisAttributeAssignment,
50    removeMemoAnnotation,
51    parametrizedNodeHasReceiver
52} from './utils';
53import { ParameterTransformer } from './parameter-transformer';
54import { ReturnTransformer } from './return-transformer';
55import { SignatureTransformer } from './signature-transformer';
56import { moveToFront } from '../common/arkts-utils';
57import { InternalsTransformer } from './internal-transformer';
58
59interface ScopeInfo extends MemoInfo {
60    regardAsSameScope?: boolean;
61}
62
63export interface FunctionTransformerOptions extends VisitorOptions {
64    positionalIdTracker: PositionalIdTracker;
65    parameterTransformer: ParameterTransformer;
66    returnTransformer: ReturnTransformer;
67    signatureTransformer: SignatureTransformer;
68    internalsTransformer?: InternalsTransformer;
69}
70
71export class FunctionTransformer extends AbstractVisitor {
72    private readonly positionalIdTracker: PositionalIdTracker;
73    private readonly parameterTransformer: ParameterTransformer;
74    private readonly returnTransformer: ReturnTransformer;
75    private readonly signatureTransformer: SignatureTransformer;
76    private readonly internalsTransformer?: InternalsTransformer;
77
78    /* Tracking whether should import `__memo_context_type` and `__memo_id_type` */
79    private modified = false;
80
81    constructor(options: FunctionTransformerOptions) {
82        super(options);
83        this.positionalIdTracker = options.positionalIdTracker;
84        this.parameterTransformer = options.parameterTransformer;
85        this.returnTransformer = options.returnTransformer;
86        this.signatureTransformer = options.signatureTransformer;
87        this.internalsTransformer = options.internalsTransformer;
88    }
89
90    private scopes: ScopeInfo[] = [];
91    private stable: number = 0;
92
93    reset() {
94        super.reset();
95        this.scopes = [];
96        this.stable = 0;
97        this.modified = false;
98        this.parameterTransformer.reset();
99        this.returnTransformer.reset();
100        this.signatureTransformer.reset();
101    }
102
103    private enterMethod(node: arkts.MethodDefinition): void {
104        const name = node.name.name;
105        const isMemo = isMemoMethodDefinition(node);
106        this.scopes.push({ name, isMemo });
107    }
108
109    private enterClassPropety(node: arkts.ClassProperty): void {
110        const name = castIdentifier(node.key).name;
111        const isMemo = isMemoClassProperty(node);
112        this.scopes.push({ name, isMemo });
113    }
114
115    private enterStandaloneArrowFunction(node: arkts.ArrowFunctionExpression): void {
116        const name = undefined;
117        const isMemo = isMemoArrowFunction(node);
118        this.scopes.push({ name, isMemo });
119    }
120
121    private enterTSTypeAliasDeclaration(node: arkts.TSTypeAliasDeclaration): void {
122        const name = castIdentifier(node.id).name;
123        const isMemo = isMemoTSTypeAliasDeclaration(node);
124        this.scopes.push({ name, isMemo });
125    }
126
127    private enterVariableDeclarator(node: arkts.VariableDeclarator): void {
128        const name = node.name.name;
129        const isMemo = isMemoVariableDeclarator(node);
130        this.scopes.push({ name, isMemo, regardAsSameScope: !!node.initializer });
131    }
132
133    private enterTSAsExpression(node: arkts.TSAsExpression): void {
134        const isMemo = findMemoFromTypeAnnotation(node.typeAnnotation);
135        this.scopes.push({ isMemo });
136    }
137
138    private enterFunctionProperty(node: arkts.Property): void {
139        const name = castIdentifier(node.key).name;
140        const isMemo = isMemoProperty(node, castArrowFunctionExpression(node.value));
141        this.scopes.push({ name, isMemo });
142    }
143
144    private enterThisAttributeAssignment(node: arkts.AssignmentExpression): void {
145        const thisAttribute = findThisAttribute(node.left!)!;
146        const name = thisAttribute.name;
147        const isMemo = isMemoThisAttribute(thisAttribute, castArrowFunctionExpression(node.right));
148        this.scopes.push({ name, isMemo });
149    }
150
151    enter(node: arkts.AstNode): this {
152        if (arkts.isMethodDefinition(node)) {
153            this.enterMethod(node);
154        }
155        if (arkts.isClassProperty(node) && !!node.key && arkts.isIdentifier(node.key)) {
156            this.enterClassPropety(node);
157        }
158        if (isStandaloneArrowFunction(node)) {
159            this.enterStandaloneArrowFunction(node);
160        }
161        if (arkts.isTSTypeAliasDeclaration(node) && !!node.id && !!node.typeAnnotation) {
162            this.enterTSTypeAliasDeclaration(node);
163        }
164        if (arkts.isVariableDeclarator(node)) {
165            this.enterVariableDeclarator(node);
166        }
167        if (arkts.isTSAsExpression(node) && !!node.expr && arkts.isArrowFunctionExpression(node.expr)) {
168            this.enterTSAsExpression(node);
169        }
170        if (isFunctionProperty(node)) {
171            this.enterFunctionProperty(node);
172        }
173        if (isThisAttributeAssignment(node) && !!node.right && arkts.isArrowFunctionExpression(node.right)) {
174            this.enterThisAttributeAssignment(node);
175        }
176        if (arkts.isClassDefinition(node)) {
177            if (hasMemoStableAnnotation(node)) {
178                this.stable++;
179            }
180        }
181        return this;
182    }
183
184    exit(node: arkts.AstNode) {
185        if (arkts.isMethodDefinition(node)) {
186            this.scopes.pop();
187        }
188        if (arkts.isClassDefinition(node)) {
189            if (hasMemoStableAnnotation(node)) {
190                this.stable--;
191            }
192        }
193        return this;
194    }
195
196    enterAnonymousScope(node: arkts.ScriptFunction) {
197        const name = undefined;
198        const isMemo = hasMemoAnnotation(node) || hasMemoIntrinsicAnnotation(node);
199        this.scopes.push({ name, isMemo });
200        return this;
201    }
202
203    exitAnonymousScope() {
204        this.scopes.pop();
205        return this;
206    }
207
208    checkMemoCallInMethod(decl: arkts.MethodDefinition) {
209        const scope = this.scopes[this.scopes.length - 1];
210        if (scope?.regardAsSameScope === false && scope?.isMemo === false) {
211            if (scope.name) {
212                console.error(`Attempt to call @memo-method ${decl.name.name} from non-@memo-method ${scope.name}`);
213                throw 'Invalid @memo usage';
214            } else {
215                console.error(`Attempt to call @memo-method ${decl.name.name} from anonymous non-@memo-method`);
216                throw 'Invalid @memo usage';
217            }
218        }
219        return this;
220    }
221
222    checkMemoCallInFunction() {
223        const scope = this.scopes[this.scopes.length - 1];
224        if (scope?.regardAsSameScope === false && scope?.isMemo === false) {
225            console.error(`Attempt to call @memo-function`);
226            throw 'Invalid @memo usage';
227        }
228        return this;
229    }
230
231    updateInternalsInScriptFunction(scriptFunction: arkts.ScriptFunction): arkts.ScriptFunction {
232        if (!scriptFunction.body || !arkts.isBlockStatement(scriptFunction.body) || !this.internalsTransformer) {
233            return scriptFunction;
234        }
235        const afterInternalsTransformer = this.internalsTransformer.visitor(
236            scriptFunction.body
237        ) as arkts.BlockStatement;
238        return arkts.factory.updateScriptFunction(
239            scriptFunction,
240            afterInternalsTransformer,
241            arkts.factory.createFunctionSignature(
242                scriptFunction.typeParams,
243                scriptFunction.params,
244                scriptFunction.returnTypeAnnotation,
245                scriptFunction.hasReceiver
246            ),
247            scriptFunction.flags,
248            scriptFunction.modifiers
249        );
250    }
251
252    updateScriptFunction(scriptFunction: arkts.ScriptFunction, name: string = ''): arkts.ScriptFunction {
253        if (!scriptFunction.body || !arkts.isBlockStatement(scriptFunction.body)) {
254            return scriptFunction;
255        }
256        if (this.parameterTransformer.isTracked(scriptFunction.body)) {
257            return this.updateInternalsInScriptFunction(scriptFunction);
258        }
259        if (hasMemoIntrinsicAnnotation(scriptFunction) || hasMemoEntryAnnotation(scriptFunction)) {
260            return this.updateInternalsInScriptFunction(scriptFunction);
261        }
262        const returnType = scriptFunction.returnTypeAnnotation;
263        const isStableThis = this.stable > 0 && returnType !== undefined && arkts.isTSThisType(returnType);
264        const returnTypeInfo: ReturnTypeInfo = buildReturnTypeInfo(
265            returnType,
266            findMemoFromTypeAnnotation(returnType),
267            isStableThis
268        );
269        const [body, parameterIdentifiers, memoParametersDeclaration, syntheticReturnStatement] =
270            factory.updateFunctionBody(
271                scriptFunction.body,
272                castParameters(scriptFunction.params),
273                returnTypeInfo,
274                this.positionalIdTracker.id(name)
275            );
276        const afterParameterTransformer = this.parameterTransformer
277            .withParameters(parameterIdentifiers)
278            .skip(memoParametersDeclaration)
279            .visitor(body);
280        const afterReturnTransformer = this.returnTransformer
281            .skip(syntheticReturnStatement)
282            .registerReturnTypeInfo(returnTypeInfo)
283            .rewriteThis(this.stable > 0)
284            .visitor(afterParameterTransformer);
285        const updateScriptFunction = factory.updateScriptFunctionWithMemoParameters(
286            scriptFunction,
287            afterReturnTransformer,
288            returnTypeInfo.node
289        );
290        this.modified = true;
291        this.parameterTransformer.track(updateScriptFunction.body);
292        return this.updateInternalsInScriptFunction(updateScriptFunction);
293    }
294
295    private updateMethodDefinition(node: arkts.MethodDefinition): arkts.MethodDefinition {
296        let updateMethod: arkts.MethodDefinition;
297        const that = this;
298        const updateOverloads = node.overloads?.map((overload) => that.visitor(overload)) ?? undefined;
299        const isMemo =
300            hasMemoAnnotation(node.scriptFunction) ||
301            hasMemoIntrinsicAnnotation(node.scriptFunction) ||
302            hasMemoEntryAnnotation(node.scriptFunction);
303        if (isMemo && node.scriptFunction.body) {
304            const hasIntrinsic = hasMemoIntrinsicAnnotation(node.scriptFunction);
305            updateMethod = arkts.factory.updateMethodDefinition(
306                node,
307                node.kind,
308                node.name,
309                this.signatureTransformer.visitor(
310                    removeMemoAnnotation(this.updateScriptFunction(node.scriptFunction, node.name.name)),
311                    hasIntrinsic
312                ),
313                node.modifiers,
314                false
315            );
316        } else {
317            updateMethod = arkts.factory.updateMethodDefinition(
318                node,
319                node.kind,
320                node.name,
321                this.signatureTransformer.visitor(node.scriptFunction),
322                node.modifiers,
323                false
324            );
325        }
326        if (!!updateOverloads) {
327            updateMethod.setOverloads(castOverloadsToMethods(updateOverloads));
328        }
329        this.modified ||= this.signatureTransformer.modified;
330        return updateMethod;
331    }
332
333    private updateDeclaredMethodMemoCall(
334        node: arkts.CallExpression,
335        decl: arkts.MethodDefinition,
336        ignoreSelf: boolean = false
337    ): arkts.CallExpression {
338        let updatedArguments: arkts.AstNode[] = node.arguments.map((it, index) => {
339            const param = decl.scriptFunction.params.at(index);
340            if (!param || !arkts.isEtsParameterExpression(param)) {
341                return it;
342            }
343            if (isMemoETSParameterExpression(param) && arkts.isArrowFunctionExpression(it)) {
344                this.enterAnonymousScope(it.scriptFunction);
345                const res = this.updateScriptFunction(it.scriptFunction);
346                this.exitAnonymousScope();
347                this.modified = true;
348                return arkts.factory.updateArrowFunction(it, res);
349            }
350            return it;
351        });
352        if (!ignoreSelf) {
353            this.checkMemoCallInMethod(decl);
354            updatedArguments = [
355                ...factory.createHiddenArguments(this.positionalIdTracker.id(decl.name.name)),
356                ...updatedArguments,
357            ];
358        }
359        const isMemo =
360            hasMemoAnnotation(decl.scriptFunction) ||
361            hasMemoIntrinsicAnnotation(decl.scriptFunction) ||
362            hasMemoEntryAnnotation(decl.scriptFunction);
363        if (parametrizedNodeHasReceiver(decl.scriptFunction) && isMemo) {
364            updatedArguments = moveToFront(updatedArguments, 2);
365        }
366        this.modified = true;
367        return arkts.factory.updateCallExpression(node, node.expression, node.typeArguments, updatedArguments);
368    }
369
370    private updateDeclaredCallWithName(node: arkts.CallExpression, name: string): arkts.CallExpression {
371        this.modified = true;
372        return factory.insertHiddenArgumentsToCall(node, this.positionalIdTracker.id(name));
373    }
374
375    private updateAnonymousCallWithMemoParams(node: arkts.CallExpression): arkts.CallExpression {
376        let newExpression: arkts.AstNode = node.expression;
377        if (isStandaloneArrowFunction(node.expression)) {
378            newExpression = arkts.factory.updateArrowFunction(
379                node.expression,
380                this.signatureTransformer.visitor(node.expression.scriptFunction)
381            );
382        }
383        const updatedArguments: arkts.AstNode[] = node.arguments.map((it) => {
384            if (arkts.isArrowFunctionExpression(it) && isMemoArrowFunction(it)) {
385                this.enterAnonymousScope(it.scriptFunction);
386                const res = this.updateScriptFunction(it.scriptFunction);
387                this.exitAnonymousScope();
388                this.modified = true;
389                return arkts.factory.updateArrowFunction(it, res);
390            }
391            return it;
392        });
393        this.modified ||= this.signatureTransformer.modified;
394        return arkts.factory.updateCallExpression(node, newExpression, node.typeArguments, updatedArguments);
395    }
396
397    private updateAnonymousMemoCall(
398        node: arkts.CallExpression,
399        expression: arkts.ArrowFunctionExpression
400    ): arkts.CallExpression {
401        const scope = this.scopes[this.scopes.length - 1];
402        const isValidScope = !!scope && scope.name === expression.scriptFunction.id?.name;
403        if (!isValidScope) {
404            return node;
405        }
406        this.exitAnonymousScope();
407        if (!scope.isMemo) {
408            return this.updateAnonymousCallWithMemoParams(node);
409        }
410        this.checkMemoCallInFunction();
411
412        this.enterAnonymousScope(expression.scriptFunction);
413        const res = this.updateScriptFunction(expression.scriptFunction, expression.scriptFunction.id?.name);
414        this.exitAnonymousScope();
415
416        const newNode = this.updateAnonymousCallWithMemoParams(node);
417        this.modified = true;
418        return arkts.factory.updateCallExpression(
419            node,
420            arkts.factory.updateArrowFunction(expression, res),
421            newNode.typeArguments,
422            [...factory.createHiddenArguments(this.positionalIdTracker.id()), ...newNode.arguments]
423        );
424    }
425
426    private updateCallExpressionWithNoDecl(node: arkts.CallExpression): arkts.CallExpression {
427        if (isStandaloneArrowFunction(node.expression)) {
428            return this.updateAnonymousMemoCall(node, node.expression);
429        }
430        return this.updateAnonymousCallWithMemoParams(node);
431    }
432
433    private updateCallExpression(node: arkts.CallExpression): arkts.CallExpression {
434        const expr = node.expression;
435        const decl = getDeclResolveAlias(expr);
436        if (!decl) {
437            return this.updateCallExpressionWithNoDecl(node);
438        }
439        if (arkts.isMethodDefinition(decl) && isMemoDeclaredMethod(decl)) {
440            return this.updateDeclaredMethodMemoCall(node, decl);
441        }
442        if (arkts.isMethodDefinition(decl) && isDeclaredMethodWithMemoParams(decl)) {
443            return this.updateDeclaredMethodMemoCall(node, decl, true);
444        }
445        if (arkts.isIdentifier(decl) && isMemoDeclaredIdentifier(decl)) {
446            return this.updateDeclaredCallWithName(node, decl.name);
447        }
448        if (
449            arkts.isClassProperty(decl) &&
450            isMemoDeclaredClassProperty(decl) &&
451            !!decl.key &&
452            arkts.isIdentifier(decl.key)
453        ) {
454            return this.updateDeclaredCallWithName(node, decl.key.name);
455        }
456        if (arkts.isEtsParameterExpression(decl) && isMemoETSParameterExpression(decl)) {
457            return this.updateDeclaredCallWithName(node, decl.identifier.name);
458        }
459        return this.updateCallExpressionWithNoDecl(node);
460    }
461
462    private updateClassProperty(node: arkts.ClassProperty, key: arkts.Identifier): arkts.ClassProperty {
463        const scope = this.scopes[this.scopes.length - 1];
464        const isValidScope = !!scope && scope.name === key.name;
465        if (!isValidScope) {
466            return node;
467        }
468        this.exitAnonymousScope();
469        if (!scope.isMemo) {
470            return node;
471        }
472
473        let res: arkts.ScriptFunction | undefined;
474        if (!!node.value && arkts.isArrowFunctionExpression(node.value)) {
475            this.enterAnonymousScope(node.value.scriptFunction);
476            res = this.updateScriptFunction(node.value.scriptFunction, key.name);
477            this.exitAnonymousScope();
478        }
479
480        let typeAnnotation: arkts.TypeNode | undefined;
481        if (!!node.typeAnnotation && !(typeAnnotation = factory.updateMemoTypeAnnotation(node.typeAnnotation))) {
482            console.error(`ETSFunctionType or ETSUnionType expected for @memo-property ${key.name}`);
483            throw 'Invalid @memo usage';
484        }
485
486        this.modified = true;
487        return arkts.factory.updateClassProperty(
488            node,
489            node.key,
490            res ? arkts.factory.updateArrowFunction(castArrowFunctionExpression(node.value), res) : undefined,
491            typeAnnotation,
492            node.modifiers,
493            node.isComputed
494        );
495    }
496
497    private updateTSTypeAliasDeclaration(node: arkts.TSTypeAliasDeclaration): arkts.TSTypeAliasDeclaration {
498        const scope = this.scopes[this.scopes.length - 1];
499        const isValidScope = !!scope && scope.name === node.id?.name;
500        if (!isValidScope) {
501            return node;
502        }
503        this.exitAnonymousScope();
504        if (!scope.isMemo) {
505            if (!!node.typeAnnotation) {
506                const newNode = arkts.factory.updateTSTypeAliasDeclaration(
507                    node,
508                    node.id,
509                    node.typeParams,
510                    this.signatureTransformer.visitor(node.typeAnnotation)
511                );
512                this.modified ||= this.signatureTransformer.modified;
513                return newNode;
514            }
515            return node;
516        }
517
518        let typeAnnotation: arkts.TypeNode | undefined;
519        if (!(typeAnnotation = factory.updateMemoTypeAnnotation(node.typeAnnotation))) {
520            console.error(`ETSFunctionType or ETSUnionType expected for @memo-type ${node.id!.name}`);
521            throw 'Invalid @memo usage';
522        }
523
524        this.modified = true;
525        return arkts.factory.updateTSTypeAliasDeclaration(node, node.id, node.typeParams, typeAnnotation);
526    }
527
528    private updateStandaloneArrowFunction(node: arkts.ArrowFunctionExpression): arkts.ArrowFunctionExpression {
529        const scope = this.scopes[this.scopes.length - 1];
530        const isValidScope = !!scope && scope.name === node.scriptFunction.id?.name;
531        if (!isValidScope) {
532            return node;
533        }
534        this.exitAnonymousScope();
535        if (!scope.isMemo) {
536            return arkts.factory.updateArrowFunction(node, this.signatureTransformer.visitor(node.scriptFunction));
537        }
538
539        this.enterAnonymousScope(node.scriptFunction);
540        const res = this.updateScriptFunction(node.scriptFunction, node.scriptFunction.id?.name);
541        this.exitAnonymousScope();
542
543        this.modified = true;
544        return arkts.factory.updateArrowFunction(node, this.signatureTransformer.visitor(res));
545    }
546
547    private updateVariableDeclarator(node: arkts.VariableDeclarator): arkts.VariableDeclarator {
548        const scope = this.scopes[this.scopes.length - 1];
549        const isValidScope = !!scope && scope.name === node.name.name;
550        if (!isValidScope) {
551            return node;
552        }
553        this.exitAnonymousScope();
554        if (!scope.isMemo) {
555            if (!!node.initializer && arkts.isArrowFunctionExpression(node.initializer)) {
556                return arkts.factory.updateVariableDeclarator(
557                    node,
558                    node.flag,
559                    node.name,
560                    arkts.factory.updateArrowFunction(
561                        node.initializer,
562                        this.signatureTransformer.visitor(node.initializer.scriptFunction)
563                    )
564                );
565            }
566            return node;
567        }
568
569        let typeAnnotation: arkts.TypeNode | undefined;
570        if (
571            !!node.name.typeAnnotation &&
572            !(typeAnnotation = factory.updateMemoTypeAnnotation(node.name.typeAnnotation))
573        ) {
574            console.error(`ETSFunctionType or ETSUnionType expected for @memo-variable-type ${node.name.name}`);
575            throw 'Invalid @memo usage';
576        }
577
578        let initializer: arkts.AstNode | undefined = node.initializer;
579        if (!!initializer && arkts.isArrowFunctionExpression(initializer)) {
580            this.enterAnonymousScope(initializer.scriptFunction);
581            const res = this.updateScriptFunction(initializer.scriptFunction, initializer.scriptFunction.id?.name);
582            this.exitAnonymousScope();
583            initializer = arkts.factory.updateArrowFunction(initializer, res);
584        }
585
586        this.modified = true;
587        return arkts.factory.updateVariableDeclarator(
588            node,
589            node.flag,
590            arkts.factory.updateIdentifier(node.name, node.name.name, typeAnnotation),
591            initializer
592        );
593    }
594
595    private updateTSAsExpression(
596        node: arkts.TSAsExpression,
597        expr: arkts.ArrowFunctionExpression
598    ): arkts.TSAsExpression {
599        const scope = this.scopes[this.scopes.length - 1];
600        const isValidScope = !!scope;
601        if (!isValidScope) {
602            return node;
603        }
604        this.exitAnonymousScope();
605        if (!scope.isMemo) {
606            return node;
607        }
608
609        this.enterAnonymousScope(expr.scriptFunction);
610        const res = this.updateScriptFunction(expr.scriptFunction, expr.scriptFunction.id?.name);
611        this.exitAnonymousScope();
612
613        let typeAnnotation: arkts.TypeNode | undefined;
614        if (!(typeAnnotation = factory.updateMemoTypeAnnotation(node.typeAnnotation))) {
615            console.error(`ETSFunctionType or ETSUnionType expected for @memo-as-type`);
616            throw 'Invalid @memo usage';
617        }
618
619        this.modified = true;
620        return arkts.factory.updateTSAsExpression(
621            node,
622            arkts.factory.updateArrowFunction(expr, res),
623            typeAnnotation,
624            node.isConst
625        );
626    }
627
628    private updateProperty(
629        node: arkts.Property,
630        key: arkts.Identifier,
631        value: arkts.ArrowFunctionExpression
632    ): arkts.Property {
633        const scope = this.scopes[this.scopes.length - 1];
634        const isValidScope = !!scope && scope.name === key.name;
635        if (!isValidScope) {
636            return node;
637        }
638        this.exitAnonymousScope();
639        if (!scope.isMemo) {
640            return node;
641        }
642
643        this.enterAnonymousScope(value.scriptFunction);
644        const res = this.updateScriptFunction(value.scriptFunction, value.scriptFunction.id?.name);
645        this.exitAnonymousScope();
646
647        this.modified = true;
648        return arkts.factory.updateProperty(node, key, arkts.factory.updateArrowFunction(value, res));
649    }
650
651    private updateThisAttributeAssignment(
652        node: arkts.AssignmentExpression,
653        thisAttribute: arkts.Identifier,
654        right: arkts.ArrowFunctionExpression
655    ): arkts.AssignmentExpression {
656        const scope = this.scopes[this.scopes.length - 1];
657        const isValidScope = !!scope && scope.name === thisAttribute.name;
658        if (!isValidScope) {
659            return node;
660        }
661        this.exitAnonymousScope();
662        if (!scope.isMemo) {
663            return node;
664        }
665
666        this.enterAnonymousScope(right.scriptFunction);
667        const res = this.updateScriptFunction(right.scriptFunction, right.scriptFunction.id?.name);
668        this.exitAnonymousScope();
669
670        this.modified = true;
671        return arkts.factory.updateAssignmentExpression(
672            node,
673            node.left!,
674            node.operatorType,
675            arkts.factory.updateArrowFunction(right, res)
676        );
677    }
678
679    visitor(beforeChildren: arkts.AstNode): arkts.AstNode {
680        this.enter(beforeChildren);
681        const node = this.visitEachChild(beforeChildren);
682        this.exit(beforeChildren);
683        if (arkts.isMethodDefinition(node)) {
684            return this.updateMethodDefinition(node);
685        }
686        if (arkts.isCallExpression(node)) {
687            return this.updateCallExpression(node);
688        }
689        if (arkts.isClassProperty(node) && !!node.key && arkts.isIdentifier(node.key)) {
690            return this.updateClassProperty(node, node.key);
691        }
692        if (arkts.isTSTypeAliasDeclaration(node)) {
693            return this.updateTSTypeAliasDeclaration(node);
694        }
695        if (isStandaloneArrowFunction(node)) {
696            return this.updateStandaloneArrowFunction(node);
697        }
698        if (arkts.isVariableDeclarator(node)) {
699            return this.updateVariableDeclarator(node);
700        }
701        if (arkts.isTSAsExpression(node) && node.expr && arkts.isArrowFunctionExpression(node.expr)) {
702            return this.updateTSAsExpression(node, node.expr);
703        }
704        if (isFunctionProperty(node)) {
705            return this.updateProperty(node, castIdentifier(node.key), castArrowFunctionExpression(node.value));
706        }
707        if (isThisAttributeAssignment(node) && !!node.right && arkts.isArrowFunctionExpression(node.right)) {
708            const thisAttribute = findThisAttribute(node.left!)!;
709            return this.updateThisAttributeAssignment(node, thisAttribute, node.right);
710        }
711        if (arkts.isEtsScript(node) && this.modified) {
712            factory.createContextTypesImportDeclaration(this.program);
713        }
714        return node;
715    }
716}
717