• 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 {
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