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 { UnknownType } from '../../core/base/Type'; 17import { ArkMethod } from '../../core/model/ArkMethod'; 18import { ArkCodeBuffer } from '../ArkStream'; 19import { SourceBase } from './SourceBase'; 20import { SourceBody } from './SourceBody'; 21import { SourceStmt } from './SourceStmt'; 22import { SourceTransformer } from './SourceTransformer'; 23import { PrinterUtils } from '../base/PrinterUtils'; 24import { Stmt } from '../../core/base/Stmt'; 25import { ArkNamespace } from '../../core/model/ArkNamespace'; 26import { ArkMetadataKind, CommentsMetadata } from '../../core/model/ArkMetadata'; 27import { getLineNo } from '../../core/base/Position'; 28import { MethodSignature } from '../../core/model/ArkSignature'; 29import { LEXICAL_ENV_NAME_PREFIX } from '../../core/common/Const'; 30 31/** 32 * @category save 33 */ 34export class SourceMethod extends SourceBase { 35 private method: ArkMethod; 36 private transformer: SourceTransformer; 37 38 public constructor(method: ArkMethod, indent: string = '') { 39 super(method.getDeclaringArkFile(), indent); 40 this.method = method; 41 this.transformer = new SourceTransformer(this); 42 this.inBuilder = this.initInBuilder(); 43 } 44 45 public getDeclaringArkNamespace(): ArkNamespace | undefined { 46 return this.method.getDeclaringArkClass().getDeclaringArkNamespace(); 47 } 48 49 public setInBuilder(inBuilder: boolean): void { 50 this.inBuilder = inBuilder; 51 } 52 53 public dump(): string { 54 this.printer.clear(); 55 const commentsMetadata = this.method.getMetadata(ArkMetadataKind.LEADING_COMMENTS); 56 if (commentsMetadata instanceof CommentsMetadata) { 57 const comments = commentsMetadata.getComments(); 58 comments.forEach(comment => { 59 this.printer.writeIndent().writeLine(comment.content); 60 }); 61 } 62 if (!this.method.isDefaultArkMethod()) { 63 this.printMethod(this.method); 64 } else { 65 this.printBody(this.method); 66 } 67 return this.printer.toString(); 68 } 69 70 public getLine(): number { 71 let line = this.method.getLine(); 72 if (line === null && this.method.getDeclareLineCols()) { 73 line = getLineNo(this.method.getDeclareLineCols()![0]); 74 } 75 if (line === null) { 76 line = 0; 77 } 78 if (line > 0) { 79 return line; 80 } 81 82 const stmts: Stmt[] = []; 83 const cfg = this.method.getCfg(); 84 if (cfg) { 85 cfg.getStmts() 86 .reverse() 87 .forEach(stmt => stmts.push(stmt)); 88 } 89 for (const stmt of stmts) { 90 if (stmt.getOriginPositionInfo().getLineNo() > 0) { 91 return stmt.getOriginPositionInfo().getLineNo(); 92 } 93 } 94 95 return line; 96 } 97 98 public dumpDefaultMethod(): SourceStmt[] { 99 let srcBody = new SourceBody(this.printer.getIndent(), this.method, false); 100 return srcBody.getStmts(); 101 } 102 103 private printMethod(method: ArkMethod): void { 104 this.printDecorator(method.getDecorators()); 105 106 let implementationSig = method.getImplementationSignature(); 107 108 if (this.method.getDeclareSignatures()) { 109 for (const methodSig of this.method.getDeclareSignatures()!) { 110 this.printer.writeIndent().writeLine(`${this.methodProtoToString(methodSig)};`); 111 } 112 } 113 114 if (!implementationSig) { 115 return; 116 } 117 118 this.printer.writeIndent().write(this.methodProtoToString(implementationSig!)); 119 120 // abstract function no body 121 if (SourceMethod.getPrinterOptions().noMethodBody) { 122 this.printer.writeIndent().writeLine(`;`); 123 return; 124 } 125 126 this.printer.writeLine(' {'); 127 this.printer.incIndent(); 128 this.printBody(method); 129 this.printer.decIndent(); 130 131 this.printer.writeIndent(); 132 if (PrinterUtils.isAnonymousMethod(method.getName())) { 133 this.printer.write('}'); 134 } else { 135 this.printer.writeLine('}'); 136 } 137 } 138 139 private printBody(method: ArkMethod): void { 140 let srcBody = new SourceBody(this.printer.getIndent(), method, this.inBuilder); 141 this.printer.write(srcBody.dump()); 142 } 143 144 private methodProtoToString(methodSig: MethodSignature): string { 145 let code = new ArkCodeBuffer(); 146 code.writeSpace(this.modifiersToString(this.method.getModifiers())); 147 if (!PrinterUtils.isAnonymousMethod(methodSig.getMethodSubSignature().getMethodName())) { 148 if (this.method.getDeclaringArkClass()?.isDefaultArkClass()) { 149 code.writeSpace('function'); 150 } 151 if (this.method.getAsteriskToken()) { 152 code.writeSpace('*'); 153 } 154 code.write(this.resolveMethodName(methodSig.getMethodSubSignature().getMethodName())); 155 } 156 157 const genericTypes = this.method.getGenericTypes(); 158 if (genericTypes && genericTypes.length > 0) { 159 code.write(`<${this.transformer.typeArrayToString(genericTypes)}>`); 160 } 161 162 let parameters: string[] = []; 163 methodSig 164 .getMethodSubSignature() 165 .getParameters() 166 .forEach(parameter => { 167 let str: string = parameter.getName(); 168 if (parameter.hasDotDotDotToken()) { 169 str = `...${parameter.getName()}`; 170 } 171 if (parameter.isOptional()) { 172 str += '?'; 173 } 174 if (parameter.getType()) { 175 str += ': ' + this.transformer.typeToString(parameter.getType()); 176 } 177 if (!str.startsWith(LEXICAL_ENV_NAME_PREFIX)) { 178 parameters.push(str); 179 } 180 }); 181 code.write(`(${parameters.join(', ')})`); 182 const returnType = methodSig.getMethodSubSignature().getReturnType(); 183 if (methodSig.getMethodSubSignature().getMethodName() !== 'constructor' && !(returnType instanceof UnknownType)) { 184 code.write(`: ${this.transformer.typeToString(returnType)}`); 185 } 186 if (PrinterUtils.isAnonymousMethod(methodSig.getMethodSubSignature().getMethodName())) { 187 code.write(' =>'); 188 } 189 return code.toString(); 190 } 191 192 public toArrowFunctionTypeString(): string { 193 let code = new ArkCodeBuffer(); 194 195 let parameters: string[] = []; 196 this.method.getParameters().forEach(parameter => { 197 let str: string = parameter.getName(); 198 if (parameter.isOptional()) { 199 str += '?'; 200 } 201 if (parameter.getType()) { 202 str += ': ' + this.transformer.typeToString(parameter.getType()); 203 } 204 parameters.push(str); 205 }); 206 code.write(`(${parameters.join(', ')}) => `); 207 const returnType = this.method.getReturnType(); 208 if (!(returnType instanceof UnknownType)) { 209 code.writeSpace(`${this.transformer.typeToString(returnType)}`); 210 } 211 212 return code.toString(); 213 } 214 215 private initInBuilder(): boolean { 216 return ( 217 this.method.hasBuilderDecorator() || 218 ((this.method.getName() === 'build' || this.method.getName() === 'pageTransition') && 219 !this.method.isStatic() && 220 this.method.getDeclaringArkClass().hasViewTree()) 221 ); 222 } 223} 224