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 Logger, { LOG_MODULE_TYPE } from '../../utils/logger'; 17import { FieldSignature } from '../model/ArkSignature'; 18import { Local } from './Local'; 19import { ArrayType, ClassType, LexicalEnvType, Type, UnknownType } from './Type'; 20import { Value } from './Value'; 21import { TypeInference } from '../common/TypeInference'; 22import { ArkMethod } from '../model/ArkMethod'; 23import { Stmt } from './Stmt'; 24import { IRInference } from '../common/IRInference'; 25 26const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'Ref'); 27 28/** 29 * @category core/base/ref 30 */ 31export abstract class AbstractRef implements Value { 32 abstract getUses(): Value[]; 33 34 abstract getType(): Type; 35 36 public inferType(arkMethod: ArkMethod): AbstractRef { 37 return this; 38 } 39} 40 41export class ArkArrayRef extends AbstractRef { 42 private base: Local; // 数组变量 43 private index: Value; // 索引 44 45 constructor(base: Local, index: Value) { 46 super(); 47 this.base = base; 48 this.index = index; 49 } 50 51 /** 52 * Returns the base of this array reference. Array reference refers to access to array elements. 53 * Array references usually consist of an local variable and an index. 54 * For example, `a[i]` is a typical array reference, where `a` is the base (i.e., local variable) 55 * pointing to the actual memory location where the array is stored 56 * and `i` is the index indicating access to the `i-th` element from array `a`. 57 * @returns the base of this array reference. 58 * @example 59 * 1. Get the base and the specific elements. 60 61 ```typescript 62 // Create an array 63 let myArray: number[] = [10, 20, 30, 40]; 64 // Create an ArrayRef object representing a reference to myArray[2] 65 let arrayRef = new ArkArrayRef(myArray, 2); 66 // Use the getBase() method to get the base of the array 67 let baseArray = arrayRef.getBase(); 68 69 console.log("Base array:", baseArray); // Output: Base array: [10, 20, 30, 40] 70 71 // Use baseArray and obeject index of ArrayRef to access to specific array elements 72 let element = baseArray[arrayRef.index]; 73 console.log("Element at index", arrayRef.index, ":", element); // Output: Element at index 2 : 30 74 ``` 75 */ 76 public getBase(): Local { 77 return this.base; 78 } 79 80 public setBase(newBase: Local): void { 81 this.base = newBase; 82 } 83 84 /** 85 * Returns the index of this array reference. 86 * In TypeScript, an array reference means that the variable stores 87 * the memory address of the array rather than the actual data of the array. 88 * @returns The index of this array reference. 89 */ 90 public getIndex(): Value { 91 return this.index; 92 } 93 94 public setIndex(newIndex: Value): void { 95 this.index = newIndex; 96 } 97 98 public getType(): Type { 99 let baseType = TypeInference.replaceTypeWithReal(this.base.getType()); 100 if (baseType instanceof ArrayType) { 101 return baseType.getBaseType(); 102 } else { 103 logger.warn(`the type of base in ArrayRef is not ArrayType`); 104 return UnknownType.getInstance(); 105 } 106 } 107 108 public getUses(): Value[] { 109 let uses: Value[] = []; 110 uses.push(this.base); 111 uses.push(...this.base.getUses()); 112 uses.push(this.index); 113 uses.push(...this.index.getUses()); 114 return uses; 115 } 116 117 public toString(): string { 118 return this.base + '[' + this.index + ']'; 119 } 120} 121 122export abstract class AbstractFieldRef extends AbstractRef { 123 private fieldSignature: FieldSignature; 124 125 constructor(fieldSignature: FieldSignature) { 126 super(); 127 this.fieldSignature = fieldSignature; 128 } 129 130 /** 131 * Returns the the field name as a **string**. 132 * @returns The the field name. 133 */ 134 public getFieldName(): string { 135 return this.fieldSignature.getFieldName(); 136 } 137 138 /** 139 * Returns a field signature, which consists of a class signature, 140 * a **string** field name, and a **boolean** label indicating whether it is static or not. 141 * @returns The field signature. 142 * @example 143 * 1. Compare two Fields 144 145 ```typescript 146 const fieldSignature = new FieldSignature(); 147 fieldSignature.setFieldName(...); 148 const fieldRef = new ArkInstanceFieldRef(baseValue as Local, fieldSignature); 149 ... 150 if (fieldRef.getFieldSignature().getFieldName() === 151 targetField.getFieldSignature().getFieldName()) { 152 ... 153 } 154 ``` 155 */ 156 public getFieldSignature(): FieldSignature { 157 return this.fieldSignature; 158 } 159 160 public setFieldSignature(newFieldSignature: FieldSignature): void { 161 this.fieldSignature = newFieldSignature; 162 } 163 164 public getType(): Type { 165 return this.fieldSignature.getType(); 166 } 167} 168 169export class ArkInstanceFieldRef extends AbstractFieldRef { 170 private base: Local; // which obj this field belong to 171 172 constructor(base: Local, fieldSignature: FieldSignature) { 173 super(fieldSignature); 174 this.base = base; 175 } 176 177 /** 178 * Returns the local of field, showing which object this field belongs to. 179 * A {@link Local} consists of : 180 * - Name: the **string** name of local value, e.g., "$temp0". 181 * - Type: the type of value. 182 * @returns The object that the field belongs to. 183 * @example 184 * 1. Get a base. 185 186 ```typescript 187 if (expr instanceof ArkInstanceFieldRef) { 188 ... 189 let base = expr.getBase(); 190 if (base.getName() == 'this') { 191 ... 192 } 193 ... 194 } 195 ``` 196 */ 197 public getBase(): Local { 198 return this.base; 199 } 200 201 public setBase(newBase: Local): void { 202 this.base = newBase; 203 } 204 205 public getUses(): Value[] { 206 let uses: Value[] = []; 207 uses.push(this.base); 208 uses.push(...this.base.getUses()); 209 return uses; 210 } 211 212 public toString(): string { 213 return this.base.toString() + '.<' + this.getFieldSignature() + '>'; 214 } 215 216 public inferType(arkMethod: ArkMethod): AbstractRef { 217 return IRInference.inferFieldRef(this, arkMethod); 218 } 219} 220 221export class ArkStaticFieldRef extends AbstractFieldRef { 222 constructor(fieldSignature: FieldSignature) { 223 super(fieldSignature); 224 } 225 226 public getUses(): Value[] { 227 return []; 228 } 229 230 public toString(): string { 231 return this.getFieldSignature().toString(); 232 } 233} 234 235export class ArkParameterRef extends AbstractRef { 236 private index: number; 237 private paramType: Type; 238 239 constructor(index: number, paramType: Type) { 240 super(); 241 this.index = index; 242 this.paramType = paramType; 243 } 244 245 public getIndex(): number { 246 return this.index; 247 } 248 249 public setIndex(index: number): void { 250 this.index = index; 251 } 252 253 public getType(): Type { 254 return this.paramType; 255 } 256 257 public setType(newType: Type): void { 258 this.paramType = newType; 259 } 260 261 public inferType(arkMethod: ArkMethod): AbstractRef { 262 return IRInference.inferParameterRef(this, arkMethod); 263 } 264 265 public getUses(): Value[] { 266 return []; 267 } 268 269 public toString(): string { 270 return 'parameter' + this.index + ': ' + this.paramType; 271 } 272} 273 274export class ArkThisRef extends AbstractRef { 275 private type: ClassType; 276 277 constructor(type: ClassType) { 278 super(); 279 this.type = type; 280 } 281 282 public getType(): ClassType { 283 return this.type; 284 } 285 286 public getUses(): Value[] { 287 return []; 288 } 289 290 public toString(): string { 291 return 'this: ' + this.type; 292 } 293} 294 295export class ArkCaughtExceptionRef extends AbstractRef { 296 private type: Type; 297 298 constructor(type: Type) { 299 super(); 300 this.type = type; 301 } 302 303 public getType(): Type { 304 return this.type; 305 } 306 307 public getUses(): Value[] { 308 return []; 309 } 310 311 public toString(): string { 312 return 'caughtexception: ' + this.type; 313 } 314} 315 316export class GlobalRef extends AbstractRef { 317 private name: string; 318 private ref: Value | null; 319 private usedStmts: Stmt[]; 320 321 constructor(name: string, ref?: Value) { 322 super(); 323 this.name = name; 324 this.ref = ref ?? null; 325 this.usedStmts = []; 326 } 327 328 public getName(): string { 329 return this.name; 330 } 331 332 public getUses(): Value[] { 333 return this.ref?.getUses() || []; 334 } 335 336 public getType(): Type { 337 return this.ref?.getType() || UnknownType.getInstance(); 338 } 339 340 public getRef(): Value | null { 341 return this.ref || null; 342 } 343 344 public setRef(value: Value): void { 345 this.ref = value; 346 } 347 348 public getUsedStmts(): Stmt[] { 349 return this.usedStmts; 350 } 351 352 public addUsedStmts(usedStmts: Stmt | Stmt[]): void { 353 if (usedStmts instanceof Stmt) { 354 this.usedStmts.push(usedStmts); 355 } else { 356 usedStmts.forEach(stmt => this.usedStmts.push(stmt)); 357 } 358 } 359 360 public toString(): string { 361 return this.getName(); 362 } 363} 364 365export class ClosureFieldRef extends AbstractRef { 366 private base: Local; 367 private fieldName: string; 368 private type: Type; 369 370 constructor(base: Local, fieldName: string, type: Type) { 371 super(); 372 this.base = base; 373 this.fieldName = fieldName; 374 this.type = type; 375 } 376 377 public getUses(): Value[] { 378 return []; 379 } 380 381 public getBase(): Local { 382 return this.base; 383 } 384 385 public getType(): Type { 386 return this.type; 387 } 388 389 public getFieldName(): string { 390 return this.fieldName; 391 } 392 393 public toString(): string { 394 return this.base.toString() + '.' + this.getFieldName(); 395 } 396 397 public inferType(arkMethod: ArkMethod): AbstractRef { 398 if (TypeInference.isUnclearType(this.type)) { 399 let type: Type | undefined = this.base.getType(); 400 if (type instanceof LexicalEnvType) { 401 type = type 402 .getClosures() 403 .find(c => c.getName() === this.fieldName) 404 ?.getType(); 405 } 406 if (type && !TypeInference.isUnclearType(type)) { 407 this.type = type; 408 } 409 } 410 return this; 411 } 412} 413