• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2024-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 { ArkBody } from '../ArkBody';
17import { ArkMethod } from '../ArkMethod';
18import { FieldSignature, MethodSignature, methodSignatureCompare, MethodSubSignature } from '../ArkSignature';
19import { CfgBuilder } from '../../graph/builder/CfgBuilder';
20import * as ts from 'ohos-typescript';
21import { Local } from '../../base/Local';
22import { MethodParameter } from './ArkMethodBuilder';
23import { LEXICAL_ENV_NAME_PREFIX, NAME_DELIMITER, NAME_PREFIX } from '../../common/Const';
24import { ArkParameterRef, ArkStaticFieldRef, ClosureFieldRef, GlobalRef } from '../../base/Ref';
25import { ArkAliasTypeDefineStmt, ArkAssignStmt, ArkInvokeStmt, ArkReturnStmt } from '../../base/Stmt';
26import { AliasType, ArrayType, ClosureType, FunctionType, LexicalEnvType, Type, UnclearReferenceType, UnionType } from '../../base/Type';
27import { AbstractInvokeExpr, ArkPtrInvokeExpr } from '../../base/Expr';
28
29type NestedMethodChain = {
30    parent: ArkMethod;
31    children: NestedMethodChain[] | null;
32};
33
34export class BodyBuilder {
35    private cfgBuilder: CfgBuilder;
36    private globals?: Map<string, GlobalRef>;
37
38    constructor(methodSignature: MethodSignature, sourceAstNode: ts.Node, declaringMethod: ArkMethod, sourceFile: ts.SourceFile) {
39        this.cfgBuilder = new CfgBuilder(sourceAstNode, methodSignature.getMethodSubSignature().getMethodName(), declaringMethod, sourceFile);
40    }
41
42    public build(): ArkBody | null {
43        this.cfgBuilder.buildCfgBuilder();
44        if (!this.cfgBuilder.isBodyEmpty()) {
45            const { cfg, locals, globals, aliasTypeMap, traps } = this.cfgBuilder.buildCfg();
46            if (globals !== null) {
47                this.setGlobals(globals);
48            }
49            cfg.buildDefUseStmt(locals);
50
51            return new ArkBody(locals, cfg, aliasTypeMap, traps.length ? traps : undefined);
52        }
53        return null;
54    }
55
56    public getCfgBuilder(): CfgBuilder {
57        return this.cfgBuilder;
58    }
59
60    public getGlobals(): Map<string, GlobalRef> | undefined {
61        return this.globals;
62    }
63
64    public setGlobals(globals: Map<string, GlobalRef>): void {
65        this.globals = globals;
66    }
67
68    /**
69     * Find out all locals in the parent method which are used by the childrenChain, these locals are the closures of the root node of the childrenChain.
70     * childrenChain contains all nested method from the root node of the childrenChain.
71     * baseLocals are all locals defined in the outer function.
72     * allNestedLocals are collect all locals defined in all outer functions of this childrenChain.
73     * Only the globals of the root of the childrenChain, which are in the baseLocals but not in the allNestedLocals are the actual closures that in baseLocals.
74     */
75    private findClosuresUsedInNested(childrenChain: NestedMethodChain, baseLocals: Map<string, Local>, allNestedLocals: Map<string, Local>): Local[] | null {
76        let closuresRes: Local[] = [];
77
78        const nestedMethod = childrenChain.parent;
79        let nestedGlobals = nestedMethod.getBodyBuilder()?.getGlobals();
80        if (nestedGlobals !== undefined) {
81            for (let global of nestedGlobals.values()) {
82                const nestedLocal = allNestedLocals.get(global.getName());
83                const closure = baseLocals.get(global.getName());
84                if (nestedLocal === undefined && closure !== undefined) {
85                    closuresRes.push(closure);
86                }
87            }
88        }
89        const children = childrenChain.children;
90        if (children === null) {
91            return closuresRes;
92        }
93        for (let chain of children) {
94            const nestedLocals = nestedMethod.getBody()?.getLocals();
95            if (nestedLocals !== undefined) {
96                nestedLocals.forEach((value, key) => {
97                    allNestedLocals.set(key, value);
98                });
99            }
100            const closures = this.findClosuresUsedInNested(chain, baseLocals, allNestedLocals);
101            if (closures) {
102                closuresRes.push(...closures);
103            }
104        }
105        return closuresRes;
106    }
107
108    /**
109     * 1. Find out all locals in the parent method which are used by the childrenChain, these locals are the closures of the root node of the childrenChain.
110     * 2. Create a lexical env local in the parent method, and pass it to root node of the childrenChain through the method signature.
111     * 3. Update the root node of the childrenChain to add parameterRef assign stmt and closureRef assign stmt.
112     * 4. Recursively do this for all nested method level by level.
113     */
114    private buildLexicalEnv(childrenChain: NestedMethodChain, baseLocals: Map<string, Local>, index: number): number {
115        let usedClosures = this.findClosuresUsedInNested(childrenChain, baseLocals, new Map<string, Local>());
116        const nestedMethod = childrenChain.parent;
117        const nestedSignature = nestedMethod.getImplementationSignature();
118        if (nestedSignature !== null && usedClosures !== null && usedClosures.length > 0) {
119            let lexicalEnv = new LexicalEnvType(nestedSignature, usedClosures);
120            const closuresLocal = new Local(`${LEXICAL_ENV_NAME_PREFIX}${index++}`, lexicalEnv);
121            baseLocals.set(closuresLocal.getName(), closuresLocal);
122            this.updateNestedMethodWithClosures(nestedMethod, closuresLocal);
123        } else if (usedClosures === null || usedClosures.length === 0) {
124            this.moveCurrentMethodLocalToGlobal(nestedMethod);
125        }
126
127        const nextNestedChains = childrenChain.children;
128        if (nextNestedChains === null) {
129            return index;
130        }
131        for (let nextChain of nextNestedChains) {
132            const newBaseLocals = nestedMethod.getBody()?.getLocals();
133            if (newBaseLocals === undefined) {
134                return index;
135            }
136            index = this.buildLexicalEnv(nextChain, newBaseLocals, index);
137        }
138        return index;
139    }
140
141    /**
142     * Find out and tag all closures from globals, and remove closures from both globals and locals.
143     * Precondition: body build has been done. All locals, globals and closures are both set as Local in body,
144     * while potential globals and closures are also recorded in bodybuilder.
145     * Constraint: only the outermost function can call this method to recursively handle closures of itself as well as all nested methods.
146     */
147    public handleGlobalAndClosure(): void {
148        /**
149         * Step1: Handle the outermost function, take it as Level 0.
150         * There must be no closures in Level 0. So only need to remove the locals which with the same name as the ones in globals.
151         */
152        let outerMethod = this.getCfgBuilder().getDeclaringMethod();
153        let outerGlobals = outerMethod.getBodyBuilder()?.getGlobals();
154        outerMethod.freeBodyBuilder();
155        let outerLocals = outerMethod.getBody()?.getLocals();
156        if (outerGlobals !== undefined && outerLocals !== undefined) {
157            outerGlobals.forEach((value, key) => {
158                const local = outerLocals!.get(key);
159                if (local !== undefined) {
160                    value.addUsedStmts(local.getUsedStmts());
161                    outerLocals!.delete(key);
162                }
163            });
164            if (outerGlobals.size > 0) {
165                outerMethod.getBody()?.setUsedGlobals(outerGlobals);
166            }
167        }
168
169        let nestedMethodChains = this.generateNestedMethodChains(outerMethod).children;
170        if (nestedMethodChains === null || outerLocals === undefined) {
171            return;
172        }
173        let closuresIndex = 0;
174        for (let nestedChain of nestedMethodChains) {
175            /**
176             * Step2: Handle each nested function in Level 1 one by one.
177             * Find out all closures from Level 0 used by these Level 1 functions as well as all their children nested functions.
178             * This will be done level by level recursively.
179             */
180            closuresIndex = this.buildLexicalEnv(nestedChain, outerLocals, closuresIndex);
181
182            /**
183             * Step3: Delete old globals which are recognized as closures, then the rest globals are the true globals.
184             * The redundancy locals should be deleted but the used stmts of them should be restored to globals.
185             * This will be done level by level recursively.
186             */
187            this.reorganizeGlobalAndLocal(nestedChain);
188
189            /**
190             * Step4: Infer UnclearReferenceType to check whether it is the type alias define in its parent function..
191             */
192            this.inferTypesDefineInOuter(outerMethod, nestedChain);
193
194            /**
195             * Step5: For each nested function, find out whether it is called by its parent function and update the related locals, globals and stmts.
196             */
197            this.updateNestedMethodUsedInOuter(nestedChain);
198
199            this.freeBodyBuilder(nestedChain);
200        }
201    }
202
203    private freeBodyBuilder(nestedChain: NestedMethodChain): void {
204        nestedChain.parent.freeBodyBuilder();
205        const childrenChains = nestedChain.children;
206        if (childrenChains === null) {
207            return;
208        }
209        for (const chain of childrenChains) {
210            this.freeBodyBuilder(chain);
211        }
212    }
213
214    private updateLocalTypesWithTypeAlias(locals: Map<string, Local>, typeAliases: Map<string, [AliasType, ArkAliasTypeDefineStmt]>): void {
215        for (let local of locals.values()) {
216            const newType = this.inferUnclearReferenceTypeWithTypeAlias(local.getType(), typeAliases);
217            if (newType !== null) {
218                local.setType(newType);
219            }
220        }
221    }
222
223    private inferUnclearReferenceTypeWithTypeAlias(localType: Type, typeAliases: Map<string, [AliasType, ArkAliasTypeDefineStmt]>): Type | null {
224        if (localType instanceof ArrayType && localType.getBaseType() instanceof UnclearReferenceType) {
225            const typeAlias = typeAliases.get((localType.getBaseType() as UnclearReferenceType).getName());
226            if (typeAlias !== undefined) {
227                localType.setBaseType(typeAlias[0]);
228                return localType;
229            }
230            return null;
231        }
232        if (localType instanceof UnionType) {
233            const optionTypes = localType.getTypes();
234            for (let i = 0; i < optionTypes.length; i++) {
235                const newType = this.inferUnclearReferenceTypeWithTypeAlias(optionTypes[i], typeAliases);
236                if (newType !== null) {
237                    optionTypes[i] = newType;
238                }
239            }
240            return localType;
241        }
242        if (localType instanceof UnclearReferenceType) {
243            const typeAlias = typeAliases.get(localType.getName());
244            if (typeAlias !== undefined) {
245                return typeAlias[0];
246            }
247        }
248        return null;
249    }
250
251    private generateNestedMethodChains(outerMethod: ArkMethod): NestedMethodChain {
252        let candidateMethods: ArkMethod[] = [];
253        outerMethod
254            .getDeclaringArkClass()
255            .getMethods()
256            .forEach(method => {
257                if (method.getName().startsWith(NAME_PREFIX) && method.getName().endsWith(`${NAME_DELIMITER}${outerMethod.getName()}`)) {
258                    candidateMethods.push(method);
259                }
260            });
261        const childrenChains = this.getNestedChildrenChains(outerMethod, candidateMethods);
262        if (childrenChains.length > 0) {
263            return { parent: outerMethod, children: childrenChains };
264        }
265        return { parent: outerMethod, children: null };
266    }
267
268    private getNestedChildrenChains(parentMethod: ArkMethod, candidateMethods: ArkMethod[]): NestedMethodChain[] {
269        let nestedMethodChain: NestedMethodChain[] = [];
270        for (let method of candidateMethods) {
271            const outerMethodSignature = method.getOuterMethod()?.getSignature();
272            if (outerMethodSignature !== undefined && methodSignatureCompare(parentMethod.getSignature(), outerMethodSignature)) {
273                const childrenChains = this.getNestedChildrenChains(method, candidateMethods);
274                if (childrenChains.length > 0) {
275                    nestedMethodChain.push({ parent: method, children: childrenChains });
276                } else {
277                    nestedMethodChain.push({ parent: method, children: null });
278                }
279            }
280        }
281        return nestedMethodChain;
282    }
283
284    private moveCurrentMethodLocalToGlobal(method: ArkMethod): void {
285        const globals = method.getBodyBuilder()?.getGlobals();
286        const locals = method.getBody()?.getLocals();
287        if (locals === undefined || globals === undefined) {
288            return;
289        }
290        globals.forEach((value, key) => {
291            const local = locals.get(key);
292            if (local !== undefined) {
293                value.addUsedStmts(local.getUsedStmts());
294                locals.delete(key);
295            }
296        });
297
298        if (globals.size > 0) {
299            method.getBody()?.setUsedGlobals(globals);
300        }
301    }
302
303    private reorganizeGlobalAndLocal(nestedChain: NestedMethodChain): void {
304        const nestedMethod = nestedChain.parent;
305        const params = nestedMethod.getSubSignature().getParameters();
306        const globals = nestedMethod.getBodyBuilder()?.getGlobals();
307        if (params.length > 0 && params[0].getType() instanceof LexicalEnvType && globals !== undefined) {
308            const closures = (params[0].getType() as LexicalEnvType).getClosures();
309            for (let closure of closures) {
310                globals.delete(closure.getName());
311            }
312        }
313
314        this.moveCurrentMethodLocalToGlobal(nestedMethod);
315
316        const childrenChains = nestedChain.children;
317        if (childrenChains === null) {
318            return;
319        }
320        for (const chain of childrenChains) {
321            this.reorganizeGlobalAndLocal(chain);
322        }
323    }
324
325    // 对嵌套函数中的UnclearReferenceType类型的变量进行类型推导,类型是否为外层函数中定义的类型别名
326    private inferTypesDefineInOuter(outerMethod: ArkMethod, childrenChain: NestedMethodChain): void {
327        const typeAliases = outerMethod.getBody()?.getAliasTypeMap();
328        const nestedLocals = childrenChain.parent.getBody()?.getLocals();
329        if (typeAliases !== undefined && nestedLocals !== undefined) {
330            this.updateLocalTypesWithTypeAlias(nestedLocals, typeAliases);
331        }
332        const childrenChains = childrenChain.children;
333        if (childrenChains === null) {
334            return;
335        }
336        for (const chain of childrenChains) {
337            this.inferTypesDefineInOuter(childrenChain.parent, chain);
338        }
339    }
340
341    private updateNestedMethodUsedInOuter(nestedChain: NestedMethodChain): void {
342        const nestedMethod = nestedChain.parent;
343        const outerMethod = nestedMethod.getOuterMethod();
344        if (outerMethod === undefined) {
345            return;
346        }
347        const outerLocals = outerMethod.getBody()?.getLocals();
348        if (outerLocals !== undefined) {
349            for (let local of outerLocals.values()) {
350                if (
351                    local.getType() instanceof LexicalEnvType &&
352                    methodSignatureCompare((local.getType() as LexicalEnvType).getNestedMethod(), nestedMethod.getSignature())
353                ) {
354                    this.updateOuterMethodWithClosures(outerMethod, nestedMethod, local);
355                    break;
356                }
357            }
358        }
359
360        const nestedMethodName = nestedMethod.getName();
361        const originalMethodName = this.getOriginalNestedMethodName(nestedMethodName) ?? '';
362        const outerGlobals = outerMethod.getBody()?.getUsedGlobals();
363        const callGlobal = outerGlobals?.get(nestedMethodName) ?? outerGlobals?.get(originalMethodName);
364        if (callGlobal !== undefined && callGlobal instanceof GlobalRef && callGlobal.getRef() === null) {
365            const fieldSignature = new FieldSignature(
366                nestedMethodName,
367                nestedMethod.getDeclaringArkClass().getSignature(),
368                new FunctionType(nestedMethod.getSignature())
369            );
370            callGlobal.setRef(new ArkStaticFieldRef(fieldSignature));
371        }
372
373        const childrenChains = nestedChain.children;
374        if (childrenChains === null) {
375            return;
376        }
377        for (const chain of childrenChains) {
378            this.updateNestedMethodUsedInOuter(chain);
379        }
380    }
381
382    private updateNestedMethodWithClosures(nestedMethod: ArkMethod, closuresLocal: Local): void {
383        if (!(closuresLocal.getType() instanceof LexicalEnvType)) {
384            return;
385        }
386
387        const declareSignatures = nestedMethod.getDeclareSignatures();
388        declareSignatures?.forEach((signature, index) => {
389            nestedMethod.setDeclareSignatureWithIndex(this.createNewSignatureWithClosures(closuresLocal, signature), index);
390        });
391
392        const implementSignature = nestedMethod.getImplementationSignature();
393        if (implementSignature !== null) {
394            nestedMethod.setImplementationSignature(this.createNewSignatureWithClosures(closuresLocal, implementSignature));
395        }
396
397        this.addClosureParamsAssignStmts(closuresLocal, nestedMethod);
398    }
399
400    private updateOuterMethodWithClosures(outerMethod: ArkMethod, nestedMethod: ArkMethod, closuresLocal: Local): void {
401        const nestedMethodName = nestedMethod.getName();
402        const nestedMethodLocal = outerMethod.getBody()?.getLocals().get(nestedMethodName);
403        if (nestedMethodLocal !== undefined) {
404            this.updateLocalInfoWithClosures(nestedMethodLocal, outerMethod, nestedMethod, closuresLocal);
405        } else {
406            const nestedMethodGlobal = outerMethod.getBody()?.getUsedGlobals()?.get(nestedMethodName);
407            if (nestedMethodGlobal !== undefined && nestedMethodGlobal instanceof GlobalRef) {
408                this.updateGlobalInfoWithClosures(nestedMethodGlobal, outerMethod, nestedMethod, closuresLocal);
409            }
410        }
411
412        const originalMethodName = this.getOriginalNestedMethodName(nestedMethodName);
413        if (originalMethodName === null) {
414            return;
415        }
416        const originalMethodLocal = outerMethod.getBody()?.getLocals().get(originalMethodName);
417        if (originalMethodLocal !== undefined) {
418            this.updateLocalInfoWithClosures(originalMethodLocal, outerMethod, nestedMethod, closuresLocal);
419        } else {
420            const originalMethodGlobal = outerMethod.getBody()?.getUsedGlobals()?.get(originalMethodName);
421            if (originalMethodGlobal !== undefined && originalMethodGlobal instanceof GlobalRef) {
422                this.updateGlobalInfoWithClosures(originalMethodGlobal, outerMethod, nestedMethod, closuresLocal);
423            }
424        }
425    }
426
427    private getOriginalNestedMethodName(nestedMethodName: string): string | null {
428        if (nestedMethodName.startsWith(NAME_PREFIX) && nestedMethodName.includes(NAME_DELIMITER)) {
429            const nameComponents = nestedMethodName.slice(1).split(NAME_DELIMITER);
430            if (nameComponents.length > 1) {
431                return nameComponents[0];
432            }
433        }
434        return null;
435    }
436
437    private updateGlobalInfoWithClosures(globalRef: GlobalRef, outerMethod: ArkMethod, nestedMethod: ArkMethod, closuresLocal: Local): void {
438        if (globalRef.getRef() !== null) {
439            return;
440        }
441        const methodSignature = nestedMethod.getImplementationSignature();
442        if (methodSignature === null) {
443            return;
444        }
445        const lexicalEnv = closuresLocal.getType();
446        if (!(lexicalEnv instanceof LexicalEnvType)) {
447            return;
448        }
449        const fieldSignature = new FieldSignature(
450            methodSignature.getMethodSubSignature().getMethodName(),
451            methodSignature.getDeclaringClassSignature(),
452            new ClosureType(lexicalEnv, methodSignature)
453        );
454        globalRef.setRef(new ArkStaticFieldRef(fieldSignature));
455        this.updateAbstractInvokeExprWithClosures(globalRef, outerMethod.getSignature(), nestedMethod.getSignature(), closuresLocal);
456    }
457
458    private updateLocalInfoWithClosures(local: Local, outerMethod: ArkMethod, nestedMethod: ArkMethod, closuresLocal: Local): void {
459        const localType = local.getType();
460        if (!(localType instanceof FunctionType)) {
461            return;
462        }
463
464        const lexicalEnv = closuresLocal.getType();
465        if (!(lexicalEnv instanceof LexicalEnvType)) {
466            return;
467        }
468
469        // 更新local的类型为ClosureType,methodSignature为内层嵌套函数
470        const nestedMethodSignature = nestedMethod.getImplementationSignature();
471        if (nestedMethodSignature !== null) {
472            local.setType(new ClosureType(lexicalEnv, nestedMethodSignature, localType.getRealGenericTypes()));
473        } else {
474            local.setType(new ClosureType(lexicalEnv, localType.getMethodSignature(), localType.getRealGenericTypes()));
475        }
476
477        this.updateAbstractInvokeExprWithClosures(local, outerMethod.getSignature(), nestedMethod.getSignature(), closuresLocal);
478    }
479
480    // 更新所有stmt中调用内层函数处的AbstractInvokeExpr中的函数签名和实参args,加入闭包参数
481    // 更新所有stmt中定义的函数指针的usedStmt中的函数签名和实参args,加入闭包参数
482    private updateAbstractInvokeExprWithClosures(
483        value: Local | GlobalRef,
484        outerMethodSignature: MethodSignature,
485        nestedMethodSignature: MethodSignature,
486        closuresLocal: Local
487    ): void {
488        for (const usedStmt of value.getUsedStmts()) {
489            if (usedStmt instanceof ArkInvokeStmt) {
490                this.updateSignatureAndArgsInArkInvokeExpr(usedStmt, nestedMethodSignature, closuresLocal);
491            } else if (usedStmt instanceof ArkAssignStmt) {
492                const rightOp = usedStmt.getRightOp();
493                if (rightOp instanceof AbstractInvokeExpr) {
494                    this.updateSignatureAndArgsInArkInvokeExpr(usedStmt, nestedMethodSignature, closuresLocal);
495                }
496                const leftOp = usedStmt.getLeftOp();
497                if (leftOp instanceof Local) {
498                    leftOp.setType(rightOp.getType());
499                }
500            } else if (usedStmt instanceof ArkReturnStmt) {
501                outerMethodSignature.getMethodSubSignature().setReturnType(value.getType());
502            }
503            const defValue = usedStmt.getDef();
504            if (defValue === null) {
505                continue;
506            }
507            if ((defValue instanceof Local || defValue instanceof GlobalRef) && defValue.getType() instanceof FunctionType) {
508                this.updateAbstractInvokeExprWithClosures(defValue, outerMethodSignature, nestedMethodSignature, closuresLocal);
509            }
510        }
511    }
512
513    private createNewSignatureWithClosures(closuresLocal: Local, oldSignature: MethodSignature): MethodSignature {
514        let oldSubSignature = oldSignature.getMethodSubSignature();
515        const params = oldSubSignature.getParameters();
516        const closuresParam = new MethodParameter();
517        closuresParam.setName(closuresLocal.getName());
518        closuresParam.setType(closuresLocal.getType());
519        params.unshift(closuresParam);
520        let newSubSignature = new MethodSubSignature(oldSubSignature.getMethodName(), params, oldSubSignature.getReturnType(), oldSubSignature.isStatic());
521        return new MethodSignature(oldSignature.getDeclaringClassSignature(), newSubSignature);
522    }
523
524    private updateSignatureAndArgsInArkInvokeExpr(stmt: ArkInvokeStmt | ArkAssignStmt, methodSignature: MethodSignature, closuresLocal: Local): void {
525        let expr: AbstractInvokeExpr;
526        if (stmt instanceof ArkInvokeStmt) {
527            expr = stmt.getInvokeExpr();
528        } else {
529            const rightOp = stmt.getRightOp();
530            if (!(rightOp instanceof AbstractInvokeExpr)) {
531                return;
532            }
533            expr = rightOp;
534        }
535        const exprMethodName = expr.getMethodSignature().getMethodSubSignature().getMethodName();
536        const nestedMethodName = methodSignature.getMethodSubSignature().getMethodName();
537        if (exprMethodName === nestedMethodName) {
538            expr.setMethodSignature(this.createNewSignatureWithClosures(closuresLocal, methodSignature));
539            expr.getArgs().unshift(closuresLocal);
540            closuresLocal.addUsedStmt(stmt);
541            return;
542        }
543
544        const originalMethodName = this.getOriginalNestedMethodName(nestedMethodName);
545        if (originalMethodName !== null) {
546            if (exprMethodName === originalMethodName || expr instanceof ArkPtrInvokeExpr) {
547                expr.setMethodSignature(methodSignature);
548                expr.getArgs().unshift(closuresLocal);
549                closuresLocal.addUsedStmt(stmt);
550            }
551        }
552    }
553
554    private addClosureParamsAssignStmts(closuresParam: Local, method: ArkMethod): void {
555        const lexicalEnv = closuresParam.getType();
556        if (!(lexicalEnv instanceof LexicalEnvType)) {
557            return;
558        }
559        const closures = lexicalEnv.getClosures();
560        if (closures.length === 0) {
561            return;
562        }
563        const oldParamRefs = method.getParameterRefs();
564        let body = method.getBody();
565        if (body === undefined) {
566            return;
567        }
568        let stmts = Array.from(body.getCfg().getBlocks())[0].getStmts();
569        let index = 0;
570        const parameterRef = new ArkParameterRef(index, lexicalEnv);
571        const closuresLocal = new Local(closuresParam.getName(), lexicalEnv);
572        body.addLocal(closuresLocal.getName(), closuresLocal);
573        let assignStmt = new ArkAssignStmt(closuresLocal, parameterRef);
574        assignStmt.setCfg(body.getCfg());
575        stmts.splice(index, 0, assignStmt);
576        closuresLocal.setDeclaringStmt(assignStmt);
577
578        oldParamRefs?.forEach(paramRef => {
579            index++;
580            paramRef.setIndex(index);
581        });
582
583        for (let closure of closures) {
584            let local = body.getLocals().get(closure.getName());
585            if (local === undefined) {
586                local = new Local(closure.getName(), closure.getType());
587                body.addLocal(local.getName(), local);
588            } else {
589                local.setType(closure.getType());
590            }
591            index++;
592            const closureFieldRef = new ClosureFieldRef(closuresParam, closure.getName(), closure.getType());
593            let assignStmt = new ArkAssignStmt(local, closureFieldRef);
594            assignStmt.setCfg(body.getCfg());
595            stmts.splice(index, 0, assignStmt);
596            local.setDeclaringStmt(assignStmt);
597            closuresLocal.addUsedStmt(assignStmt);
598        }
599    }
600}
601