diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..93a464b --- /dev/null +++ b/.npmignore @@ -0,0 +1 @@ +static/ \ No newline at end of file diff --git a/README.md b/README.md index ebdcf66..8fef00b 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,13 @@ npm run build npm pack ``` +## Static Analysis Setup +If you want to use the static analysis module (```--policy static_guided```), you need to install its dependencies first: +``` +cd static/test-demo +npm install +``` + ## Instructions ### 1. Usage @@ -26,15 +33,24 @@ npm pack haptest [options] Options: - -V, --version output the version number - -i --hap HAP bundle name or HAP file path or HAP project source root - -o --output output dir (default: "out") - --policy policy name (default: "manu") - -t --target [connectkey] hdc connectkey - -c --coverage enable coverage (default: false) - -h, --help display help for command + -V, --version output the version number + -i, --hap HAP bundle name or HAP file path or HAP project source root + -o, --output output dir (default: "out") + --policy policy name (default: "manu") + -t, --target [connectkey] hdc connectkey + -c, --coverage enable coverage (default: false) + --llm enable LLM-guided exploration (default: false) + --simk set similarity threshold K for tarpit detection (default: 3) + --staticConfig path to static analysis configuration file (required when policy=static_guided) + -h, --help display help for command + ``` +#### Note: +- ```--policy static_guided:``` Enable the static-analysis-guided exploration policy (requires ```--staticConfig``` to specify the static module configuration file). +-``` --llm:``` Enable the LLM-based enhanced exploration module; can be combined with static_guided policy for a hybrid strategy. +- ```--simk:``` Set the UI similarity threshold for tarpit detection. + ### 2. Using DevEco simulator to run HAP 1. download DevEco: https://developer.huawei.com/consumer/cn/deveco-studio/ @@ -57,6 +73,11 @@ haptest -i {Hap project root } --policy greedy_dfs -o out haptest --policy perf_start_hap -i ALL --exclude com.huawei.* com.ohos.* -o out ``` +### 5. Run with static analysis and LLM enabled +``` +haptest -i com.example.demo --policy static_guided --staticConfig config.json --llm --simk 3 -o out +``` + ## Contribution 1. Fork the repository diff --git a/config.json b/config.json new file mode 100644 index 0000000..da7fc57 --- /dev/null +++ b/config.json @@ -0,0 +1,7 @@ +{ + "GPT_CONFIG":{ + "baseURL": "", + "apiKey": "" + } + +} \ No newline at end of file diff --git a/src/cli/cli.ts b/src/cli/cli.ts index a61e57a..ee382bc 100644 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -38,6 +38,7 @@ const logger = getLogger(); .option('--exclude [excludes...]', 'exclude bundle name') .option('--llm', 'start llm policy', false) .option('--simK ', '', '8') + .option('--staticConfig ', '静态引导策略配置文件路径') .parse(); let options = program.opts(); let logLevel = LOG_LEVEL.INFO; @@ -54,7 +55,8 @@ const logger = getLogger(); reportRoot: options.report, excludes: options.exclude, llm: options.llm, - simK: options.simK + simK: options.simK, + staticConfig: options.staticConfig, }; HapTestLogger.configure(path.join(options.output, 'haptest.log'), logLevel); diff --git a/src/event/event_builder.ts b/src/event/event_builder.ts index 21d3ebd..d52c484 100644 --- a/src/event/event_builder.ts +++ b/src/event/event_builder.ts @@ -151,4 +151,29 @@ export class EventBuilder { this.randomText.push(RandomUtils.genRandomString(len)); } } + + static createEventFromNode(node: any): Event |undefined{ + let eventType = node.call_back_method; + console.info(`解析事件类型: ${eventType}`); + console.info(`解析节点: ${JSON.stringify(node)}`); + let component = SerializeUtils.deserialize(Component, node); + console.info(`解析组件: ${JSON.stringify(component)}`); + if(eventType.includes("onClick")){ + eventType = "onClick"; + console.info(`标准化事件类型为: ${eventType}`); + } else if(eventType.includes("onTouch")){ + eventType = "onTouch"; + } + switch (eventType) { + case 'onClick': + case 'onTouch': + console.info(`组件 bounds: ${JSON.stringify(component.bounds)}, type: ${typeof component.bounds}`); + let point = component.getCenterPoint(); + console.info(`生成 TouchEvent,坐标: (${point.x}, ${point.y})`); + return new TouchEvent(point); + default: + // throw new Error(`Unsupported event type: ${eventType}`); + return undefined; + } + } } diff --git a/src/policy/llm_guided_policy.ts b/src/policy/llm_guided_policy.ts index a643f34..df561b6 100644 --- a/src/policy/llm_guided_policy.ts +++ b/src/policy/llm_guided_policy.ts @@ -1,3 +1,5 @@ +import * as fs from 'fs'; +import * as path from 'path'; import { Device } from "../device/device"; import { Event } from "../event/event"; import { EventBuilder } from "../event/event_builder"; @@ -23,13 +25,10 @@ export class LLMGuidedPolicy extends PTGPolicy { private text: string; private actionList:string[]; - // GPT configuration - private static readonly GPT_CONFIG = { - baseURL: 'https://api.chatanywhere.tech/v1', - apiKey: 'sk-t6Dn1cwNdxVCkNE8jSPYqna47G0SY0yuDC5ajQeP9OkNo97f' - }; + // GPT 配置 + private static GPT_CONFIG: { baseURL: string; apiKey: string }; - private openai = new OpenAI(LLMGuidedPolicy.GPT_CONFIG); + private openai: OpenAI; constructor(device: Device, hap: Hap, name: PolicyName, ptg: PTG) { super(device, hap, name, true); @@ -41,27 +40,47 @@ export class LLMGuidedPolicy extends PTGPolicy { this.ptg = ptg; this.taskPrompt = new String("You are an expert in App GUI testing. Please guide the testing tool to enhance the coverage of functional scenarios in testing the App based on your extensive App testing experience. "); // initial task prompt + + // 加载 GPT 配置 + LLMGuidedPolicy.GPT_CONFIG = this.loadConfig(); + + // 初始化 OpenAI 客户端 + this.openai = new OpenAI(LLMGuidedPolicy.GPT_CONFIG); + } + + + private loadConfig(): { baseURL: string; apiKey: string } { + const configPath = path.resolve(__dirname, '../../config.json'); // 配置文件路径 + try { + const configData = fs.readFileSync(configPath, 'utf-8'); + const config = JSON.parse(configData); + return config.GPT_CONFIG; + } catch (error) { + this.logger.error(`加载配置文件失败: ${error}`); + throw new Error("无法加载 GPT 配置,请检查配置文件是否存在且格式正确。"); + } } - // Add a buffer to the class to store asynchronously fetched events + // 给类添加一个缓冲区来保存异步获取的事件 private pendingEvent: Event | null = null; private eventFetching: boolean = false; generateEventBasedOnPtg(): Event { this.updateState(); - // If an event has already been fetched asynchronously, return it directly + // 如果已经异步拿到一个事件了,就直接返回它 if (this.pendingEvent) { const event = this.pendingEvent; this.pendingEvent = null; + this.eventFetching = false; return event; } if(!this.eventFetching){ - this.logger.info("Start asynchronous call to selectEventFromLLM"); - // Start asynchronous logic (only once) + this.logger.info("开始异步调用 selectEventFromLLM"); + // 启动异步逻辑(只启动一次) this.eventFetching = true; - // Start asynchronous logic to fetch events and cache them in pendingEvent + // 启动异步逻辑获取事件,缓存到 pendingEvent this.selectEventFromLLM().then(event => { if (event === undefined) { if (this.retryCount > MAX_NUM_RESTARTS) { @@ -74,14 +93,13 @@ export class LLMGuidedPolicy extends PTGPolicy { } else { this.retryCount = 0; this.pendingEvent = event; - this.logger.info("selectEventFromLLM successfully returned"); + this.logger.info("selectEventFromLLM 成功返回"); } }).catch(err => { this.logger.error(`selectEventFromLLM failed: ${err}`); this.pendingEvent = EventBuilder.createRandomTouchEvent(this.device); }).finally(() => { this.logger.info("selectEventFromLLM finally"); - this.eventFetching = false; }); } diff --git a/src/policy/policy.ts b/src/policy/policy.ts index 4932c66..cf37aa4 100644 --- a/src/policy/policy.ts +++ b/src/policy/policy.ts @@ -35,10 +35,10 @@ export enum PolicyName { RANDOM = 'random', PERF_START_HAP = 'perf_start_hap', LLM_GUIDED = 'llm_guided', + STATIC_GUIDED = 'static_guided', } export abstract class Policy { - protected device: Device; protected hap: Hap; protected _enabled: boolean; diff --git a/src/policy/policy_builder.ts b/src/policy/policy_builder.ts index b10317f..d34541c 100644 --- a/src/policy/policy_builder.ts +++ b/src/policy/policy_builder.ts @@ -25,6 +25,7 @@ import { PtgRandomSearchPolicy } from './ptg_random_search_policy'; import { PerfPolicy } from './perf_policy'; import { LLMGuidedPolicy } from './llm_guided_policy'; import { PTG } from '../model/ptg'; +import { StaticGuidedPolicy } from './static_guided_policy'; export class PolicyBuilder { static buildLLMPolicy(device: Device, hap: Hap, options: FuzzOptions, ptg: PTG): LLMGuidedPolicy { @@ -43,7 +44,11 @@ export class PolicyBuilder { return new PtgRandomSearchPolicy(device, hap, PolicyName.RANDOM); } else if (options.policyName === PolicyName.PERF_START_HAP) { return new PerfPolicy(device, hap, PolicyName.PERF_START_HAP); - } else { + } else if (options.policyName === PolicyName.STATIC_GUIDED) { + // 静态引导策略实际上是基于PTG的,因此这里复用PTG的类 + return new StaticGuidedPolicy(device, hap, PolicyName.STATIC_GUIDED, options.staticConfig!); + } + else { return new PtgNaiveSearchPolicy(device, hap, PolicyName.NAIVE); } } diff --git a/src/policy/static_guided_policy.ts b/src/policy/static_guided_policy.ts new file mode 100644 index 0000000..3e1df81 --- /dev/null +++ b/src/policy/static_guided_policy.ts @@ -0,0 +1,194 @@ +import path from "path"; +import { Device } from "../device/device"; +import { Event } from "../event/event"; +import { Hap } from "../model/hap"; +import { PolicyName } from "./policy"; +import { MAX_NUM_RESTARTS, PTGPolicy } from "./ptg_policy"; +import { exec } from "child_process"; +import * as fs from "fs"; +import { EventBuilder } from "../event/event_builder"; +import { RandomUtils } from "../utils/random_utils"; +import { Component } from "../model/component"; +import { ExitEvent } from "../event/system_event"; +import { WaitEvent } from "../event/wait_event"; + +export class StaticGuidedPolicy extends PTGPolicy { + private pageComponentMap: Map; + private dumpDir: string = "../../static/test-demo/layout/"; + private outputDir: string = "../../static/test-demo/out/"; + private analyzerPath: string; + private originalCwd: string; + private targetDir: string; + private config: string; + + constructor(device: Device, hap: Hap, name: PolicyName, config: string) { + super(device, hap, name, true); + this.pageComponentMap = new Map(); + this.analyzerPath = path.resolve(__dirname, "../../static/test-demo/arkuianalyzer.js"); + // 保存当前工作目录 + this.originalCwd = process.cwd(); + console.info(`原始工作目录: ${this.originalCwd}`); + this.targetDir = path.dirname(this.analyzerPath); + this.config = config; + } + + // 给类添加一个缓冲区来保存异步获取的事件 + private pendingEvent: Event | null = null; + private eventFetching: boolean = false; + + + generateEventBasedOnPtg(): Event { + this.updateState(); + const fileName = `layout_${Date.now()}.json`; // 使用时间戳生成唯一文件名 + const filePath = path.resolve(__dirname, this.dumpDir, fileName); + + + // 如果已经异步拿到一个事件了,就直接返回它 + if (this.pendingEvent) { + const event = this.pendingEvent; + this.pendingEvent = null; + this.eventFetching = false; // 重置状态,允许下一次异步获取 + return event; + } + + if(!this.eventFetching){ + this.logger.info("开始异步调用 dumpLayout 并保存布局文件..."); + // 启动异步逻辑(只启动一次) + this.eventFetching = true; + + // 获取当前页面布局并保存到文件 + this.dumpLayout() + .then(layout => { + try { + fs.writeFileSync(filePath, layout, "utf-8"); + this.logger.info(`页面布局已成功保存到文件: ${filePath}`); + } catch (error) { + this.logger.error(`保存页面布局到文件失败: ${error}`); + throw new Error("保存页面布局到文件失败。"); + } + + // 将 exec 包装为 Promise + return new Promise((resolve, reject) => { + // 切换到目标目录 + process.chdir(this.targetDir); + this.logger.info(`切换到目录: ${this.targetDir}`); + // 运行 arkuianalyzer.js 脚本 + + exec(`node arkuianalyzer.js ${this.config}`, (error: any, stdout: string, stderr: string) => { + if (error) { + this.logger.error(`运行 arkuianalyzer.js 时出错: ${error.message}`); + reject(error); + return; + } + if (stderr) { + this.logger.warn(`arkuianalyzer.js 警告: ${stderr}`); + } + this.logger.info(`arkuianalyzer.js 输出: ${stdout}`); + + // 确保在 node 命令执行完成后再调用 generateEventBasedOnStaticJsonFile + const event = this.generateEventBasedOnStaticJsonFile(fileName); + if (event === undefined) { + if (this.retryCount > MAX_NUM_RESTARTS) { + this.stop(); + this.pendingEvent = new ExitEvent(); + } else { + this.retryCount++; + this.pendingEvent = EventBuilder.createRandomTouchEvent(this.device); + } + } else { + this.retryCount = 0; + this.pendingEvent = event; + this.logger.info("generateEventBasedOnStaticJsonFile 成功返回"); + } + + resolve(stdout); + }); + }); + }) + .catch(err => { + this.logger.error(`dumpLayout failed: ${err}`); + this.logger.info("生成一个随机事件"); + this.pendingEvent = EventBuilder.createRandomTouchEvent(this.device); + }) + .finally(() => { + this.logger.info("dumpLayout finally"); + fs.unlinkSync(filePath); // 删除布局文件 + this.logger.info(`已删除布局文件: ${filePath}`); + // 恢复原始工作目录 + process.chdir(this.originalCwd); + this.logger.info(`恢复到原始目录: ${this.originalCwd}`); + }); + } + + return new WaitEvent(); // 返回一个等待事件,等待布局文件生成和分析完成 + } + + async dumpLayout(): Promise { + let layout = await this.device.getDriver().dumpLayout(); + return JSON.stringify(layout, null, 4); // 转换为 JSON 字符串 + } + + private generateEventBasedOnStaticJsonFile(jsonfile:string): Event|undefined { + let events: Event[] = []; // 用于存储所有 event 的内容 + try { + const file = jsonfile.replace(".json","_guided.json"); // 指定一个具体的文件名 + this.logger.info(`尝试读取 JSON 文件: ${file}`); + const filePath = path.resolve(__dirname, this.outputDir, file); + const content = fs.readFileSync(filePath, "utf-8"); + // this.logger.info(`读取到的 JSON 文件内容 (${filePath}): ${content}`); + this.logger.info("尝试读取并解析 JSON 文件以生成事件..."); + + // 解析 JSON 文件内容 + const jsonData = JSON.parse(content); + + for (const edge of jsonData.edges) { + for (const node of edge.nodes) { + this.logger.info(`解析node: ${JSON.stringify(node)}`); + const event = EventBuilder.createEventFromNode(node); + if(event){ + events.push(event); + }else{ + this.logger.warn(`从节点生成事件时返回了 undefined,跳过该节点: ${JSON.stringify(node)}`); + } + } + } + + // 删除读取的文件 + fs.unlinkSync(filePath); + this.logger.info(`已删除临时文件: ${filePath}`); + + if(events.length == 1){ + this.logger.info(`仅生成了一个事件,直接返回该事件: ${JSON.stringify(events[0])}`); + return events[0]; + }else{ + this.logger.info(`生成了多个事件,共 ${events.length} 个。`); + // 随机选择一个事件返回 + const randomEvent = events[RandomUtils.genRandomNum(0, events.length - 1)]; + this.logger.info(`随机选择的事件: ${JSON.stringify(randomEvent)}`); + return randomEvent; + } + + } catch (error) { + this.logger.error(`读取或删除 JSON 文件时出错: ${error}`); + } + + return undefined; + } + + private updateState(): void { + if (!this.currentPage!.isForeground()) { + return; + } + + let pageSig = this.currentPage!.getContentSig(); + if (!this.pageComponentMap.has(pageSig)) { + let components: Component[] = []; + for (const component of this.currentPage!.getComponents()) { + if (component.hasUIEvent()) { + components.push(component); + } + } + this.pageComponentMap.set(pageSig, components); + } + } +} \ No newline at end of file diff --git a/src/runner/fuzz_options.ts b/src/runner/fuzz_options.ts index bec89b8..1cc57f7 100644 --- a/src/runner/fuzz_options.ts +++ b/src/runner/fuzz_options.ts @@ -24,6 +24,8 @@ export interface FuzzOptions { hapFile?: string; reportRoot?: string; excludes?: string[]; + // xmq: add cli option llm?: boolean; simK: number; + staticConfig?: string; // 新增静态配置选项 } diff --git a/src/runner/runner_manager.ts b/src/runner/runner_manager.ts index 6dd32df..e900ae4 100644 --- a/src/runner/runner_manager.ts +++ b/src/runner/runner_manager.ts @@ -46,15 +46,15 @@ export class RunnerManager { this.enabled = true; this.policy = PolicyBuilder.buildPolicyByName(device, hap, options); - // New logic: If the --llm option is true, create the LLM policy - // and enable UI tarpit detection + // 新增逻辑:如果 --llm 选项为 true,则创建 LLM 策略 + // 并开启UI陷阱检测 const llmEnabled = this.options.llm; if (!llmEnabled) { - return; // LLM option is not enabled, return directly + return; // LLM 选项没开,直接返回 } - // If the LLM option is enabled, ensure the policy is based on PTGPolicy + // LLM 选项开启,确保策略基于 PTGPolicy if (!(this.policy instanceof PTGPolicy)) { throw new Error("Current policy is not based on PTGPolicy"); } @@ -74,6 +74,10 @@ export class RunnerManager { let page = await this.device.getCurrentPage(this.hap); while (this.enabled && this.policy.enabled) { let event = await this.policy.generateEvent(page); + if (event instanceof WaitEvent) { + await new Promise(r => setTimeout(r, 1500)); // 等待异步完成 + continue; + } page = await this.addEvent(page, event); } } @@ -100,7 +104,7 @@ export class RunnerManager { } if (event instanceof WaitEvent) { - await new Promise(r => setTimeout(r, 1500)); // wait for 1.5s + await new Promise(r => setTimeout(r, 1500)); // 等待异步完成 continue; } lastPage = page; diff --git a/src/utils/serialize_utils.ts b/src/utils/serialize_utils.ts index 3d2ae1f..c1eb30d 100644 --- a/src/utils/serialize_utils.ts +++ b/src/utils/serialize_utils.ts @@ -32,4 +32,27 @@ export class SerializeUtils { return plainToInstance(cls, plain, options); } + + // 新增反序列化方法 + static deserialize(cls: ClassConstructor, plain: V, options?: ClassTransformOptions): T { + let defaultOptions: ClassTransformOptions = { enableCircularCheck: true, excludeExtraneousValues: true }; + defaultOptions.groups = options?.groups; + + // 反序列化为类实例 + const instance = plainToInstance(cls, plain, defaultOptions); + + // 如果实例有 bounds 字段,且是字符串,则转换为 Point[] 数组 + if (typeof instance === 'object' && instance !== null && 'bounds' in instance && typeof (instance as any).bounds === 'string') { + const boundsString = (instance as any).bounds; + const regex = /\[(\d+),(\d+)\]/g; + const points: { x: number; y: number }[] = []; + let match; + while ((match = regex.exec(boundsString)) !== null) { + points.push({ x: parseInt(match[1], 10), y: parseInt(match[2], 10) }); + } + (instance as any).bounds = points; // 转换后的 Point[] 数组 + } + + return instance; + } } diff --git a/static/test-demo/README.md b/static/test-demo/README.md new file mode 100644 index 0000000..8ded34e --- /dev/null +++ b/static/test-demo/README.md @@ -0,0 +1,3 @@ +npm install + +node arkuianalyzer.js \ No newline at end of file diff --git a/static/test-demo/arkuianalyzer.js b/static/test-demo/arkuianalyzer.js new file mode 100644 index 0000000..58664ed --- /dev/null +++ b/static/test-demo/arkuianalyzer.js @@ -0,0 +1,138 @@ +// 1. 导入 bundle.js(关键:所有需要的模块都从 bundle 中获取,无需再写 ../../src 路径) +const { + ModelUtils, + Scene, + SceneConfig, + ArkUIViewTree, + UIFuncGraph, + tabBar_2_TabContent, + GlobalOverlayTree, + UIFuncGraphBuilder, + ArkUIViewTreePrinter, +} = require('./bundle.js'); // 路径需对应你的 bundle.js 实际位置(如在根目录就写 './bundle.js') + +// 2. 导入 Node.js 内置的 fs 模块(之前已在 Rollup 中配置 external: ['fs'],直接 require 即可) +const fs = require('fs'); +const path = require('path'); + +// 3. 定义测试类(逻辑与你的原代码完全一致) +class ViewTreeTest { + constructor(configPath) { + this.configPath = configPath; // 从构造函数接收配置路径 + } + + // 初始化项目(原逻辑不变) + InitProject() { + //const configPath = path.resolve(__dirname, "./config/project_config.json"); + // 转为绝对路径(避免相对路径混乱) + const finalConfigPath = path.resolve(this.configPath); + // 检查文件是否存在 + if (!fs.existsSync(finalConfigPath)) { + console.error(`[错误] 配置文件不存在:${finalConfigPath}`); + process.exit(1); + } + console.log(`使用配置文件: ${finalConfigPath}`); + const sceneConfig = new SceneConfig(); + sceneConfig.buildFromJson(finalConfigPath); + + const scene = new Scene(); + scene.buildBasicInfo(sceneConfig); + scene.buildScene4HarmonyProject(); + + scene.inferTypes(); + + console.log("=== Scene Build Complete ==="); + for (const arkfile of scene.getFiles()) { + console.log("File:", arkfile.getName()); + } + + let uifuncgraph = new UIFuncGraph(scene); + let uiFuncGraphBuilder = new UIFuncGraphBuilder(uifuncgraph, scene); + + // 打印 ViewTree + if (1) { + this.printViewTree(scene); + } + + // 构建 UI 函数图 + if (1) { + uiFuncGraphBuilder.InitNode(); + uiFuncGraphBuilder.FindApiFromViewTreeUseCallGraph(); + // uiFuncGraphBuilder.FindApiFromViewTree(); + // uifuncgraph.removeNonUiabilityAndNonPageNodes(); + } + + // 匹配动态组件树并生成 dot 文件 + const dynamic_trees = uifuncgraph.MatchStaticAndDynamicComponentTrees(uifuncgraph); + console.log("dynmaic tree size = ", dynamic_trees.length); + for (let i = 0; i < dynamic_trees.length; i++) { + let dynamic_tree = dynamic_trees[i]; + let dotFileName = `viewtree/componentTree_dynamic.dot`; + let treePrinter = new ArkUIViewTreePrinter(dynamic_tree); + let dotContent = treePrinter.dump(); + fs.writeFileSync(dotFileName, dotContent, 'utf8'); + } + + console.log("tabbar size = ", tabBar_2_TabContent.size); + uifuncgraph.dump(path.resolve(__dirname, "./out/cg.dot")); + } + + // 打印 ViewTree(原逻辑不变) + printViewTree(scene) { + const outputDir = "viewtree"; + if (!fs.existsSync(outputDir)) { + fs.mkdirSync(outputDir); // 创建输出文件夹 + } + + // 遍历场景中的文件和类,生成组件树 dot 文件 + for (const arkFile of scene.getFiles()) { + for (const arkclass of ModelUtils.getAllClassesInFile(arkFile)) { + let viewTree = arkclass.getArkUIViewTree(); + let class_name = arkclass.getName(); + + if (viewTree) { + // 处理文件名中的斜杠(避免路径错误) + let sanitizedFileName = arkFile.getName().replace(/\//g, '_'); + let dotFileName = `viewtree/componentTree_${sanitizedFileName}_${class_name}.dot`; + let treePrinter = new ArkUIViewTreePrinter(viewTree); + let dotContent = treePrinter.dump(); + fs.writeFileSync(dotFileName, dotContent, 'utf8'); + console.log(`Component tree for ${class_name} has been written to ${dotFileName}`); + } + } + } + + // 处理全局 Overlay 树 + if (GlobalOverlayTree.length > 0) { + console.log("GlobalOverlayTree size = ", GlobalOverlayTree.length); + console.log("==== Global Overlay Tree ===="); + GlobalOverlayTree.forEach((overlayNode, idx) => { + let dotFileName = `viewtree/ArkUI_overlayTree_${idx}.dot`; + // 构建临时 ArkUIViewTree 实例(原逻辑不变) + let fakeTree = { + getRoot: () => overlayNode, + isClassField: () => false, + getClassFieldType: () => undefined, + getStateValues: () => new Map() + }; + let treePrinter = new ArkUIViewTreePrinter(fakeTree); + let dotContent = treePrinter.dump(); + fs.writeFileSync(dotFileName, dotContent, 'utf8'); + console.log(`Overlay tree #${idx} has been written to ${dotFileName}`); + }); + } else { + console.log("No overlay nodes found in GlobalOverlayTree."); + } + } +} + +// 4. 执行测试(实例化并调用 InitProject) +const args = process.argv.slice(2); // 获取命令行参数(排除前两个默认参数) + +if (args.length === 0) { + console.error("请指定配置文件路径!用法:node viewTreeTest.js "); + process.exit(1); +} +const configPath = path.resolve(args[0]); +const test = new ViewTreeTest(configPath); +test.InitProject(); \ No newline at end of file diff --git a/static/test-demo/bundle.js b/static/test-demo/bundle.js new file mode 100644 index 0000000..78fcc25 --- /dev/null +++ b/static/test-demo/bundle.js @@ -0,0 +1,40591 @@ +'use strict'; + +var path$1 = require('path'); +var crypto = require('crypto'); +var ts = require('ohos-typescript'); +var log4js = require('log4js'); +var fs$1 = require('fs'); + +function _interopNamespaceDefault(e) { + var n = Object.create(null); + if (e) { + Object.keys(e).forEach(function (k) { + if (k !== 'default') { + var d = Object.getOwnPropertyDescriptor(e, k); + Object.defineProperty(n, k, d.get ? d : { + enumerable: true, + get: function () { return e[k]; } + }); + } + }); + } + n.default = e; + return Object.freeze(n); +} + +var crypto__namespace = /*#__PURE__*/_interopNamespaceDefault(crypto); +var ts__namespace = /*#__PURE__*/_interopNamespaceDefault(ts); +var fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs$1); + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +function transfer2UnixPath(path2Do) { + return path$1.posix.join(...path2Do.split(/\\/)); +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// names +const NAME_DELIMITER = '$'; +const NAME_PREFIX = '%'; +const UNKNOWN_NAME = 'unk'; +const DEFAULT_NAME = 'dflt'; +// ArkClass const +const DEFAULT_ARK_CLASS_NAME = NAME_PREFIX + DEFAULT_NAME; +const ANONYMOUS_CLASS_PREFIX = NAME_PREFIX + 'AC'; +const ANONYMOUS_CLASS_DELIMITER = NAME_DELIMITER; +// ArkMethod const +const DEFAULT_ARK_METHOD_NAME = NAME_PREFIX + DEFAULT_NAME; +const INSTANCE_INIT_METHOD_NAME = NAME_PREFIX + 'instInit'; +const STATIC_INIT_METHOD_NAME = NAME_PREFIX + 'statInit'; +const STATIC_BLOCK_METHOD_NAME_PREFIX = NAME_PREFIX + 'statBlock'; +const ANONYMOUS_METHOD_PREFIX = NAME_PREFIX + 'AM'; +const CALL_SIGNATURE_NAME = 'create'; +// ArkSignature const +const UNKNOWN_PROJECT_NAME = NAME_PREFIX + UNKNOWN_NAME; +const UNKNOWN_FILE_NAME = NAME_PREFIX + UNKNOWN_NAME; +const UNKNOWN_NAMESPACE_NAME = NAME_PREFIX + UNKNOWN_NAME; +const UNKNOWN_CLASS_NAME = ''; // temp for being compatible with existing type inference +const UNKNOWN_FIELD_NAME = ''; // temp for being compatible with existing type inference +const UNKNOWN_METHOD_NAME = ''; // temp for being compatible with existing type inference +// IR const +const TEMP_LOCAL_PREFIX = NAME_PREFIX; +const LEXICAL_ENV_NAME_PREFIX = TEMP_LOCAL_PREFIX + 'closures'; +// ArkTS version +const ARKTS_STATIC_MARK = 'use static'; + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class CryptoUtils { + static sha256(content) { + return this.hash(content, 'sha256'); + } + static hash(content, algorithm) { + return crypto__namespace.createHash(algorithm).update(content).digest('base64url'); + } + static hashcode(content) { + let h = 0; + for (let i = 0; i < content.length; i++) { + h = (Math.imul(31, h) + content.charCodeAt(i)) | 0; + } + return h; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @category core/model + */ +class FileSignature { + projectName; + fileName; + hashcode; + static DEFAULT = new FileSignature(UNKNOWN_PROJECT_NAME, UNKNOWN_FILE_NAME); + constructor(projectName, fileName) { + this.projectName = projectName; + this.fileName = transfer2UnixPath(fileName); + this.hashcode = CryptoUtils.hashcode(this.toString()); + } + getProjectName() { + return this.projectName; + } + getFileName() { + return this.fileName; + } + toString() { + return `@${this.projectName}/${this.fileName}: `; + } + toMapKey() { + return `${this.hashcode}${path$1.basename(this.fileName)}`; + } +} +class NamespaceSignature { + namespaceName; + declaringFileSignature; + declaringNamespaceSignature; + static DEFAULT = new NamespaceSignature(UNKNOWN_NAMESPACE_NAME, FileSignature.DEFAULT, null); + constructor(namespaceName, declaringFileSignature, declaringNamespaceSignature = null) { + this.namespaceName = namespaceName; + this.declaringFileSignature = declaringFileSignature; + this.declaringNamespaceSignature = declaringNamespaceSignature; + } + getNamespaceName() { + return this.namespaceName; + } + getDeclaringFileSignature() { + return this.declaringFileSignature; + } + getDeclaringNamespaceSignature() { + return this.declaringNamespaceSignature; + } + toString() { + if (this.declaringNamespaceSignature) { + return this.declaringNamespaceSignature.toString() + '.' + this.namespaceName; + } + else { + return this.declaringFileSignature.toString() + this.namespaceName; + } + } + toMapKey() { + if (this.declaringNamespaceSignature) { + return this.declaringNamespaceSignature.toMapKey() + '.' + this.namespaceName; + } + else { + return this.declaringFileSignature.toMapKey() + this.namespaceName; + } + } +} +class ClassSignature { + declaringFileSignature; + declaringNamespaceSignature; + className; + static DEFAULT = new ClassSignature(UNKNOWN_CLASS_NAME, FileSignature.DEFAULT, null); + constructor(className, declaringFileSignature, declaringNamespaceSignature = null) { + this.className = className; + this.declaringFileSignature = declaringFileSignature; + this.declaringNamespaceSignature = declaringNamespaceSignature; + } + /** + * Returns the declaring file signature. + * @returns The declaring file signature. + */ + getDeclaringFileSignature() { + return this.declaringFileSignature; + } + /** + * Get the declaring namespace's signature. + * @returns the declaring namespace's signature. + */ + getDeclaringNamespaceSignature() { + return this.declaringNamespaceSignature; + } + /** + * Get the **string** name of class from the the class signature. The default value is `""`. + * @returns The name of this class. + */ + getClassName() { + return this.className; + } + /** + * + * @returns The name of the declare class. + */ + getDeclaringClassName() { + if (this.className.startsWith(ANONYMOUS_CLASS_PREFIX)) { + let temp = this.className; + do { + temp = temp.substring(temp.indexOf(NAME_DELIMITER) + 1, temp.lastIndexOf('.')); + } while (temp.startsWith(ANONYMOUS_CLASS_PREFIX)); + return temp; + } + return this.className; + } + setClassName(className) { + this.className = className; + } + getType() { + return new ClassType(this); + } + toString() { + if (this.declaringNamespaceSignature) { + return this.declaringNamespaceSignature.toString() + '.' + this.className; + } + else { + return this.declaringFileSignature.toString() + this.className; + } + } + toMapKey() { + if (this.declaringNamespaceSignature) { + return this.declaringNamespaceSignature.toMapKey() + '.' + this.className; + } + else { + return this.declaringFileSignature.toMapKey() + this.className; + } + } +} +/** + * `AliasClassSignature` is used to extend `ClassSignature`, preserving the actual name used during invocation. + */ +class AliasClassSignature extends ClassSignature { + aliasName; + constructor(aliasName, signature) { + super(signature.getClassName(), signature.getDeclaringFileSignature(), signature.getDeclaringNamespaceSignature()); + this.aliasName = aliasName; + } + /** + * Returns the name used in the code. + */ + getClassName() { + return this.aliasName; + } + /** + * Return the original name of declared class + */ + getOriginName() { + return super.getClassName(); + } +} +class FieldSignature { + declaringSignature; + fieldName; + type; + staticFlag; + constructor(fieldName, declaringSignature, type, staticFlag = false) { + this.fieldName = fieldName; + this.declaringSignature = declaringSignature; + this.type = type; + this.staticFlag = staticFlag; + } + getDeclaringSignature() { + return this.declaringSignature; + } + getBaseName() { + return this.declaringSignature instanceof ClassSignature ? this.declaringSignature.getClassName() : this.declaringSignature.getNamespaceName(); + } + getFieldName() { + return this.fieldName; + } + getType() { + return this.type; + } + isStatic() { + return this.staticFlag; + } + // temp for being compatible with existing type inference + setType(type) { + this.type = type; + } + // temp for being compatible with existing type inference + setStaticFlag(flag) { + this.staticFlag = flag; + } + toString() { + let tmpSig = this.fieldName; + if (this.isStatic()) { + tmpSig = '[static]' + tmpSig; + } + return this.getDeclaringSignature().toString() + '.' + tmpSig; + } +} +class MethodSubSignature { + methodName; + parameters; + returnType; + staticFlag; + constructor(methodName, parameters, returnType, staticFlag = false) { + this.methodName = methodName; + this.parameters = parameters; + this.returnType = returnType; + this.staticFlag = staticFlag; + } + getMethodName() { + return this.methodName; + } + getParameters() { + return this.parameters; + } + getParameterTypes() { + const parameterTypes = []; + this.parameters.forEach(parameter => { + parameterTypes.push(parameter.getType()); + }); + return parameterTypes; + } + getReturnType() { + return this.returnType; + } + setReturnType(returnType) { + this.returnType = returnType; + } + isStatic() { + return this.staticFlag; + } + toString(ptrName) { + let paraStr = ''; + this.getParameterTypes().forEach(parameterType => { + paraStr += parameterType.toString() + ', '; + }); + paraStr = paraStr.replace(/, $/, ''); + let tmpSig = `${ptrName ?? this.getMethodName()}(${paraStr})`; + if (this.isStatic()) { + tmpSig = '[static]' + tmpSig; + } + return tmpSig; + } +} +/** + * @category core/model + */ +class MethodSignature { + declaringClassSignature; + methodSubSignature; + constructor(declaringClassSignature, methodSubSignature) { + this.declaringClassSignature = declaringClassSignature; + this.methodSubSignature = methodSubSignature; + } + /** + * Return the declaring class signature. + * A {@link ClassSignature} includes: + * - File Signature: including the **string** names of the project and file, respectively. + * The default value of project's name is "%unk" and the default value of file's name is "%unk". + * - Namespace Signature | **null**: it may be a namespace signature or **null**. + * A namespace signature can indicate its **string** name of namespace and its file signature. + * - Class Name: the **string** name of this class. + * @returns The declaring class signature. + * @example + * 1. get class signature from ArkMethod. + + ```typescript + let methodSignature = expr.getMethodSignature(); + let name = methodSignature.getDeclaringClassSignature().getClassName(); + ``` + * + */ + getDeclaringClassSignature() { + return this.declaringClassSignature; + } + /** + * Returns the sub-signature of this method signature. + * The sub-signature is part of the method signature, which is used to + * identify the name of the method, its parameters and the return value type. + * @returns The sub-signature of this method signature. + */ + getMethodSubSignature() { + return this.methodSubSignature; + } + getType() { + return this.methodSubSignature.getReturnType(); + } + toString(ptrName) { + return this.declaringClassSignature.toString() + '.' + this.methodSubSignature.toString(ptrName); + } + toMapKey() { + return this.declaringClassSignature.toMapKey() + '.' + this.methodSubSignature.toString(); + } + isMatch(signature) { + return this.toString() === signature.toString() && this.getType().toString() === signature.getType().toString(); + } + getParamLength() { + return this.methodSubSignature.getParameters().filter(p => !p.getName().startsWith(LEXICAL_ENV_NAME_PREFIX)).length; + } +} +class LocalSignature { + name; + declaringMethodSignature; + constructor(name, declaringMethodSignature) { + this.name = name; + this.declaringMethodSignature = declaringMethodSignature; + } + getName() { + return this.name; + } + getDeclaringMethodSignature() { + return this.declaringMethodSignature; + } + toString() { + return this.declaringMethodSignature.toString() + '#' + this.name; + } +} +class AliasTypeSignature { + name; + declaringMethodSignature; + constructor(name, declaringMethodSignature) { + this.name = name; + this.declaringMethodSignature = declaringMethodSignature; + } + getName() { + return this.name; + } + getDeclaringMethodSignature() { + return this.declaringMethodSignature; + } + toString() { + return this.declaringMethodSignature.toString() + '#' + this.name; + } +} +//TODO, reconstruct +function fieldSignatureCompare(leftSig, rightSig) { + if (leftSig.getDeclaringSignature().toString() === rightSig.getDeclaringSignature().toString() && leftSig.getFieldName() === rightSig.getFieldName()) { + return true; + } + return false; +} +function methodSignatureCompare(leftSig, rightSig) { + if (classSignatureCompare(leftSig.getDeclaringClassSignature(), rightSig.getDeclaringClassSignature()) && + methodSubSignatureCompare(leftSig.getMethodSubSignature(), rightSig.getMethodSubSignature())) { + return true; + } + return false; +} +function methodSubSignatureCompare(leftSig, rightSig) { + if (leftSig.getMethodName() === rightSig.getMethodName() && + arrayCompare(leftSig.getParameterTypes(), rightSig.getParameterTypes()) && + leftSig.getReturnType() === rightSig.getReturnType()) { + return true; + } + return false; +} +function classSignatureCompare(leftSig, rightSig) { + if (fileSignatureCompare(leftSig.getDeclaringFileSignature(), rightSig.getDeclaringFileSignature()) && leftSig.getClassName() === rightSig.getClassName()) { + return true; + } + return false; +} +function fileSignatureCompare(leftSig, rightSig) { + if (leftSig.getFileName() === rightSig.getFileName() && leftSig.getProjectName() === rightSig.getProjectName()) { + return true; + } + return false; +} +function arrayCompare(leftArray, rightArray) { + if (leftArray.length !== rightArray.length) { + return false; + } + for (let i = 0; i < leftArray.length; i++) { + if (leftArray[i] !== rightArray[i]) { + return false; + } + } + return true; +} +function genSignature4ImportClause(arkFileName, importClauseName) { + return `<${arkFileName}>.<${importClauseName}>`; +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +exports.LOG_LEVEL = void 0; +(function (LOG_LEVEL) { + LOG_LEVEL["ERROR"] = "ERROR"; + LOG_LEVEL["WARN"] = "WARN"; + LOG_LEVEL["INFO"] = "INFO"; + LOG_LEVEL["DEBUG"] = "DEBUG"; + LOG_LEVEL["TRACE"] = "TRACE"; +})(exports.LOG_LEVEL || (exports.LOG_LEVEL = {})); +exports.LOG_MODULE_TYPE = void 0; +(function (LOG_MODULE_TYPE) { + LOG_MODULE_TYPE["DEFAULT"] = "default"; + LOG_MODULE_TYPE["ARKANALYZER"] = "ArkAnalyzer"; + LOG_MODULE_TYPE["HOMECHECK"] = "HomeCheck"; + LOG_MODULE_TYPE["TOOL"] = "Tool"; +})(exports.LOG_MODULE_TYPE || (exports.LOG_MODULE_TYPE = {})); +class ConsoleLogger { + static configure(logFilePath, arkanalyzer_level = exports.LOG_LEVEL.ERROR, tool_level = exports.LOG_LEVEL.INFO, use_console = false) { + let appendersTypes = []; + if (logFilePath) { + appendersTypes.push('file'); + } + if (!appendersTypes.length || use_console) { + appendersTypes.push('console'); + } + log4js.configure({ + appenders: { + file: { + type: 'fileSync', + filename: `${logFilePath}`, + maxLogSize: 5 * 1024 * 1024, + backups: 5, + compress: true, + encoding: 'utf-8', + layout: { + type: 'pattern', + pattern: '[%d] [%p] [%z] [%X{module}] - [%X{tag}] %m', + }, + }, + console: { + type: 'console', + layout: { + type: 'pattern', + pattern: '[%d] [%p] [%z] [%X{module}] - [%X{tag}] %m', + }, + }, + }, + categories: { + default: { + appenders: ['console'], + level: 'info', + enableCallStack: false, + }, + ArkAnalyzer: { + appenders: appendersTypes, + level: arkanalyzer_level, + enableCallStack: true, + }, + Tool: { + appenders: appendersTypes, + level: tool_level, + enableCallStack: true, + }, + }, + }); + } + static getLogger(log_type, tag = '-') { + let logger; + if (log_type === exports.LOG_MODULE_TYPE.DEFAULT || log_type === exports.LOG_MODULE_TYPE.ARKANALYZER) { + logger = log4js.getLogger(log_type); + } + else { + logger = log4js.getLogger(exports.LOG_MODULE_TYPE.TOOL); + } + logger.addContext('module', log_type); + logger.addContext('tag', tag); + return logger; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$C = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'Position'); +const LOW_BITS_SIZE = 16; +const LOW_BITS_MASK = 0xffff; +const HIGH_BITS_MASK = 0xffff0000; +const MIN_NUMBER = 0; +const MAX_NUMBER = 0xffff; +const INVALID_LINE = -1; +function setLine(lineCol, lineNo) { + if (lineNo < MIN_NUMBER) { + lineNo = MIN_NUMBER; + } + if (lineNo > MAX_NUMBER) { + logger$C.warn(`setLine overflow ${lineNo}`); + lineNo = MAX_NUMBER; + } + return (lineNo << LOW_BITS_SIZE) | (lineCol & LOW_BITS_MASK); +} +function setCol(lineCol, colNo) { + if (colNo < MIN_NUMBER) { + colNo = MIN_NUMBER; + } + if (colNo > MAX_NUMBER) { + logger$C.warn(`setCol overflow ${colNo}`); + colNo = MAX_NUMBER; + } + return (lineCol & HIGH_BITS_MASK) | colNo; +} +function setLineCol(lineNo, colNo) { + let lineCol = 0; + lineCol = setLine(lineCol, lineNo); + lineCol = setCol(lineCol, colNo); + return lineCol; +} +function getLineNo(lineCol) { + let line = lineCol >>> LOW_BITS_SIZE; + if (line === MIN_NUMBER) { + return INVALID_LINE; + } + return line; +} +function getColNo(lineCol) { + let col = lineCol & LOW_BITS_MASK; + if (col === MIN_NUMBER) { + return INVALID_LINE; + } + return col; +} +/** + * @category core/base + */ +class LineColPosition { + lineCol; + static DEFAULT = new LineColPosition(INVALID_LINE, INVALID_LINE); + constructor(lineNo, colNo) { + this.lineCol = setLineCol(lineNo, colNo); + } + getLineNo() { + return getLineNo(this.lineCol); + } + getColNo() { + return getColNo(this.lineCol); + } + static buildFromNode(node, sourceFile) { + let { line, character } = ts.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)); + // line start from 1. + return new LineColPosition(line + 1, character + 1); + } +} +class FullPosition { + first; + last; + static DEFAULT = new FullPosition(INVALID_LINE, INVALID_LINE, INVALID_LINE, INVALID_LINE); + constructor(firstLine, firstCol, lastLine, lastCol) { + this.first = setLineCol(firstLine, firstCol); + this.last = setLineCol(lastLine, lastCol); + } + getFirstLine() { + return getLineNo(this.first); + } + getLastLine() { + return getLineNo(this.last); + } + getFirstCol() { + return getColNo(this.first); + } + getLastCol() { + return getColNo(this.last); + } + static buildFromNode(node, sourceFile) { + const { line: startLine, character: startCharacter } = ts.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)); + const { line: endLine, character: endCharacter } = ts.getLineAndCharacterOfPosition(sourceFile, node.getEnd()); + // line start from 1 + return new FullPosition(startLine + 1, startCharacter + 1, endLine + 1, endCharacter + 1); + } + static merge(leftMostPosition, rightMostPosition) { + return new FullPosition(leftMostPosition.getFirstLine(), leftMostPosition.getFirstCol(), rightMostPosition.getLastLine(), rightMostPosition.getLastCol()); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const CONSTRUCTOR_NAME = 'constructor'; +const SUPER_NAME = 'super'; +const THIS_NAME = 'this'; +const GLOBAL_THIS_NAME = 'globalThis'; +const DEFAULT = 'default'; +const ALL = '*'; +const IMPORT = 'import'; +const PROMISE = 'Promise'; +const FUNCTION = 'Function'; +// ast const +const DECLARE_KEYWORD = 'DeclareKeyword'; +const NULL_KEYWORD = 'null'; +const UNDEFINED_KEYWORD = 'undefined'; +const ANY_KEYWORD = 'any'; +const UNKNOWN_KEYWORD = 'unknown'; +const BOOLEAN_KEYWORD = 'boolean'; +const NUMBER_KEYWORD = 'number'; +const STRING_KEYWORD = 'string'; +const VOID_KEYWORD = 'void'; +const NEVER_KEYWORD = 'never'; +const BIGINT_KEYWORD = 'bigint'; +const TSCONFIG_JSON = 'tsconfig.json'; + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const ETS_COMPILER_OPTIONS = { + ets: { + emitDecorators: [ + { + name: 'Entry', + emitParameters: true, + }, + { + name: 'Component', + emitParameters: false, + }, + { + name: 'Reusable', + emitParameters: false, + }, + { + name: 'CustomDialog', + emitParameters: false, + }, + { + name: 'Consume', + emitParameters: true, + }, + { + name: 'Link', + emitParameters: false, + }, + { + name: 'LocalStorageLink', + emitParameters: true, + }, + { + name: 'LocalStorageProp', + emitParameters: true, + }, + { + name: 'ObjectLink', + emitParameters: false, + }, + { + name: 'Prop', + emitParameters: false, + }, + { + name: 'Provide', + emitParameters: true, + }, + { + name: 'State', + emitParameters: false, + }, + { + name: 'StorageLink', + emitParameters: true, + }, + { + name: 'StorageProp', + emitParameters: true, + }, + { + name: 'Builder', + emitParameters: false, + }, + { + name: 'LocalBuilder', + emitParameters: false, + }, + { + name: 'BuilderParam', + emitParameters: false, + }, + { + name: 'Observed', + emitParameters: false, + }, + { + name: 'Require', + emitParameters: false, + }, + { + name: 'Sendable', + emitParameters: false, + }, + { + name: 'Track', + emitParameters: false, + }, + { + name: 'ComponentV2', + emitParameters: true, + }, + { + name: 'ObservedV2', + emitParameters: false, + }, + { + name: 'Trace', + emitParameters: false, + }, + { + name: 'Local', + emitParameters: false, + }, + { + name: 'Param', + emitParameters: false, + }, + { + name: 'Once', + emitParameters: false, + }, + { + name: 'Event', + emitParameters: false, + }, + { + name: 'Monitor', + emitParameters: true, + }, + { + name: 'Provider', + emitParameters: true, + }, + { + name: 'Consumer', + emitParameters: true, + }, + { + name: 'Computed', + emitParameters: false, + }, + { + name: 'Type', + emitParameters: true, + }, + ], + propertyDecorators: [ + { + name: 'Link', + needInitialization: false, + }, + { + name: 'Prop', + needInitialization: false, + }, + { + name: 'ObjectLink', + needInitialization: false, + }, + { + name: 'Consume', + needInitialization: false, + }, + ], + render: { + method: ['build', 'pageTransition'], + decorator: 'Builder', + }, + components: [ + 'AbilityComponent', + 'AlphabetIndexer', + 'Animator', + 'Badge', + 'Blank', + 'Button', + 'Calendar', + 'CalendarPicker', + 'Camera', + 'Canvas', + 'Checkbox', + 'CheckboxGroup', + 'Circle', + 'ColorPicker', + 'ColorPickerDialog', + 'Column', + '__Common__', + 'TabBar', + 'ColumnSplit', + 'ContentSlot', + 'Counter', + 'DataPanel', + 'DatePicker', + 'Divider', + 'EffectComponent', + 'Ellipse', + 'EmbeddedComponent', + 'Flex', + 'FolderStack', + 'FormComponent', + 'FormLink', + 'Gauge', + 'GeometryView', + 'Grid', + 'GridItem', + 'GridContainer', + 'Hyperlink', + 'Image', + 'ImageAnimator', + 'Line', + 'List', + 'ListItem', + 'ListItemGroup', + 'LoadingProgress', + 'Marquee', + 'MediaCachedImage', + 'Menu', + 'MenuItem', + 'MenuItemGroup', + 'MovingPhotoView', + 'NavDestination', + 'NavRouter', + 'Navigation', + 'Navigator', + 'NodeContainer', + 'Option', + 'PageTransitionEnter', + 'PageTransitionExit', + 'Panel', + 'Particle', + 'Path', + 'PatternLock', + 'Piece', + 'PluginComponent', + 'Polygon', + 'Polyline', + 'Progress', + 'QRCode', + 'Radio', + 'Rating', + 'Rect', + 'Refresh', + 'RelativeContainer', + 'RemoteWindow', + 'RootScene', + 'Row', + 'RowSplit', + 'RichText', + 'Screen', + 'Scroll', + 'ScrollBar', + 'Search', + 'Section', + 'Select', + 'Shape', + 'Sheet', + 'SideBarContainer', + 'Slider', + 'Span', + 'Stack', + 'Stepper', + 'StepperItem', + 'Swiper', + 'SymbolGlyph', + 'SymbolSpan', + 'TabContent', + 'Tabs', + 'Text', + 'TextPicker', + 'TextClock', + 'TextArea', + 'TextInput', + 'TextTimer', + 'TimePicker', + 'Toggle', + 'Video', + 'Web', + 'WindowScene', + 'WithTheme', + 'XComponent', + 'GridRow', + 'GridCol', + 'WaterFlow', + 'FlowItem', + 'ImageSpan', + 'LocationButton', + 'PasteButton', + 'SaveButton', + 'UIExtensionComponent', + 'IsolatedComponent', + 'RichEditor', + 'Component3D', + 'ContainerSpan', + ], + extend: { + decorator: ['Extend', 'AnimatableExtend'], + components: [ + { + name: 'AbilityComponent', + type: 'AbilityComponentAttribute', + instance: 'AbilityComponentInstance', + }, + { + name: 'AlphabetIndexer', + type: 'AlphabetIndexerAttribute', + instance: 'AlphabetIndexerInstance', + }, + { + name: 'Animator', + type: 'AnimatorAttribute', + instance: 'AnimatorInstance', + }, + { + name: 'Badge', + type: 'BadgeAttribute', + instance: 'BadgeInstance', + }, + { + name: 'Blank', + type: 'BlankAttribute', + instance: 'BlankInstance', + }, + { + name: 'Button', + type: 'ButtonAttribute', + instance: 'ButtonInstance', + }, + { + name: 'Calendar', + type: 'CalendarAttribute', + instance: 'CalendarInstance', + }, + { + name: 'CalendarPicker', + type: 'CalendarPickerAttribute', + instance: 'CalendarPickerInstance', + }, + { + name: 'Camera', + type: 'CameraAttribute', + instance: 'CameraInstance', + }, + { + name: 'Canvas', + type: 'CanvasAttribute', + instance: 'CanvasInstance', + }, + { + name: 'Checkbox', + type: 'CheckboxAttribute', + instance: 'CheckboxInstance', + }, + { + name: 'CheckboxGroup', + type: 'CheckboxGroupAttribute', + instance: 'CheckboxGroupInstance', + }, + { + name: 'Circle', + type: 'CircleAttribute', + instance: 'CircleInstance', + }, + { + name: 'ColorPicker', + type: 'ColorPickerAttribute', + instance: 'ColorPickerInstance', + }, + { + name: 'ColorPickerDialog', + type: 'ColorPickerDialogAttribute', + instance: 'ColorPickerDialogInstance', + }, + { + name: 'Column', + type: 'ColumnAttribute', + instance: 'ColumnInstance', + }, + { + name: 'ColumnSplit', + type: 'ColumnSplitAttribute', + instance: 'ColumnSplitInstance', + }, + { + name: 'Counter', + type: 'CounterAttribute', + instance: 'CounterInstance', + }, + { + name: 'DataPanel', + type: 'DataPanelAttribute', + instance: 'DataPanelInstance', + }, + { + name: 'DatePicker', + type: 'DatePickerAttribute', + instance: 'DatePickerInstance', + }, + { + name: 'Divider', + type: 'DividerAttribute', + instance: 'DividerInstance', + }, + { + name: 'EffectComponent', + type: 'EffectComponentAttribute', + instance: 'EffectComponentInstance', + }, + { + name: 'Ellipse', + type: 'EllipseAttribute', + instance: 'EllipseInstance', + }, + { + name: 'EmbeddedComponent', + type: 'EmbeddedComponentAttribute', + instance: 'EmbeddedComponentInstance', + }, + { + name: 'Flex', + type: 'FlexAttribute', + instance: 'FlexInstance', + }, + { + name: 'FolderStack', + type: 'FolderStackAttribute', + instance: 'FolderStackInstance', + }, + { + name: 'FormComponent', + type: 'FormComponentAttribute', + instance: 'FormComponentInstance', + }, + { + name: 'FormLink', + type: 'FormLinkAttribute', + instance: 'FormLinkInstance', + }, + { + name: 'Gauge', + type: 'GaugeAttribute', + instance: 'GaugeInstance', + }, + { + name: 'GeometryView', + type: 'GeometryViewAttribute', + instance: 'GeometryViewInstance', + }, + { + name: 'Grid', + type: 'GridAttribute', + instance: 'GridInstance', + }, + { + name: 'GridItem', + type: 'GridItemAttribute', + instance: 'GridItemInstance', + }, + { + name: 'GridContainer', + type: 'GridContainerAttribute', + instance: 'GridContainerInstance', + }, + { + name: 'Hyperlink', + type: 'HyperlinkAttribute', + instance: 'HyperlinkInstance', + }, + { + name: 'Image', + type: 'ImageAttribute', + instance: 'ImageInstance', + }, + { + name: 'ImageAnimator', + type: 'ImageAnimatorAttribute', + instance: 'ImageAnimatorInstance', + }, + { + name: 'Line', + type: 'LineAttribute', + instance: 'LineInstance', + }, + { + name: 'List', + type: 'ListAttribute', + instance: 'ListInstance', + }, + { + name: 'ListItem', + type: 'ListItemAttribute', + instance: 'ListItemInstance', + }, + { + name: 'ListItemGroup', + type: 'ListItemGroupAttribute', + instance: 'ListItemGroupInstance', + }, + { + name: 'LoadingProgress', + type: 'LoadingProgressAttribute', + instance: 'LoadingProgressInstance', + }, + { + name: 'Marquee', + type: 'MarqueeAttribute', + instance: 'MarqueeInstance', + }, + { + name: 'MediaCachedImage', + type: 'MediaCachedImageAttribute', + instance: 'MediaCachedImageInstance', + }, + { + name: 'Menu', + type: 'MenuAttribute', + instance: 'MenuInstance', + }, + { + name: 'MenuItem', + type: 'MenuItemAttribute', + instance: 'MenuItemInstance', + }, + { + name: 'MenuItemGroup', + type: 'MenuItemGroupAttribute', + instance: 'MenuItemGroupInstance', + }, + { + name: 'MovingPhotoView', + type: 'MovingPhotoViewAttribute', + instance: 'MovingPhotoViewInstance', + }, + { + name: 'NavDestination', + type: 'NavDestinationAttribute', + instance: 'NavDestinationInstance', + }, + { + name: 'NavRouter', + type: 'NavRouterAttribute', + instance: 'NavRouterInstance', + }, + { + name: 'Navigation', + type: 'NavigationAttribute', + instance: 'NavigationInstance', + }, + { + name: 'Navigator', + type: 'NavigatorAttribute', + instance: 'NavigatorInstance', + }, + { + name: 'NodeContainer', + type: 'NodeContainerAttribute', + instance: 'NodeContainerInstance', + }, + { + name: 'Option', + type: 'OptionAttribute', + instance: 'OptionInstance', + }, + { + name: 'PageTransitionEnter', + type: 'PageTransitionEnterAttribute', + instance: 'PageTransitionEnterInstance', + }, + { + name: 'PageTransitionExit', + type: 'PageTransitionExitAttribute', + instance: 'PageTransitionExitInstance', + }, + { + name: 'Panel', + type: 'PanelAttribute', + instance: 'PanelInstance', + }, + { + name: 'Particle', + type: 'ParticleAttribute', + instance: 'ParticleInstance', + }, + { + name: 'Path', + type: 'PathAttribute', + instance: 'PathInstance', + }, + { + name: 'PatternLock', + type: 'PatternLockAttribute', + instance: 'PatternLockInstance', + }, + { + name: 'Piece', + type: 'PieceAttribute', + instance: 'PieceInstance', + }, + { + name: 'PluginComponent', + type: 'PluginComponentAttribute', + instance: 'PluginComponentInstance', + }, + { + name: 'Polygon', + type: 'PolygonAttribute', + instance: 'PolygonInstance', + }, + { + name: 'Polyline', + type: 'PolylineAttribute', + instance: 'PolylineInstance', + }, + { + name: 'Progress', + type: 'ProgressAttribute', + instance: 'ProgressInstance', + }, + { + name: 'QRCode', + type: 'QRCodeAttribute', + instance: 'QRCodeInstance', + }, + { + name: 'Radio', + type: 'RadioAttribute', + instance: 'RadioInstance', + }, + { + name: 'Rating', + type: 'RatingAttribute', + instance: 'RatingInstance', + }, + { + name: 'Rect', + type: 'RectAttribute', + instance: 'RectInstance', + }, + { + name: 'RelativeContainer', + type: 'RelativeContainerAttribute', + instance: 'RelativeContainerInstance', + }, + { + name: 'Refresh', + type: 'RefreshAttribute', + instance: 'RefreshInstance', + }, + { + name: 'RemoteWindow', + type: 'RemoteWindowAttribute', + instance: 'RemoteWindowInstance', + }, + { + name: 'RootScene', + type: 'RootSceneAttribute', + instance: 'RootSceneInstance', + }, + { + name: 'Row', + type: 'RowAttribute', + instance: 'RowInstance', + }, + { + name: 'RowSplit', + type: 'RowSplitAttribute', + instance: 'RowSplitInstance', + }, + { + name: 'RichText', + type: 'RichTextAttribute', + instance: 'RichTextInstance', + }, + { + name: 'Screen', + type: 'ScreenAttribute', + instance: 'ScreenInstance', + }, + { + name: 'Scroll', + type: 'ScrollAttribute', + instance: 'ScrollInstance', + }, + { + name: 'ScrollBar', + type: 'ScrollBarAttribute', + instance: 'ScrollBarInstance', + }, + { + name: 'Search', + type: 'SearchAttribute', + instance: 'SearchInstance', + }, + { + name: 'Section', + type: 'SectionAttribute', + instance: 'SectionInstance', + }, + { + name: 'Select', + type: 'SelectAttribute', + instance: 'SelectInstance', + }, + { + name: 'Shape', + type: 'ShapeAttribute', + instance: 'ShapeInstance', + }, + { + name: 'Sheet', + type: 'SheetAttribute', + instance: 'SheetInstance', + }, + { + name: 'SideBarContainer', + type: 'SideBarContainerAttribute', + instance: 'SideBarContainerInstance', + }, + { + name: 'Slider', + type: 'SliderAttribute', + instance: 'SliderInstance', + }, + { + name: 'Span', + type: 'SpanAttribute', + instance: 'SpanInstance', + }, + { + name: 'Stack', + type: 'StackAttribute', + instance: 'StackInstance', + }, + { + name: 'Stepper', + type: 'StepperAttribute', + instance: 'StepperInstance', + }, + { + name: 'StepperItem', + type: 'StepperItemAttribute', + instance: 'StepperItemInstance', + }, + { + name: 'Swiper', + type: 'SwiperAttribute', + instance: 'SwiperInstance', + }, + { + name: 'SymbolGlyph', + type: 'SymbolGlyphAttribute', + instance: 'SymbolGlyphInstance', + }, + { + name: 'SymbolSpan', + type: 'SymbolSpanAttribute', + instance: 'SymbolSpanInstance', + }, + { + name: 'TabContent', + type: 'TabContentAttribute', + instance: 'TabContentInstance', + }, + { + name: 'Tabs', + type: 'TabsAttribute', + instance: 'TabsInstance', + }, + { + name: 'Text', + type: 'TextAttribute', + instance: 'TextInstance', + }, + { + name: 'TextPicker', + type: 'TextPickerAttribute', + instance: 'TextPickerInstance', + }, + { + name: 'TextClock', + type: 'TextClockAttribute', + instance: 'TextClockInstance', + }, + { + name: 'TextArea', + type: 'TextAreaAttribute', + instance: 'TextAreaInstance', + }, + { + name: 'TextInput', + type: 'TextInputAttribute', + instance: 'TextInputInstance', + }, + { + name: 'TextTimer', + type: 'TextTimerAttribute', + instance: 'TextTimerInstance', + }, + { + name: 'TimePicker', + type: 'TimePickerAttribute', + instance: 'TimePickerInstance', + }, + { + name: 'Toggle', + type: 'ToggleAttribute', + instance: 'ToggleInstance', + }, + { + name: 'Video', + type: 'VideoAttribute', + instance: 'VideoInstance', + }, + { + name: 'Web', + type: 'WebAttribute', + instance: 'WebInstance', + }, + { + name: 'WindowScene', + type: 'WindowSceneAttribute', + instance: 'WindowSceneInstance', + }, + { + name: 'XComponent', + type: 'XComponentAttribute', + instance: 'XComponentInstance', + }, + { + name: 'GridRow', + type: 'GridRowAttribute', + instance: 'GridRowInstance', + }, + { + name: 'GridCol', + type: 'GridColAttribute', + instance: 'GridColInstance', + }, + { + name: 'WaterFlow', + type: 'WaterFlowAttribute', + instance: 'WaterFlowInstance', + }, + { + name: 'FlowItem', + type: 'FlowItemAttribute', + instance: 'FlowItemInstance', + }, + { + name: 'ImageSpan', + type: 'ImageSpanAttribute', + instance: 'ImageSpanInstance', + }, + { + name: 'LocationButton', + type: 'LocationButtonAttribute', + instance: 'LocationButtonInstance', + }, + { + name: 'PasteButton', + type: 'PasteButtonAttribute', + instance: 'PasteButtonInstance', + }, + { + name: 'SaveButton', + type: 'SaveButtonAttribute', + instance: 'SaveButtonInstance', + }, + { + name: 'UIExtensionComponent', + type: 'UIExtensionComponentAttribute', + instance: 'UIExtensionComponentInstance', + }, + { + name: 'IsolatedComponent', + type: 'IsolatedComponentAttribute', + instance: 'IsolatedComponentInstance', + }, + { + name: 'RichEditor', + type: 'RichEditorAttribute', + instance: 'RichEditorInstance', + }, + { + name: 'Component3D', + type: 'Component3DAttribute', + instance: 'Component3DInstance', + }, + { + name: 'ContainerSpan', + type: 'ContainerSpanAttribute', + instance: 'ContainerSpanInstance', + }, + ], + }, + styles: { + decorator: 'Styles', + component: { + name: 'Common', + type: 'T', + instance: 'CommonInstance', + }, + property: 'stateStyles', + }, + concurrent: { + decorator: 'Concurrent', + }, + customComponent: 'CustomComponent', + syntaxComponents: { + paramsUICallback: ['ForEach', 'LazyForEach'], + attrUICallback: [ + { + name: 'Repeat', + attributes: ['each', 'template'], + }, + ], + }, + libs: [], + }, +}; +const COMPONENT_FOR_EACH = 'ForEach'; +const COMPONENT_LAZY_FOR_EACH = 'LazyForEach'; +const BUILDIN_SYSTEM_COMPONENT = new Set([...ETS_COMPILER_OPTIONS.ets.components, COMPONENT_FOR_EACH, COMPONENT_LAZY_FOR_EACH]); +const BUILDIN_ATOMIC_COMPONENT = new Set([ + 'AbilityComponent', + 'AlphabetIndexer', + 'Animator', + 'Blank', + 'CalendarPicker', + 'Camera', + 'Circle', + 'Component3D', + 'ContentSlot', + 'Divider', + 'Ellipse', + 'EmbeddedComponent', + 'FormComponent', + 'FrictionMotion', + 'GeometryView', + 'Image', + 'ImageAnimator', + 'ImageSpan', + 'Line', + 'LoadingProgress', + 'LocationButton', + 'Marquee', + 'MediaCachedImage', + 'NodeContainer', + 'PageTransitionEnter', + 'PageTransitionExit', + 'Particle', + 'PasteButton', + 'Path', + 'PatternLock', + 'Polygon', + 'Polyline', + 'Progress', + 'Radio', + 'Rect', + 'RemoteWindow', + 'RichEditor', + 'RichText', + 'SaveButton', + 'ScrollMotion', + 'Search', + 'Slider', + 'Span', + 'SpringMotion', + 'SpringProp', + 'SymbolSpan', + 'SymbolGlyph', + 'TextArea', + 'TextInput', + 'UIExtensionComponent', + 'Video', + 'Web', +]); +const COMPONENT_DECORATOR = new Set(['Reusable', 'Component', 'ComponentV2', 'CustomDialog']); +const ENTRY_DECORATOR = 'Entry'; +const BUILDER_DECORATOR = 'Builder'; +const BUILDER_PARAM_DECORATOR = 'BuilderParam'; +function isEtsAtomicComponent(name) { + return BUILDIN_ATOMIC_COMPONENT.has(name); +} +function isEtsSystemComponent(name) { + return BUILDIN_SYSTEM_COMPONENT.has(name); +} +function isEtsContainerComponent(name) { + return isEtsSystemComponent(name) && !isEtsAtomicComponent(name); +} +const COMPONENT_CREATE_FUNCTION = 'create'; +const COMPONENT_POP_FUNCTION = 'pop'; +const COMPONENT_CUSTOMVIEW = 'View'; +const COMPONENT_REPEAT = 'Repeat'; +const COMPONENT_DIALOG = 'Dialog'; +const COMPONENT_MENU = 'Menu'; +const COMPONENT_TOAST = 'Toast'; +const COMPONENT_TABBAR = 'TabBar'; +const COMPONENT_BINDCONTENT = 'BindContent'; +const COMPONENT_BINDSHEET = 'BindSheet'; +const COMPONENT_MENUWRAPPER = 'MenuWrapper'; +const COMPONENT_POPUP = 'Popup'; +const COMPONENT_IF = 'If'; +const COMPONENT_IF_BRANCH = 'IfBranch'; +const COMPONENT_BRANCH_FUNCTION = 'branch'; +const COMPONENT_BUILD_FUNCTION = 'build'; +const SPECIAL_CONTAINER_COMPONENT = new Set([COMPONENT_IF, COMPONENT_IF_BRANCH, COMPONENT_CUSTOMVIEW, COMPONENT_REPEAT]); +const COMPONENT_COMMON = 'Common'; +const COMPONENT_INSTANCE = 'Instance'; +const COMPONENT_ATTRIBUTE = 'Attribute'; +const CALL_BACK = 'Callback'; +const ON_OFF = new Set(['on', 'off']); +const OH_PACKAGE_JSON5 = 'oh-package.json5'; +const BUILD_PROFILE_JSON5 = 'build-profile.json5'; + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var ArkErrorCode; +(function (ArkErrorCode) { + ArkErrorCode[ArkErrorCode["OK"] = 0] = "OK"; + ArkErrorCode[ArkErrorCode["CLASS_INSTANCE_FIELD_UNDEFINDED"] = -1] = "CLASS_INSTANCE_FIELD_UNDEFINDED"; + ArkErrorCode[ArkErrorCode["BB_MORE_THAN_ONE_BRANCH_RET_STMT"] = -2] = "BB_MORE_THAN_ONE_BRANCH_RET_STMT"; + ArkErrorCode[ArkErrorCode["BB_BRANCH_RET_STMT_NOT_AT_END"] = -3] = "BB_BRANCH_RET_STMT_NOT_AT_END"; + ArkErrorCode[ArkErrorCode["CFG_NOT_FOUND_START_BLOCK"] = -4] = "CFG_NOT_FOUND_START_BLOCK"; + ArkErrorCode[ArkErrorCode["CFG_HAS_UNREACHABLE_BLOCK"] = -5] = "CFG_HAS_UNREACHABLE_BLOCK"; + ArkErrorCode[ArkErrorCode["METHOD_SIGNATURE_UNDEFINED"] = -6] = "METHOD_SIGNATURE_UNDEFINED"; + ArkErrorCode[ArkErrorCode["METHOD_SIGNATURE_LINE_UNMATCHED"] = -7] = "METHOD_SIGNATURE_LINE_UNMATCHED"; +})(ArkErrorCode || (ArkErrorCode = {})); + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var ArkMetadataKind; +(function (ArkMetadataKind) { + ArkMetadataKind[ArkMetadataKind["LEADING_COMMENTS"] = 0] = "LEADING_COMMENTS"; + ArkMetadataKind[ArkMetadataKind["TRAILING_COMMENTS"] = 1] = "TRAILING_COMMENTS"; +})(ArkMetadataKind || (ArkMetadataKind = {})); +/** + * ArkMetadata + * @example + * // get leading comments + * let stmt: Stmt = xxx; + * let comments = stmt.getMetadata(ArkMetadataKind.LEADING_COMMENTS) || []; + * comments.forEach((comment) => { + * logger.info(comment); + * }); + */ +class ArkMetadata { + metadata; + getMetadata(kind) { + return this.metadata?.get(kind); + } + setMetadata(kind, value) { + if (!this.metadata) { + this.metadata = new Map(); + } + this.metadata.set(kind, value); + } +} +class CommentsMetadata { + comments = []; + constructor(comments) { + this.comments = comments; + } + getComments() { + return this.comments; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$B = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'ArkBaseModel'); +const COMPONENT_MEMBER_DECORATORS = new Set([ + 'State', + 'Prop', + 'Link', + 'StorageProp', + 'StorageLink', + 'Provide', + 'Consume', + 'ObjectLink', + 'LocalStorageLink', + 'LocalStorageProp', + 'Local', + 'Param', + 'Event', + 'Provider', + 'Consumer', +]); +var ModifierType; +(function (ModifierType) { + ModifierType[ModifierType["PRIVATE"] = 1] = "PRIVATE"; + ModifierType[ModifierType["PROTECTED"] = 2] = "PROTECTED"; + ModifierType[ModifierType["PUBLIC"] = 4] = "PUBLIC"; + ModifierType[ModifierType["EXPORT"] = 8] = "EXPORT"; + ModifierType[ModifierType["STATIC"] = 16] = "STATIC"; + ModifierType[ModifierType["ABSTRACT"] = 32] = "ABSTRACT"; + ModifierType[ModifierType["ASYNC"] = 64] = "ASYNC"; + ModifierType[ModifierType["CONST"] = 128] = "CONST"; + ModifierType[ModifierType["ACCESSOR"] = 256] = "ACCESSOR"; + ModifierType[ModifierType["DEFAULT"] = 512] = "DEFAULT"; + ModifierType[ModifierType["IN"] = 1024] = "IN"; + ModifierType[ModifierType["READONLY"] = 2048] = "READONLY"; + ModifierType[ModifierType["OUT"] = 4096] = "OUT"; + ModifierType[ModifierType["OVERRIDE"] = 8192] = "OVERRIDE"; + ModifierType[ModifierType["DECLARE"] = 16384] = "DECLARE"; +})(ModifierType || (ModifierType = {})); +const MODIFIER_TYPE_MASK = 0xffff; +const MODIFIER_TYPE_STRINGS = [ + 'private', + 'protected', + 'public', + 'export', + 'static', + 'abstract', + 'async', + 'const', + 'accessor', + 'default', + 'in', + 'readonly', + 'out', + 'override', + 'declare', +]; +const MODIFIER_KIND_2_ENUM = new Map([ + [ts.SyntaxKind.AbstractKeyword, ModifierType.ABSTRACT], + [ts.SyntaxKind.AccessorKeyword, ModifierType.ACCESSOR], + [ts.SyntaxKind.AsyncKeyword, ModifierType.ASYNC], + [ts.SyntaxKind.ConstKeyword, ModifierType.CONST], + [ts.SyntaxKind.DeclareKeyword, ModifierType.DECLARE], + [ts.SyntaxKind.DefaultKeyword, ModifierType.DEFAULT], + [ts.SyntaxKind.ExportKeyword, ModifierType.EXPORT], + [ts.SyntaxKind.InKeyword, ModifierType.IN], + [ts.SyntaxKind.PrivateKeyword, ModifierType.PRIVATE], + [ts.SyntaxKind.ProtectedKeyword, ModifierType.PROTECTED], + [ts.SyntaxKind.PublicKeyword, ModifierType.PUBLIC], + [ts.SyntaxKind.ReadonlyKeyword, ModifierType.READONLY], + [ts.SyntaxKind.OutKeyword, ModifierType.OUT], + [ts.SyntaxKind.OverrideKeyword, ModifierType.OVERRIDE], + [ts.SyntaxKind.StaticKeyword, ModifierType.STATIC], +]); +function modifierKind2Enum(kind) { + return MODIFIER_KIND_2_ENUM.get(kind); +} +function modifiers2stringArray(modifiers) { + let strs = []; + for (let idx = 0; idx < MODIFIER_TYPE_STRINGS.length; idx++) { + if (modifiers & 0x01) { + strs.push(MODIFIER_TYPE_STRINGS[idx]); + } + modifiers = modifiers >>> 1; + } + return strs; +} +class ArkBaseModel { + modifiers; + decorators; + metadata; + getMetadata(kind) { + return this.metadata?.getMetadata(kind); + } + setMetadata(kind, value) { + if (!this.metadata) { + this.metadata = new ArkMetadata(); + } + return this.metadata?.setMetadata(kind, value); + } + getModifiers() { + if (!this.modifiers) { + return 0; + } + return this.modifiers; + } + setModifiers(modifiers) { + if (modifiers !== 0) { + this.modifiers = modifiers; + } + } + addModifier(modifier) { + this.modifiers = this.getModifiers() | modifier; + } + removeModifier(modifier) { + if (!this.modifiers) { + return; + } + this.modifiers &= MODIFIER_TYPE_MASK ^ modifier; + } + isStatic() { + return this.containsModifier(ModifierType.STATIC); + } + isProtected() { + return this.containsModifier(ModifierType.PROTECTED); + } + isPrivate() { + return this.containsModifier(ModifierType.PRIVATE); + } + isPublic() { + return this.containsModifier(ModifierType.PUBLIC); + } + isReadonly() { + return this.containsModifier(ModifierType.READONLY); + } + isAbstract() { + return this.containsModifier(ModifierType.ABSTRACT); + } + isExport() { + return this.containsModifier(ModifierType.EXPORT); + } + isDefault() { + return this.containsModifier(ModifierType.DEFAULT); + } + /** @deprecated Use {@link isExport} instead. */ + isExported() { + return this.isExport(); + } + isDeclare() { + return this.containsModifier(ModifierType.DECLARE); + } + containsModifier(modifierType) { + if (!this.modifiers) { + return false; + } + return (this.modifiers & modifierType) === modifierType; + } + getDecorators() { + if (this.decorators) { + return Array.from(this.decorators); + } + return []; + } + setDecorators(decorators) { + if (decorators.size > 0) { + this.decorators = decorators; + } + } + addDecorator(decorator) { + if (!this.decorators) { + this.decorators = new Set(); + } + this.decorators.add(decorator); + } + removeDecorator(kind) { + this.decorators?.forEach(value => { + if (value.getKind() === kind) { + this.decorators?.delete(value); + } + }); + } + hasBuilderDecorator() { + return this.hasDecorator(BUILDER_DECORATOR); + } + getStateDecorators() { + if (!this.decorators) { + return []; + } + return Array.from(this.decorators).filter(item => { + return COMPONENT_MEMBER_DECORATORS.has(item.getKind()); + }); + } + hasBuilderParamDecorator() { + return this.hasDecorator(BUILDER_PARAM_DECORATOR); + } + hasEntryDecorator() { + return this.hasDecorator(ENTRY_DECORATOR); + } + hasComponentDecorator() { + return this.hasDecorator(COMPONENT_DECORATOR); + } + hasDecorator(kind) { + let decorators = this.getDecorators(); + return (decorators.filter(value => { + if (kind instanceof Set) { + return kind.has(value.getKind()); + } + return value.getKind() === kind; + }).length !== 0); + } + validateFields(fields) { + let errs = []; + for (const field of fields) { + let value = Reflect.get(this, field); + if (!value) { + errs.push(field); + } + } + if (errs.length === 0) { + return { errCode: ArkErrorCode.OK }; + } + logger$B.error(`class fields: ${errs.join(',')} is undefined.`); + return { + errCode: ArkErrorCode.CLASS_INSTANCE_FIELD_UNDEFINDED, + errMsg: `${errs.join(',')} is undefined.`, + }; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var ExportType; +(function (ExportType) { + ExportType[ExportType["NAME_SPACE"] = 0] = "NAME_SPACE"; + ExportType[ExportType["CLASS"] = 1] = "CLASS"; + ExportType[ExportType["METHOD"] = 2] = "METHOD"; + ExportType[ExportType["LOCAL"] = 3] = "LOCAL"; + ExportType[ExportType["TYPE"] = 4] = "TYPE"; + ExportType[ExportType["UNKNOWN"] = 9] = "UNKNOWN"; +})(ExportType || (ExportType = {})); +/** + * @category core/model + */ +class ExportInfo extends ArkBaseModel { + _default; + nameBeforeAs; + exportClauseName = ''; + exportClauseType = ExportType.UNKNOWN; + arkExport; + exportFrom; + originTsPosition; + tsSourceCode; + declaringArkFile; + declaringArkNamespace; + constructor() { + super(); + } + /** + * Returns the program language of the file where this export info defined. + */ + getLanguage() { + return this.getDeclaringArkFile().getLanguage(); + } + getFrom() { + return this.exportFrom; + } + getOriginName() { + return this.nameBeforeAs ?? this.exportClauseName; + } + getExportClauseName() { + return this.exportClauseName; + } + setExportClauseType(exportClauseType) { + this.exportClauseType = exportClauseType; + } + getExportClauseType() { + return this.exportClauseType; + } + getNameBeforeAs() { + return this.nameBeforeAs; + } + setArkExport(value) { + this.arkExport = value; + } + getArkExport() { + return this.arkExport; + } + isDefault() { + if (this.exportFrom) { + return this.nameBeforeAs === DEFAULT; + } + if (this._default === undefined) { + this._default = this.containsModifier(ModifierType.DEFAULT); + } + return this._default; + } + getOriginTsPosition() { + return this.originTsPosition ?? LineColPosition.DEFAULT; + } + getTsSourceCode() { + return this.tsSourceCode ?? ''; + } + getDeclaringArkFile() { + return this.declaringArkFile; + } + getDeclaringArkNamespace() { + return this.declaringArkNamespace; + } + static Builder = class ArkExportBuilder { + exportInfo = new ExportInfo(); + exportClauseName(exportClauseName) { + this.exportInfo.exportClauseName = exportClauseName; + return this; + } + exportClauseType(exportClauseType) { + this.exportInfo.setExportClauseType(exportClauseType); + return this; + } + nameBeforeAs(nameBeforeAs) { + this.exportInfo.nameBeforeAs = nameBeforeAs; + return this; + } + modifiers(modifiers) { + this.exportInfo.modifiers = modifiers; + return this; + } + originTsPosition(originTsPosition) { + this.exportInfo.originTsPosition = originTsPosition; + return this; + } + tsSourceCode(tsSourceCode) { + this.exportInfo.tsSourceCode = tsSourceCode; + return this; + } + declaringArkFile(value) { + this.exportInfo.declaringArkFile = value; + return this; + } + declaringArkNamespace(value) { + this.exportInfo.declaringArkNamespace = value; + return this; + } + arkExport(value) { + this.exportInfo.arkExport = value; + return this; + } + exportFrom(exportFrom) { + if (exportFrom !== '') { + this.exportInfo.exportFrom = exportFrom; + } + return this; + } + setLeadingComments(commentsMetadata) { + if (commentsMetadata.getComments().length > 0) { + this.exportInfo.setMetadata(ArkMetadataKind.LEADING_COMMENTS, commentsMetadata); + } + return this; + } + setTrailingComments(commentsMetadata) { + if (commentsMetadata.getComments().length > 0) { + this.exportInfo.setMetadata(ArkMetadataKind.TRAILING_COMMENTS, commentsMetadata); + } + return this; + } + build() { + return this.exportInfo; + } + }; + validate() { + return this.validateFields(['declaringArkFile']); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @category core/base/type + */ +class Type { + toString() { + return this.getTypeString(); + } +} +/** + * any type + * @category core/base/type + */ +class AnyType extends Type { + static INSTANCE = new AnyType(); + static getInstance() { + return this.INSTANCE; + } + constructor() { + super(); + } + getTypeString() { + return ANY_KEYWORD; + } +} +/** + * unknown type + * @category core/base/type + */ +class UnknownType extends Type { + static INSTANCE = new UnknownType(); + static getInstance() { + return this.INSTANCE; + } + constructor() { + super(); + } + getTypeString() { + return UNKNOWN_KEYWORD; + } +} +/** + * unclear type + * @category core/base/type + */ +class UnclearReferenceType extends Type { + name; + genericTypes; + constructor(name, genericTypes = []) { + super(); + this.name = name; + this.genericTypes = genericTypes; + } + getName() { + return this.name; + } + getGenericTypes() { + return this.genericTypes; + } + getTypeString() { + let str = this.name; + if (this.genericTypes.length > 0) { + str += '<' + this.genericTypes.join(',') + '>'; + } + return str; + } +} +/** + * primitive type + * @category core/base/type + */ +class PrimitiveType extends Type { + name; + constructor(name) { + super(); + this.name = name; + } + getName() { + return this.name; + } + getTypeString() { + return this.name; + } +} +class BooleanType extends PrimitiveType { + static INSTANCE = new BooleanType(); + constructor() { + super(BOOLEAN_KEYWORD); + } + static getInstance() { + return this.INSTANCE; + } +} +class NumberType extends PrimitiveType { + static INSTANCE = new NumberType(); + constructor() { + super(NUMBER_KEYWORD); + } + static getInstance() { + return this.INSTANCE; + } +} +/** + * bigint type + * @category core/base/type + */ +class BigIntType extends PrimitiveType { + static INSTANCE = new BigIntType(); + constructor() { + super(BIGINT_KEYWORD); + } + static getInstance() { + return this.INSTANCE; + } +} +class StringType extends PrimitiveType { + static INSTANCE = new StringType(); + constructor() { + super(STRING_KEYWORD); + } + static getInstance() { + return this.INSTANCE; + } +} +/** + * null type + * @category core/base/type + */ +class NullType extends PrimitiveType { + static INSTANCE = new NullType(); + static getInstance() { + return this.INSTANCE; + } + constructor() { + super(NULL_KEYWORD); + } +} +/** + * undefined type + * @category core/base/type + */ +class UndefinedType extends PrimitiveType { + static INSTANCE = new UndefinedType(); + static getInstance() { + return this.INSTANCE; + } + constructor() { + super(UNDEFINED_KEYWORD); + } +} +/** + * literal type + * @category core/base/type + */ +class LiteralType extends PrimitiveType { + static TRUE = new LiteralType(true); + static FALSE = new LiteralType(false); + literalName; + constructor(literalName) { + super('literal'); + this.literalName = literalName; + } + getLiteralName() { + return this.literalName; + } + getTypeString() { + return this.literalName.toString(); + } +} +/** + * union type + * @category core/base/type + */ +class UnionType extends Type { + types; + currType; // The true type of the value at this time + constructor(types, currType = UnknownType.getInstance()) { + super(); + this.types = [...types]; + this.currType = currType; + } + getTypes() { + return this.types; + } + getCurrType() { + return this.currType; + } + setCurrType(newType) { + this.currType = newType; + } + getTypeString() { + let typesString = []; + this.getTypes().forEach(t => { + if (t instanceof UnionType || t instanceof IntersectionType) { + typesString.push(`(${t.toString()})`); + } + else { + typesString.push(t.toString()); + } + }); + return typesString.join('|'); + } + // TODO: Need to remove this function because of IntersectionType has been added. + flatType() { + const result = []; + this.types.forEach(t => { + if (t instanceof UnionType) { + t.flatType().forEach(e => result.push(e)); + } + else { + result.push(t); + } + }); + return result; + } +} +/** + * intersection type + * @category core/base/type + */ +class IntersectionType extends Type { + types; + constructor(types) { + super(); + this.types = [...types]; + } + getTypes() { + return this.types; + } + getTypeString() { + let typesString = []; + this.getTypes().forEach(t => { + if (t instanceof UnionType || t instanceof IntersectionType) { + typesString.push(`(${t.toString()})`); + } + else { + typesString.push(t.toString()); + } + }); + return typesString.join('&'); + } +} +/** + * types for function void return type + * @category core/base/type + */ +class VoidType extends Type { + static INSTANCE = new VoidType(); + static getInstance() { + return this.INSTANCE; + } + constructor() { + super(); + } + getTypeString() { + return VOID_KEYWORD; + } +} +class NeverType extends Type { + static INSTANCE = new NeverType(); + static getInstance() { + return this.INSTANCE; + } + constructor() { + super(); + } + getTypeString() { + return NEVER_KEYWORD; + } +} +/** + * function type + * @category core/base/type + */ +class FunctionType extends Type { + methodSignature; + realGenericTypes; + constructor(methodSignature, realGenericTypes) { + super(); + this.methodSignature = methodSignature; + this.realGenericTypes = realGenericTypes; + } + getMethodSignature() { + return this.methodSignature; + } + getRealGenericTypes() { + return this.realGenericTypes; + } + getTypeString() { + return this.methodSignature.toString(); + } +} +/** + * types for closures which is a special FunctionType with a lexical env + * @category core/base/type + */ +class ClosureType extends FunctionType { + lexicalEnv; + constructor(lexicalEnv, methodSignature, realGenericTypes) { + super(methodSignature, realGenericTypes); + this.lexicalEnv = lexicalEnv; + } + getLexicalEnv() { + return this.lexicalEnv; + } + getTypeString() { + return 'closures: ' + super.getTypeString(); + } +} +/** + * type of an object + * @category core/base/type + */ +class ClassType extends Type { + classSignature; + realGenericTypes; + constructor(classSignature, realGenericTypes) { + super(); + this.classSignature = classSignature; + this.realGenericTypes = realGenericTypes; + } + getClassSignature() { + return this.classSignature; + } + setClassSignature(newClassSignature) { + this.classSignature = newClassSignature; + } + getRealGenericTypes() { + return this.realGenericTypes; + } + setRealGenericTypes(types) { + this.realGenericTypes = types; + } + getTypeString() { + let temp = this.classSignature.toString(); + let generic = this.realGenericTypes?.join(','); + if (generic) { + temp += `<${generic}>`; + } + return temp; + } +} +/** + * Array type + * @category core/base/type + * @extends Type + * @example + ```typescript + // baseType is number, dimension is 1, readonlyFlag is true + let a: readonly number[] = [1, 2, 3]; + + // baseType is number, dimension is 1, readonlyFlag is undefined + let a: number[] = [1, 2, 3]; + ``` + */ +class ArrayType extends Type { + baseType; + dimension; + readonlyFlag; + constructor(baseType, dimension) { + super(); + this.baseType = baseType; + this.dimension = dimension; + } + /** + * Returns the base type of this array, such as `Any`, `Unknown`, `TypeParameter`, etc. + * @returns The base type of array. + */ + getBaseType() { + return this.baseType; + } + setBaseType(newType) { + this.baseType = newType; + } + getDimension() { + return this.dimension; + } + setReadonlyFlag(readonlyFlag) { + this.readonlyFlag = readonlyFlag; + } + getReadonlyFlag() { + return this.readonlyFlag; + } + getTypeString() { + const strs = []; + if (this.getReadonlyFlag()) { + strs.push('readonly '); + } + if (this.baseType instanceof UnionType || this.baseType instanceof IntersectionType) { + strs.push('(' + this.baseType.toString() + ')'); + } + else if (this.baseType) { + strs.push(this.baseType.toString()); + } + for (let i = 0; i < this.dimension; i++) { + strs.push('[]'); + } + return strs.join(''); + } +} +/** + * Tuple type + * @category core/base/type + * @extends Type + * @example + ```typescript + // types are number and string, dimension is 1, readonlyFlag is true + let a: readonly number[] = [1, 2, 3]; + + // baseType is number, dimension is 1, readonlyFlag is undefined + let a: number[] = [1, 2, 3]; + ``` + */ +class TupleType extends Type { + types; + readonlyFlag; + constructor(types) { + super(); + this.types = types; + } + getTypes() { + return this.types; + } + setReadonlyFlag(readonlyFlag) { + this.readonlyFlag = readonlyFlag; + } + getReadonlyFlag() { + return this.readonlyFlag; + } + getTypeString() { + if (this.getReadonlyFlag()) { + return 'readonly [' + this.types.join(', ') + ']'; + } + return '[' + this.types.join(', ') + ']'; + } +} +/** + * alias type + * @category core/base/type + * @extends Type + * @example + ```typescript + // alias type A is defined without any genericTypes (undefined) or realGenericTypes (undefined) + type A = number; + + // alias type B is defined with genericTypes but not instance with realGenericTypes (undefined) + type B = T[]; + + // alias type could also be defined with another instance generic type such as aliaType, FunctionType and ClassType + // genericTypes and realGenericTypes of C are both undefined + // originalType of C is an instance of B with genericTypes [T] and realGenericTypes [numberType] + type C = B; + ``` + */ +class AliasType extends Type { + originalType; + name; + signature; + modifiers; + genericTypes; + realGenericTypes; + constructor(name, originalType, signature, genericTypes) { + super(); + this.name = name; + this.originalType = originalType; + this.signature = signature; + this.genericTypes = genericTypes; + } + getName() { + return this.name; + } + setOriginalType(type) { + this.originalType = type; + } + getOriginalType() { + return this.originalType; + } + getTypeString() { + let res = this.getSignature().toString(); + let generic = this.getRealGenericTypes()?.join(',') ?? this.getGenericTypes()?.join(','); + if (generic) { + res += `<${generic}>`; + } + return res; + } + getExportType() { + return ExportType.TYPE; + } + getModifiers() { + if (!this.modifiers) { + return 0; + } + return this.modifiers; + } + containsModifier(modifierType) { + if (!this.modifiers) { + return false; + } + return (this.modifiers & modifierType) === modifierType; + } + setModifiers(modifiers) { + if (modifiers !== 0) { + this.modifiers = modifiers; + } + } + addModifier(modifier) { + this.modifiers = this.getModifiers() | modifier; + } + removeModifier(modifier) { + if (!this.modifiers) { + return; + } + this.modifiers &= MODIFIER_TYPE_MASK ^ modifier; + } + getSignature() { + return this.signature; + } + setGenericTypes(genericTypes) { + this.genericTypes = genericTypes; + } + getGenericTypes() { + return this.genericTypes; + } + setRealGenericTypes(realGenericTypes) { + this.realGenericTypes = realGenericTypes; + } + getRealGenericTypes() { + return this.realGenericTypes; + } +} +class GenericType extends Type { + name; + defaultType; + constraint; + index = 0; + constructor(name, defaultType, constraint) { + super(); + this.name = name; + this.defaultType = defaultType; + this.constraint = constraint; + } + getName() { + return this.name; + } + getDefaultType() { + return this.defaultType; + } + setDefaultType(type) { + this.defaultType = type; + } + getConstraint() { + return this.constraint; + } + setConstraint(type) { + this.constraint = type; + } + setIndex(index) { + this.index = index; + } + getIndex() { + return this.index ?? 0; + } + getTypeString() { + let str = this.name; + if (this.constraint) { + str += ' extends ' + this.constraint.toString(); + } + if (this.defaultType) { + str += ' = ' + this.defaultType.toString(); + } + return str; + } +} +class AnnotationType extends Type { + originType; + constructor(originType) { + super(); + this.originType = originType; + } + getOriginType() { + return this.originType; + } + getTypeString() { + return this.originType; + } +} +class AnnotationNamespaceType extends AnnotationType { + namespaceSignature = NamespaceSignature.DEFAULT; + static getInstance(signature) { + const type = new AnnotationNamespaceType(signature.getNamespaceName()); + type.setNamespaceSignature(signature); + return type; + } + getNamespaceSignature() { + return this.namespaceSignature; + } + setNamespaceSignature(signature) { + this.namespaceSignature = signature; + } + constructor(originType) { + super(originType); + } + getOriginType() { + return super.getOriginType(); + } +} +class AnnotationTypeQueryType extends AnnotationType { + constructor(originType) { + super(originType); + } +} +class LexicalEnvType extends Type { + nestedMethodSignature; + closures = []; + constructor(nestedMethod, closures) { + super(); + this.nestedMethodSignature = nestedMethod; + this.closures = closures ?? this.closures; + } + getNestedMethod() { + return this.nestedMethodSignature; + } + getClosures() { + return this.closures; + } + addClosure(closure) { + this.closures.push(closure); + } + getTypeString() { + return `[${this.getClosures().join(', ')}]`; + } +} +class EnumValueType extends Type { + signature; + constant; + constructor(signature, constant) { + super(); + this.signature = signature; + this.constant = constant; + } + getFieldSignature() { + return this.signature; + } + getConstant() { + return this.constant; + } + getTypeString() { + return this.signature.toString(); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const BITWORD_SIZE = 16; // bits of a Word +const DEFAULT_SIZE = 64; +class SparseBitVectorElement { + ELEMENT_SIZE; // bits of element. Default as 128 + BITWORDS_NUM; // number of words + bits; + constructor(elementSize = DEFAULT_SIZE) { + this.ELEMENT_SIZE = elementSize; + this.BITWORDS_NUM = Math.ceil(this.ELEMENT_SIZE / BITWORD_SIZE); + this.bits = new Uint16Array(this.BITWORDS_NUM); + } + word(idx) { + return this.bits[idx]; + } + clone() { + return new Uint16Array(this.bits); + } + get elementSize() { + return this.ELEMENT_SIZE; + } + get bitWordNum() { + return this.BITWORDS_NUM; + } + // Check if the element is empty (all bits are zero) + isEmpty() { + return this.isZero(); + } + // Set a bit at the given index + set(bitIdx) { + const wordIndex = Math.floor(bitIdx / BITWORD_SIZE); + const bitOffset = bitIdx % BITWORD_SIZE; + this.bits[wordIndex] |= 1 << bitOffset; + } + setWord(word) { + this.bits = word; + } + // Reset a bit at the given index + reset(bitIdx) { + const wordIndex = Math.floor(bitIdx / BITWORD_SIZE); + const bitOffset = bitIdx % BITWORD_SIZE; + this.bits[wordIndex] &= ~(1 << bitOffset); + } + // Test if a bit is set + test(bitIdx) { + const wordIndex = Math.floor(bitIdx / BITWORD_SIZE); + const bitOffset = bitIdx % BITWORD_SIZE; + return (this.bits[wordIndex] & (1 << bitOffset)) !== 0; + } + // Set if not existing, else return + test_and_set(bitIdx) { + let old = this.test(bitIdx); + if (!old) { + this.set(bitIdx); + return true; + } + return false; + } + // Count the number of set bits in this element + count() { + let numBits = 0; + this.bits.forEach(word => { + numBits += this.countBits(word); + }); + return numBits; + } + // Find the index of the first set bit in this element + findFirst() { + for (let i = 0; i < this.bits.length; i++) { + if (this.bits[i] !== 0) { + return i * BITWORD_SIZE + this.countTrailingZeros(this.bits[i]); + } + } + return -1; // No bits are set + } + // Find the next set bit after the given index + findNext(bitIdx) { + bitIdx++; + let wordIndex = Math.floor(bitIdx / BITWORD_SIZE); + let bitOffset = bitIdx % BITWORD_SIZE; + // Check the current word + // Mask off previous bits + let word = this.bits[wordIndex] & (-1 << bitOffset); + if (word !== 0) { + return wordIndex * BITWORD_SIZE + this.countTrailingZeros(word); + } + // Check subsequent words + for (let i = wordIndex + 1; i < this.bits.length; i++) { + if (this.bits[i] !== 0) { + return i * BITWORD_SIZE + this.countTrailingZeros(this.bits[i]); + } + } + return -1; // No more bits are set + } + // Comparison + equals(rhs) { + for (let i = 0; i < this.BITWORDS_NUM; i++) { + if (this.bits[i] !== rhs.word(i)) { + return false; + } + } + return true; + } + // Union this element with another element and return true if this one changed + unionWith(other) { + let changed = false; + for (let i = 0; i < this.bits.length; i++) { + const oldWord = changed ? 0 : this.bits[i]; + this.bits[i] |= other.bits[i]; + if (!changed && oldWord !== this.bits[i]) { + changed = true; + } + } + return changed; + } + // Intersect this element with another element and return true if this one changed. + intersectWith(other) { + let changed = false; + for (let i = 0; i < this.bits.length; i++) { + const oldWord = changed ? 0 : this.bits[i]; + this.bits[i] &= other.bits[i]; + if (!changed && oldWord !== this.bits[i]) { + changed = true; + } + } + return changed; + } + // Subtract another SparseBitVectorElement from this one. + subtractWith(rhs) { + let changed = false; + // Perform subtraction: this = this & ~rhs + for (let i = 0; i < this.bits.length; i++) { + const oldWord = this.bits[i]; + this.bits[i] &= ~rhs.bits[i]; + // If any bit was changed, mark as changed + if (this.bits[i] !== oldWord) { + changed = true; + } + } + return changed; + } + // Count the number of set bits in a word + countBitsV2(word) { + let count = 0; + while (word !== 0) { + word &= word - 1; + count++; + } + return count; + } + // Count the number of set bits in a word + countBits(word) { + // assume the value is treated as a unsigned integer + let v = word; + // Step 1: Pairwise addition of bits + v = v - ((v >> 1) & 0x55555555); + // Step 2: Group bits into 4-bit chunks and add + v = (v & 0x33333333) + ((v >> 2) & 0x33333333); + // Step 3: Group bits into 8-bit chunks and add + v = (v + (v >> 4)) & 0xf0f0f0f; + // Step 4: Multiply by a magic number to sum all 8-bit chunks into the highest byte + v = (v * 0x1010101) >> 24; + return v; + } + isZero() { + for (let i = 0; i < this.BITWORDS_NUM; i++) { + if (this.bits[i] !== 0) { + return false; + } + } + return true; + } + // Count trailing zeros in a word + countTrailingZeros(word) { + if (word === 0) { + return BITWORD_SIZE; + } + if ((word & 1) !== 0) { + return 0; + } + let zeroBits = 0; + let shift = BITWORD_SIZE / 2; // Start with half the bit width + let mask = (1 << shift) - 1; // Mask for the lower half + while (shift > 0) { + if ((word & mask) === 0) { + word >>= shift; + zeroBits |= Number(shift); + } + shift >>= 1; + mask >>= shift; + } + return zeroBits; + } +} +class SparseBitVector { + ELEMENT_SIZE; + // Unordered storage of elements. + // key is actually the element index (normally it is in element) + elements = new Map(); + constructor(elementsSize = DEFAULT_SIZE) { + this.ELEMENT_SIZE = elementsSize; + } + get elementSize() { + return this.ELEMENT_SIZE; + } + get elems() { + return this.elements; + } + // Set a bit at the given index + set(bitIdx) { + const elementIndex = Math.floor(bitIdx / this.ELEMENT_SIZE); + let element = this.elements.get(elementIndex); + if (!element) { + element = new SparseBitVectorElement(this.ELEMENT_SIZE); + this.elements.set(elementIndex, element); + } + element.set(bitIdx % this.ELEMENT_SIZE); + } + // Test if a bit is set + test(bitIdx) { + const elementIndex = Math.floor(bitIdx / this.ELEMENT_SIZE); + const element = this.elements.get(elementIndex); + return element ? element.test(bitIdx % this.ELEMENT_SIZE) : false; + } + // Set a bit if not existing. Else return + testAndSet(bitIdx) { + let old = this.test(bitIdx); + if (!old) { + this.set(bitIdx); + return true; + } + return false; + } + // Reset a bit at the given index + reset(bitIdx) { + const elementIndex = Math.floor(bitIdx / this.ELEMENT_SIZE); + let element = this.elements.get(elementIndex); + if (element) { + element.reset(bitIdx % this.ELEMENT_SIZE); + if (element.isEmpty()) { + this.elements.delete(elementIndex); + } + } + } + // Clear all elements + clear() { + this.elements.clear(); + } + // Clone, return a deep copied object + clone() { + const newVector = new SparseBitVector(this.elementSize); + for (const [idx, element] of this.elements) { + const newElement = new SparseBitVectorElement(this.elementSize); + newElement.setWord(element.clone()); + newVector.elems.set(idx, newElement); + } + return newVector; + } + // Find the first set bit in the vector + findFirst() { + if (this.elements.size === 0) { + return -1; + } + const firstElement = this.elements.entries().next().value; + if (firstElement) { + const firstBit = firstElement[1].findFirst(); + return firstElement[0] * this.ELEMENT_SIZE + firstBit; + } + else { + return -1; + } + } + // Count the number of set bits in the vector + count() { + let count = 0; + this.elements.forEach((elem, _) => { + count += elem.count(); + }); + return count; + } + // Check if the vector is empty + isEmpty() { + return this.elements.size === 0; + } + [Symbol.iterator]() { + let iter = this.elements.entries(); + let next = iter.next(); + const elementSize = this.ELEMENT_SIZE; + let element = next.value; + if (!element) { + return { + next() { + return { value: undefined, done: true }; + }, + [Symbol.iterator]() { + return this; // Make the iterator itself iterable + }, + }; + } + let bitIndex = element[1].findFirst(); + return { + next() { + if (element) { + let v = element[0] * elementSize + bitIndex; + bitIndex = element[1].findNext(bitIndex); + if (bitIndex === -1) { + next = iter.next(); + element = next.value; + if (element) { + bitIndex = element[1].findFirst(); + } + } + return { value: v, done: false }; + } + return { value: undefined, done: true }; + }, + [Symbol.iterator]() { + return this; // Make the iterator itself iterable + }, + }; + } + /** + * Check if this SparseBitVector is equal to another SparseBitVector. + */ + equals(rhs) { + if (this.ELEMENT_SIZE !== rhs.ELEMENT_SIZE || this.elems.size !== rhs.elems.size) { + return false; + } + let rhsElems = rhs.elems; + for (let p of this.elements) { + let rhsElem = rhsElems.get(p[0]); + if (!rhsElem) { + return false; + } + if (!rhsElem.equals(p[1])) { + return false; + } + } + return true; + } + /** + * Perform a union operation with another SparseBitVector. + * Returns True if this vector was changed, false otherwise. + */ + unionWith(rhs) { + if (this.equals(rhs) || rhs.elems.size === 0) { + return false; + } + let changed = false; + let newElems = new Map(); + for (let p of rhs.elems) { + let elem = this.elements.get(p[0]); + if (elem) { + changed = elem.unionWith(p[1]) || changed; + } + else { + newElems.set(p[0], p[1]); + } + } + if (newElems.size > 0) { + newElems.forEach((v, k) => this.elements.set(k, v)); + changed = true; + } + return changed; + } + /** + * Perform an intersection operation with another SparseBitVector. + * Returns True if this vector was changed, false otherwise. + */ + intersectWith(rhs) { + if (this.equals(rhs) || rhs.elems.size === 0) { + return false; + } + let changed = false; + // If either vector is empty, the result is empty + if (this.elements.size === 0 || rhs.elems.size === 0) { + if (this.elements.size > 0) { + this.elements = new Map(); + changed = true; + } + return changed; + } + let needDeleteIdx = new Set(); + for (let p of this.elems) { + let elem = rhs.elems.get(p[0]); + if (elem) { + changed = p[1].intersectWith(elem) || changed; + if (changed && p[1].isZero()) { + needDeleteIdx.add(p[0]); + } + } + else { + needDeleteIdx.add(p[0]); + } + } + if (needDeleteIdx.size > 0) { + needDeleteIdx.forEach(idx => this.elements.delete(idx)); + changed = true; + } + return changed; + } + /** + * Subtract another SparseBitVector from this one. + * This operation modifies the current SparseBitVector in place. + * Return True if the current SparseBitVector was modified, false otherwise. + */ + subtractWith(rhs) { + if (this.elementSize !== rhs.elementSize || this.isEmpty() || rhs.isEmpty()) { + return false; + } + let needDeleteIdx = new Set(); + let changed = false; + for (const [elementIndex, element] of this.elements) { + const rhsElement = rhs.elements.get(elementIndex); + if (rhsElement) { + changed = element.subtractWith(rhsElement) || changed; + if (element.isEmpty()) { + needDeleteIdx.add(elementIndex); + } + } + } + if (needDeleteIdx.size > 0) { + needDeleteIdx.forEach(idx => this.elements.delete(idx)); + changed = true; + } + return changed; + } + toString() { + let ar = [...this]; + return ar.toString(); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Return PtsSet or PtsBV 's constructor by input type + */ +function createPtsCollectionCtor(type) { + if (type === PtsCollectionType.Set) { + return PtsSet; + } + else if (type === PtsCollectionType.BitVector) { + return PtsBV; + } + throw new Error(`Unsupported pts collection type: ${type}`); +} +/* + * A simple set to store pts data + */ +class PtsSet { + pts; + constructor() { + this.pts = new Set(); + } + contains(elem) { + return this.pts.has(elem); + } + insert(elem) { + if (this.pts.has(elem)) { + return false; + } + this.pts.add(elem); + return true; + } + remove(elem) { + if (!this.pts.has(elem)) { + return false; + } + this.pts.delete(elem); + return true; + } + clone() { + let clonedSet = new PtsSet(); + clonedSet.pts = new Set(this.pts); + // TODO: need validate + return clonedSet; + } + union(other) { + let changed = false; + for (const elem of other.pts) { + changed = this.insert(elem) || changed; + } + return changed; + } + subtract(other) { + let changed = false; + for (const elem of other.pts) { + changed = this.remove(elem) || changed; + } + return changed; + } + clear() { + this.pts.clear(); + } + count() { + return this.pts.size; + } + isEmpty() { + return this.pts.size === 0; + } + // If current collection is a super set of other + superset(other) { + for (const elem of other.pts) { + if (!this.pts.has(elem)) { + return false; + } + } + return true; + } + // If current collection is intersect with other + intersect(other) { + for (const elem of other.pts) { + if (this.pts.has(elem)) { + return true; + } + } + return false; + } + getProtoPtsSet() { + return this.pts; + } + [Symbol.iterator]() { + return this.pts[Symbol.iterator](); + } +} +class PtsBV { + pts; + constructor() { + this.pts = new SparseBitVector(); + } + contains(elem) { + return this.pts.test(elem); + } + insert(elem) { + this.pts.set(elem); + return true; + } + remove(elem) { + this.pts.reset(elem); + return true; + } + clone() { + let cloned = new PtsBV(); + cloned.pts = this.pts.clone(); + return cloned; + } + union(other) { + return this.pts.unionWith(other.pts); + } + subtract(other) { + return this.pts.subtractWith(other.pts); + } + clear() { + this.pts.clear(); + } + count() { + return this.pts.count(); + } + isEmpty() { + return this.pts.isEmpty(); + } + // If current collection is a super set of other + superset(other) { + for (const elem of other.pts) { + if (!this.pts.test(elem)) { + return false; + } + } + return true; + } + // If current collection is intersect with other + intersect(other) { + for (const elem of other.pts) { + if (this.pts.test(elem)) { + return true; + } + } + return false; + } + getProtoPtsSet() { + return this.pts; + } + [Symbol.iterator]() { + return this.pts[Symbol.iterator](); + } +} +var PtsCollectionType; +(function (PtsCollectionType) { + PtsCollectionType[PtsCollectionType["Set"] = 0] = "Set"; + PtsCollectionType[PtsCollectionType["BitVector"] = 1] = "BitVector"; +})(PtsCollectionType || (PtsCollectionType = {})); +class DiffPTData { + DSCreator; + diffPtsMap; + propaPtsMap; + constructor(DSCreator) { + this.DSCreator = DSCreator; + this.diffPtsMap = new Map(); + this.propaPtsMap = new Map(); + } + clear() { + this.diffPtsMap.clear(); + this.propaPtsMap.clear(); + } + addPts(v, elem) { + let propa = this.propaPtsMap.get(v); + if (propa && propa.contains(elem)) { + return false; + } + let diff = this.diffPtsMap.get(v) || new this.DSCreator(); + this.diffPtsMap.set(v, diff); + return diff.insert(elem); + } + resetElem(v) { + let propa = this.propaPtsMap.get(v); + if (propa) { + this.diffPtsMap.set(v, propa.clone()); + return true; + } + return false; + } + unionDiffPts(dstv, srcv) { + if (dstv === srcv) { + return false; + } + let changed = false; + let diff = this.diffPtsMap.get(srcv); + if (diff) { + let srcDs = diff.clone(); + changed = this.unionPtsTo(dstv, srcDs); + } + return changed; + } + unionPts(dstv, srcv) { + if (dstv === srcv) { + return false; + } + let changed = false; + let diff = this.diffPtsMap.get(srcv); + if (diff) { + let srcDs = diff.clone(); + changed = this.unionPtsTo(dstv, srcDs); + } + let propa = this.propaPtsMap.get(srcv); + if (propa) { + let srcDs = propa.clone(); + changed = this.unionPtsTo(dstv, srcDs) || changed; + } + return changed; + } + unionPtsTo(dstv, srcDs) { + let diff = this.diffPtsMap.get(dstv) || new this.DSCreator(); + let propa = this.propaPtsMap.get(dstv) || new this.DSCreator(); + let newSet = srcDs.clone(); + newSet.subtract(propa); + let changed = diff.union(newSet); + this.diffPtsMap.set(dstv, diff); + return changed; + } + removePtsElem(v, elem) { + let removedFromDiff = this.diffPtsMap.get(v)?.remove(elem) ?? false; + let removedFromPropa = this.propaPtsMap.get(v)?.remove(elem) ?? false; + return removedFromDiff || removedFromPropa; + } + getDiffPts(v) { + return this.diffPtsMap.get(v); + } + getMutDiffPts(v) { + if (!this.diffPtsMap.has(v)) { + this.diffPtsMap.set(v, new this.DSCreator()); + } + return this.diffPtsMap.get(v); + } + getPropaPts(v) { + return this.propaPtsMap.get(v); + } + getAllPropaPts() { + return this.propaPtsMap; + } + getPropaPtsMut(v) { + if (!this.propaPtsMap.has(v)) { + this.propaPtsMap.set(v, new this.DSCreator()); + } + return this.propaPtsMap.get(v); + } + flush(v) { + if (!this.diffPtsMap.has(v)) { + return; + } + let diff = this.diffPtsMap.get(v); + let propa = this.getPropaPtsMut(v); + // do not clear origin propa, only copy the pt and add it to diff + propa.union(diff); + diff.clear(); + } + clearPts(v) { + let diff = this.diffPtsMap.get(v); + if (diff) { + diff.clear(); + } + let propa = this.propaPtsMap.get(v); + if (propa) { + propa.clear(); + } + } + clearDiffPts(v) { + let diff = this.diffPtsMap.get(v); + if (diff) { + diff.clear(); + } + } + clearPropaPts(v) { + let propa = this.propaPtsMap.get(v); + if (propa) { + propa.clear(); + } + } + calculateDiff(src, dst) { + let srcDiff = this.diffPtsMap.get(src); + let dstPropa = this.propaPtsMap.get(dst); + if (!dstPropa) { + return srcDiff.clone(); + } + let result = srcDiff.clone(); + result.subtract(dstPropa); + return result; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$A = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'CG'); +class AbstractAnalysis { + scene; + cg; + cgBuilder; + workList = []; + processedMethod; + constructor(s, cg) { + this.scene = s; + this.cg = cg; + } + getScene() { + return this.scene; + } + getCallGraph() { + return this.cg; + } + resolveInvokeExpr(invokeExpr) { + const method = this.scene.getMethod(invokeExpr.getMethodSignature()); + if (method != null) { + return method; + } + return undefined; + } + getClassHierarchy(arkClass) { + // TODO: remove abstract class + let classWorkList = [arkClass]; + // TODO: check class with no super Class + let classHierarchy = []; + while (classWorkList.length > 0) { + // TODO: no dumplicated check, TS doesn't allow multi extend + let tempClass = classWorkList.shift(); + classWorkList.push(...tempClass.getExtendedClasses().values()); + classHierarchy.push(tempClass); + } + return classHierarchy; + } + start(displayGeneratedMethod) { + this.init(); + while (this.workList.length !== 0) { + const method = this.workList.shift(); + const cgNode = this.cg.getNode(method); + if (this.processedMethod.contains(method) || cgNode.isSdkMethod()) { + continue; + } + // pre process for RTA only + this.preProcessMethod(method).forEach((cs) => { + this.workList.push(cs.calleeFuncID); + }); + this.processMethod(method).forEach((cs) => { + this.processCallSite(method, cs, displayGeneratedMethod); + }); + } + } + projectStart(displayGeneratedMethod) { + this.cgBuilder.buildCGNodes(this.scene.getMethods()); + for (let n of this.cg.getNodesIter()) { + let cgNode = n; + if (cgNode.isSdkMethod()) { + continue; + } + this.preProcessMethod(cgNode.getID()); + this.processMethod(cgNode.getID()).forEach((cs) => { + this.processCallSite(cgNode.getID(), cs, displayGeneratedMethod, true); + }); + } + this.cgBuilder.setEntries(); + } + processCallSite(method, cs, displayGeneratedMethod, isProject = false) { + let me = this.cg.getArkMethodByFuncID(cs.calleeFuncID); + let meNode = this.cg.getNode(cs.calleeFuncID); + this.addCallGraphEdge(method, me, cs, displayGeneratedMethod); + if (isProject) { + return; + } + this.processedMethod.insert(cs.callerFuncID); + if (this.processedMethod.contains(cs.calleeFuncID) || meNode.isSdkMethod()) { + return; + } + if (displayGeneratedMethod || !me?.isGenerated()) { + this.workList.push(cs.calleeFuncID); + logger$A.trace(`New workList item ${cs.calleeFuncID}: ${this.cg.getArkMethodByFuncID(cs.calleeFuncID)?.getSignature().toString()}`); + } + } + init() { + this.processedMethod = new (createPtsCollectionCtor(PtsCollectionType.BitVector))(); + this.cg.getEntries().forEach(entryFunc => { + this.workList.push(entryFunc); + }); + } + processMethod(methodID) { + let cgNode = this.cg.getNode(methodID); + let arkMethod = this.scene.getMethod(cgNode.getMethod(), true); + let calleeMethods = []; + if (!arkMethod) { + throw new Error('can not find method'); + } + const cfg = arkMethod.getCfg(); + if (!cfg) { + return []; + } + cfg.getStmts().forEach(stmt => { + if (stmt.containsInvokeExpr()) { + this.resolveCall(cgNode.getID(), stmt).forEach(callSite => { + calleeMethods.push(callSite); + this.cg.addStmtToCallSiteMap(stmt, callSite); + this.cg.addMethodToCallSiteMap(callSite.calleeFuncID, callSite); + }); + } + }); + return calleeMethods; + } + getParamAnonymousMethod(invokeExpr) { + let paramMethod = []; + invokeExpr.getArgs().forEach(args => { + let argsType = args.getType(); + if (argsType instanceof FunctionType) { + paramMethod.push(argsType.getMethodSignature()); + } + }); + return paramMethod; + } + addCallGraphEdge(caller, callee, cs, displayGeneratedMethod) { + // check if need to display generated method + if (!callee) { + logger$A.error(`FuncID has no method ${cs.calleeFuncID}`); + } + else { + if (displayGeneratedMethod || !callee?.isGenerated()) { + this.cg.addDynamicCallEdge(caller, cs.calleeFuncID, cs.callStmt); + } + } + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class ArkSignatureBuilder { + static buildMethodSignatureFromClassNameAndMethodName(className, methodName, staticFlag = false) { + const classSignature = this.buildClassSignatureFromClassName(className); + const methodSubSignature = this.buildMethodSubSignatureFromMethodName(methodName, staticFlag); + return new MethodSignature(classSignature, methodSubSignature); + } + static buildMethodSignatureFromMethodName(methodName, staticFlag = false) { + const methodSubSignature = this.buildMethodSubSignatureFromMethodName(methodName, staticFlag); + return new MethodSignature(ClassSignature.DEFAULT, methodSubSignature); + } + static buildMethodSubSignatureFromMethodName(methodName, staticFlag = false) { + return new MethodSubSignature(methodName, [], UnknownType.getInstance(), staticFlag); + } + static buildClassSignatureFromClassName(className) { + return new ClassSignature(className, FileSignature.DEFAULT); + } + static buildFieldSignatureFromFieldName(fieldName, staticFlag = false) { + return new FieldSignature(fieldName, ClassSignature.DEFAULT, UnknownType.getInstance(), staticFlag); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Replace old use of a Expr inplace + */ +class ExprUseReplacer { + oldUse; + newUse; + constructor(oldUse, newUse) { + this.oldUse = oldUse; + this.newUse = newUse; + } + caseExpr(expr) { + if (expr instanceof AbstractBinopExpr) { + this.caseBinopExpr(expr); + } + else if (expr instanceof AbstractInvokeExpr) { + this.caseInvokeExpr(expr); + } + else if (expr instanceof ArkNewArrayExpr) { + this.caseNewArrayExpr(expr); + } + else if (expr instanceof ArkTypeOfExpr) { + this.caseTypeOfExpr(expr); + } + else if (expr instanceof ArkInstanceOfExpr) { + this.caseInstanceOfExpr(expr); + } + else if (expr instanceof ArkCastExpr) { + this.caseCastExpr(expr); + } + else if (expr instanceof ArkAwaitExpr) { + this.caseAwaitExpr(expr); + } + else if (expr instanceof ArkYieldExpr) { + this.caseYieldExpr(expr); + } + else if (expr instanceof ArkDeleteExpr) { + this.caseDeleteExpr(expr); + } + else if (expr instanceof ArkUnopExpr) { + this.caseUnopExpr(expr); + } + } + caseBinopExpr(expr) { + if (expr.getOp1() === this.oldUse) { + expr.setOp1(this.newUse); + } + if (expr.getOp2() === this.oldUse) { + expr.setOp2(this.newUse); + } + } + caseInvokeExpr(expr) { + let args = expr.getArgs(); + for (let i = 0; i < args.length; i++) { + if (args[i] === this.oldUse) { + args[i] = this.newUse; + } + } + if (expr instanceof ArkInstanceInvokeExpr && expr.getBase() === this.oldUse) { + expr.setBase(this.newUse); + } + else if (expr instanceof ArkPtrInvokeExpr && expr.getFuncPtrLocal() === this.oldUse && this.newUse instanceof Local) { + expr.setFunPtrLocal(this.newUse); + } + } + caseNewArrayExpr(expr) { + if (expr.getSize() === this.oldUse) { + expr.setSize(this.newUse); + } + } + caseTypeOfExpr(expr) { + if (expr.getOp() === this.oldUse) { + expr.setOp(this.newUse); + } + } + caseInstanceOfExpr(expr) { + if (expr.getOp() === this.oldUse) { + expr.setOp(this.newUse); + } + } + caseCastExpr(expr) { + if (expr.getOp() === this.oldUse) { + expr.setOp(this.newUse); + } + } + caseAwaitExpr(expr) { + if (expr.getPromise() === this.oldUse) { + expr.setPromise(this.newUse); + } + } + caseDeleteExpr(expr) { + if (expr.getField() === this.oldUse && this.newUse instanceof AbstractFieldRef) { + expr.setField(this.newUse); + } + } + caseYieldExpr(expr) { + if (expr.getYieldValue() === this.oldUse) { + expr.setYieldValue(this.newUse); + } + } + caseUnopExpr(expr) { + if (expr.getOp() === this.oldUse) { + expr.setOp(this.newUse); + } + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Replace old use of a Ref inplace + */ +class RefUseReplacer { + oldUse; + newUse; + constructor(oldUse, newUse) { + this.oldUse = oldUse; + this.newUse = newUse; + } + caseRef(ref) { + if (ref instanceof ArkInstanceFieldRef) { + this.caseFieldRef(ref); + } + else if (ref instanceof ArkArrayRef) { + this.caseArrayRef(ref); + } + } + caseFieldRef(ref) { + if (ref.getBase() === this.oldUse && this.newUse instanceof Local) { + ref.setBase(this.newUse); + } + } + caseArrayRef(ref) { + if (ref.getBase() === this.oldUse) { + if (this.newUse instanceof Local) { + ref.setBase(this.newUse); + } + } + else if (ref.getIndex() === this.oldUse) { + ref.setIndex(this.newUse); + } + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class IRUtils { + static moreThanOneAddress(value) { + if (value instanceof AbstractBinopExpr || + value instanceof AbstractInvokeExpr || + value instanceof AbstractFieldRef || + value instanceof ArkArrayRef || + value instanceof ArkCastExpr || + value instanceof ArkUnopExpr) { + return true; + } + return false; + } + static generateTextForStmt(scene) { + for (const method of scene.getMethods()) { + const cfg = method.getCfg(); + if (cfg) { + for (const stmt of cfg.getStmts()) { + stmt.setText(stmt.toString()); + } + } + } + } + static setComments(metadata, node, sourceFile, options) { + const leadingCommentsMetadata = this.getCommentsMetadata(node, sourceFile, options, true); + if (leadingCommentsMetadata.getComments().length > 0) { + metadata.setMetadata(ArkMetadataKind.LEADING_COMMENTS, leadingCommentsMetadata); + } + const trailingCommentsMetadata = this.getCommentsMetadata(node, sourceFile, options, false); + if (trailingCommentsMetadata.getComments().length > 0) { + metadata.setMetadata(ArkMetadataKind.TRAILING_COMMENTS, trailingCommentsMetadata); + } + } + static getCommentsMetadata(node, sourceFile, options, isLeading) { + const comments = []; + if ((isLeading && !options.enableLeadingComments) || (!isLeading && !options.enableTrailingComments)) { + return new CommentsMetadata(comments); + } + // node.pos is the start position of + const commentRanges = (isLeading ? ts.getLeadingCommentRanges(sourceFile.text, node.pos) : ts.getTrailingCommentRanges(sourceFile.text, node.end)) || []; + // leading comment, while node.end is the + // end position of the statement + const getPosition = (pos, end) => { + const start = ts.getLineAndCharacterOfPosition(sourceFile, pos); + const endPos = ts.getLineAndCharacterOfPosition(sourceFile, end); + return new FullPosition(start.line + 1, start.character + 1, endPos.line + 1, endPos.character + 1); + }; + for (const range of commentRanges) { + comments.push({ + content: sourceFile.text.substring(range.pos, range.end).replace(/\r\n/g, '\n'), + position: getPosition(range.pos, range.end), + }); + } + return new CommentsMetadata(comments); + } + static isTempLocal(value) { + return value instanceof Local && value.getName().startsWith(NAME_PREFIX); + } + static findOperandIdx(stmt, operand) { + let index = -1; + const operands = stmt.getDefAndUses(); + for (let i = 0; i < operands.length; i++) { + if (operands[i] === operand) { + index = i; + break; + } + } + return index; + } + static adjustOperandOriginalPositions(stmt, oldValue, newValue) { + const operandOriginalPositions = stmt.getOperandOriginalPositions(); + if (!operandOriginalPositions) { + return; + } + const operandOriginalPositionSize = operandOriginalPositions.length; + const defUseSize = stmt.getDefAndUses().length; + const oldValueUseSize = oldValue.getUses().length; + const newValueUseSize = newValue.getUses().length; + const oldValueIdx = IRUtils.findOperandIdx(stmt, oldValue); + const baseValueOffset = 1; + const fieldValueOffset = 2; + if (oldValue instanceof AbstractRef && newValue instanceof AbstractRef) { + if (newValue instanceof ArkStaticFieldRef) { + operandOriginalPositions.splice(oldValueIdx + baseValueOffset, oldValueUseSize - newValueUseSize); + } + else if (oldValue instanceof ArkStaticFieldRef) { + operandOriginalPositions.splice(oldValueIdx + baseValueOffset, 0, ...IRUtils.generateDefaultPositions(newValueUseSize - oldValueUseSize)); + } + if (oldValue instanceof ArkInstanceFieldRef && newValue instanceof ArkArrayRef) { + if (operandOriginalPositionSize === defUseSize) { + // may not reserve positions for field name + operandOriginalPositions.splice(oldValueIdx + fieldValueOffset, 0, ...IRUtils.generateDefaultPositions(newValueUseSize - oldValueUseSize)); + } + } + else if (oldValue instanceof ArkArrayRef && newValue instanceof ArkInstanceFieldRef) { + operandOriginalPositions.splice(oldValueIdx + fieldValueOffset, oldValueUseSize - newValueUseSize); + } + } + else if (oldValue instanceof AbstractInvokeExpr && newValue instanceof AbstractInvokeExpr) { + if (oldValueUseSize === newValueUseSize + 1) { + operandOriginalPositions.splice(oldValueIdx + baseValueOffset, 1); + } + else if (oldValueUseSize === newValueUseSize - 1) { + operandOriginalPositions.splice(oldValueIdx + baseValueOffset, 0, FullPosition.DEFAULT); + } + } + } + static generateDefaultPositions(count) { + const defaultPositions = []; + for (let i = 0; i < count; i++) { + defaultPositions.push(FullPosition.DEFAULT); + } + return defaultPositions; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Replace old use(Value) of a Stmt inplace + */ +class StmtUseReplacer { + oldUse; + newUse; + constructor(oldUse, newUse) { + this.oldUse = oldUse; + this.newUse = newUse; + } + caseStmt(stmt) { + if (stmt instanceof ArkAssignStmt) { + this.caseAssignStmt(stmt); + } + else if (stmt instanceof ArkInvokeStmt) { + this.caseInvokeStmt(stmt); + } + else if (stmt instanceof ArkReturnStmt) { + this.caseReturnStmt(stmt); + } + else if (stmt instanceof ArkIfStmt) { + this.caseIfStmt(stmt); + } + else if (stmt instanceof ArkThrowStmt) { + this.caseThrowStmt(stmt); + } + } + caseAssignStmt(stmt) { + const lValue = stmt.getLeftOp(); + if (lValue instanceof AbstractRef) { + const refUseReplacer = new RefUseReplacer(this.oldUse, this.newUse); + refUseReplacer.caseRef(lValue); + } + const rValue = stmt.getRightOp(); + if (rValue === this.oldUse) { + IRUtils.adjustOperandOriginalPositions(stmt, this.oldUse, this.newUse); + stmt.setRightOp(this.newUse); + } + else if (rValue instanceof AbstractRef) { + const refUseReplacer = new RefUseReplacer(this.oldUse, this.newUse); + refUseReplacer.caseRef(rValue); + } + else if (rValue instanceof AbstractExpr) { + const exprUseReplacer = new ExprUseReplacer(this.oldUse, this.newUse); + exprUseReplacer.caseExpr(rValue); + } + } + caseInvokeStmt(stmt) { + const invokeExpr = stmt.getInvokeExpr(); + if (invokeExpr === this.oldUse) { + if (this.newUse instanceof AbstractInvokeExpr) { + IRUtils.adjustOperandOriginalPositions(stmt, this.oldUse, this.newUse); + stmt.replaceInvokeExpr(this.newUse); + } + } + else { + let exprUseReplacer = new ExprUseReplacer(this.oldUse, this.newUse); + exprUseReplacer.caseExpr(stmt.getInvokeExpr()); + } + } + caseReturnStmt(stmt) { + if (stmt.getOp() === this.oldUse) { + IRUtils.adjustOperandOriginalPositions(stmt, this.oldUse, this.newUse); + stmt.setReturnValue(this.newUse); + } + } + caseIfStmt(stmt) { + const conditionExpr = stmt.getConditionExpr(); + if (conditionExpr === this.oldUse) { + if (this.newUse instanceof ArkConditionExpr) { + IRUtils.adjustOperandOriginalPositions(stmt, this.oldUse, this.newUse); + stmt.setConditionExpr(this.newUse); + } + } + else { + let exprUseReplacer = new ExprUseReplacer(this.oldUse, this.newUse); + exprUseReplacer.caseExpr(stmt.getConditionExpr()); + } + } + caseThrowStmt(stmt) { + if (stmt.getOp() === this.oldUse) { + IRUtils.adjustOperandOriginalPositions(stmt, this.oldUse, this.newUse); + stmt.setOp(this.newUse); + } + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Replace old def(Value) of a Stmt inplace + */ +class StmtDefReplacer { + oldDef; + newDef; + constructor(oldDef, newDef) { + this.oldDef = oldDef; + this.newDef = newDef; + } + caseStmt(stmt) { + if (stmt instanceof ArkAssignStmt) { + this.caseAssignStmt(stmt); + } + } + caseAssignStmt(stmt) { + const lValue = stmt.getLeftOp(); + if (lValue === this.oldDef) { + IRUtils.adjustOperandOriginalPositions(stmt, this.oldDef, this.newDef); + stmt.setLeftOp(this.newDef); + } + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var FieldCategory; +(function (FieldCategory) { + FieldCategory[FieldCategory["PROPERTY_DECLARATION"] = 0] = "PROPERTY_DECLARATION"; + FieldCategory[FieldCategory["PROPERTY_ASSIGNMENT"] = 1] = "PROPERTY_ASSIGNMENT"; + FieldCategory[FieldCategory["SHORT_HAND_PROPERTY_ASSIGNMENT"] = 2] = "SHORT_HAND_PROPERTY_ASSIGNMENT"; + FieldCategory[FieldCategory["SPREAD_ASSIGNMENT"] = 3] = "SPREAD_ASSIGNMENT"; + FieldCategory[FieldCategory["PROPERTY_SIGNATURE"] = 4] = "PROPERTY_SIGNATURE"; + FieldCategory[FieldCategory["ENUM_MEMBER"] = 5] = "ENUM_MEMBER"; + FieldCategory[FieldCategory["INDEX_SIGNATURE"] = 6] = "INDEX_SIGNATURE"; + FieldCategory[FieldCategory["GET_ACCESSOR"] = 7] = "GET_ACCESSOR"; + FieldCategory[FieldCategory["PARAMETER_PROPERTY"] = 8] = "PARAMETER_PROPERTY"; +})(FieldCategory || (FieldCategory = {})); +/** + * @category core/model + */ +class ArkField extends ArkBaseModel { + code = ''; + category; + declaringClass; + questionToken = false; + exclamationToken = false; + fieldSignature; + originPosition; + initializer = []; + constructor() { + super(); + } + /** + * Returns the program language of the file where this field's class defined. + */ + getLanguage() { + return this.getDeclaringArkClass().getLanguage(); + } + getDeclaringArkClass() { + return this.declaringClass; + } + setDeclaringArkClass(declaringClass) { + this.declaringClass = declaringClass; + } + /** + * Returns the codes of field as a **string.** + * @returns the codes of field. + */ + getCode() { + return this.code; + } + setCode(code) { + this.code = code; + } + getCategory() { + return this.category; + } + setCategory(category) { + this.category = category; + } + getName() { + return this.fieldSignature.getFieldName(); + } + getType() { + return this.fieldSignature.getType(); + } + getSignature() { + return this.fieldSignature; + } + setSignature(fieldSig) { + this.fieldSignature = fieldSig; + } + /** + * Returns an array of statements used for initialization. + * @returns An array of statements used for initialization. + */ + getInitializer() { + return this.initializer; + } + setInitializer(initializer) { + this.initializer = initializer; + } + setQuestionToken(questionToken) { + this.questionToken = questionToken; + } + setExclamationToken(exclamationToken) { + this.exclamationToken = exclamationToken; + } + getQuestionToken() { + return this.questionToken; + } + getExclamationToken() { + return this.exclamationToken; + } + setOriginPosition(position) { + this.originPosition = position; + } + /** + * Returns the original position of the field at source code. + * @returns The original position of the field at source code. + */ + getOriginPosition() { + return this.originPosition ?? LineColPosition.DEFAULT; + } + validate() { + return this.validateFields(['category', 'declaringClass', 'fieldSignature']); + } + // For class field, it is default public if there is not any access modify + isPublic() { + if (!this.containsModifier(ModifierType.PUBLIC) && + !this.containsModifier(ModifierType.PRIVATE) && + !this.containsModifier(ModifierType.PROTECTED) && + this.getDeclaringArkClass().getCategory() === ClassCategory.CLASS) { + return true; + } + return this.containsModifier(ModifierType.PUBLIC); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class Builtin { + // built-in classes + // TODO: Automatically obtain from the standard library + static OBJECT = 'Object'; + static ARRAY = 'Array'; + static SET = 'Set'; + static MAP = 'Map'; + static REGEXP = 'RegExp'; + static BUILT_IN_CLASSES = this.buildBuiltInClasses(); + // signature for built-in class + static DUMMY_PROJECT_NAME = 'ES2015'; + static DUMMY_FILE_NAME = 'BuiltinClass'; + static BUILT_IN_CLASSES_FILE_SIGNATURE = Builtin.buildBuiltInClassesFileSignature(); + static OBJECT_CLASS_SIGNATURE = this.buildBuiltInClassSignature(this.OBJECT); + static OBJECT_CLASS_TYPE = new ClassType(this.OBJECT_CLASS_SIGNATURE); + static ARRAY_CLASS_SIGNATURE = this.buildBuiltInClassSignature(this.ARRAY); + static SET_CLASS_SIGNATURE = this.buildBuiltInClassSignature(this.SET); + static MAP_CLASS_SIGNATURE = this.buildBuiltInClassSignature(this.MAP); + static REGEXP_CLASS_SIGNATURE = this.buildBuiltInClassSignature(this.REGEXP); + static REGEXP_CLASS_TYPE = new ClassType(this.REGEXP_CLASS_SIGNATURE); + static BUILT_IN_CLASS_SIGNATURE_MAP = this.buildBuiltInClassSignatureMap(); + // constants for iterator + static ITERATOR_FUNCTION = 'iterator'; + static ITERATOR = 'Iterator'; + static ITERATOR_NEXT = 'next'; + static ITERATOR_RESULT = 'IteratorResult'; + static ITERATOR_RESULT_DONE = 'done'; + static ITERATOR_RESULT_VALUE = 'value'; + static ITERATOR_CLASS_SIGNATURE = this.buildBuiltInClassSignature(this.ITERATOR); + static ITERATOR_RESULT_CLASS_SIGNATURE = this.buildBuiltInClassSignature(this.ITERATOR_RESULT); + static ITERATOR_CLASS_TYPE = new ClassType(this.ITERATOR_CLASS_SIGNATURE, [new GenericType('T')]); + static ITERATOR_RESULT_CLASS_TYPE = new ClassType(this.ITERATOR_RESULT_CLASS_SIGNATURE, [new GenericType('T')]); + // constants for string + static TO_STRING = 'toString'; + static TO_STRING_METHOD_SIGNATURE = new MethodSignature(ClassSignature.DEFAULT, new MethodSubSignature(this.TO_STRING, [], StringType.getInstance(), false)); + static buildBuiltInClasses() { + const builtInClasses = new Set(); + builtInClasses.add(this.OBJECT); + builtInClasses.add(this.ARRAY); + builtInClasses.add(this.SET); + builtInClasses.add(this.MAP); + builtInClasses.add(this.REGEXP); + return builtInClasses; + } + static buildBuiltInClassesFileSignature() { + return new FileSignature(this.DUMMY_PROJECT_NAME, this.DUMMY_FILE_NAME); + } + static buildBuiltInClassSignature(className) { + return new ClassSignature(className, this.BUILT_IN_CLASSES_FILE_SIGNATURE); + } + static buildBuiltInClassSignatureMap() { + const builtInClassSignatureMap = new Map(); + builtInClassSignatureMap.set(this.OBJECT, this.OBJECT_CLASS_SIGNATURE); + builtInClassSignatureMap.set(this.ARRAY, this.ARRAY_CLASS_SIGNATURE); + builtInClassSignatureMap.set(this.SET, this.SET_CLASS_SIGNATURE); + builtInClassSignatureMap.set(this.MAP, this.MAP_CLASS_SIGNATURE); + builtInClassSignatureMap.set(this.REGEXP, this.REGEXP_CLASS_SIGNATURE); + return builtInClassSignatureMap; + } + static isBuiltinClass(className) { + return this.BUILT_IN_CLASSES.has(className); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @category core/base + */ +class Constant { + value; + type; + constructor(value, type) { + this.value = value; + this.type = type; + } + /** + * Returns the constant's value as a **string**. + * @returns The constant's value. + */ + getValue() { + return this.value; + } + getUses() { + return []; + } + /** + * Returns the type of this constant. + * @returns The type of this constant. + */ + getType() { + return this.type; + } + /** + * Get a string of constant value in Constant. + * @returns The string of constant value. + */ + toString() { + let str = ''; + if (this.type instanceof StringType) { + str = "'" + this.value + "'"; + } + else { + str = this.value; + } + return str; + } +} +class BooleanConstant extends Constant { + static FALSE = new BooleanConstant(false); + static TRUE = new BooleanConstant(true); + constructor(value) { + super(value.toString(), BooleanType.getInstance()); + } + static getInstance(value) { + return value ? this.TRUE : this.FALSE; + } +} +class NumberConstant extends Constant { + constructor(value) { + super(value.toString(), NumberType.getInstance()); + } +} +class BigIntConstant extends Constant { + constructor(value) { + super(value.toString(), BigIntType.getInstance()); + } +} +class StringConstant extends Constant { + constructor(value) { + super(value.toString(), StringType.getInstance()); + } +} +class NullConstant extends Constant { + static INSTANCE = new NullConstant(); + constructor() { + super(NULL_KEYWORD, NullType.getInstance()); + } + static getInstance() { + return this.INSTANCE; + } +} +class UndefinedConstant extends Constant { + static INSTANCE = new UndefinedConstant(); + constructor() { + super(UNDEFINED_KEYWORD, UndefinedType.getInstance()); + } + static getInstance() { + return this.INSTANCE; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const EMPTY_STRING = ''; +class ValueUtil { + static NumberConstantCache = new Map(); + static EMPTY_STRING_CONSTANT = new StringConstant(EMPTY_STRING); + static getOrCreateNumberConst(n) { + let constant = this.NumberConstantCache.get(n); + if (constant === undefined) { + constant = new NumberConstant(n); + this.NumberConstantCache.set(n, constant); + } + return constant; + } + static createBigIntConst(bigInt) { + return new BigIntConstant(bigInt); + } + static createStringConst(str) { + if (str === EMPTY_STRING) { + return this.EMPTY_STRING_CONSTANT; + } + return new StringConstant(str); + } + static createConst(str) { + const n = Number(str); + if (!isNaN(n)) { + return this.getOrCreateNumberConst(n); + } + return new StringConstant(str); + } + static getUndefinedConst() { + return UndefinedConstant.getInstance(); + } + static getNullConstant() { + return NullConstant.getInstance(); + } + static getBooleanConstant(value) { + return BooleanConstant.getInstance(value); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$z = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'IRInference'); +class IRInference { + static inferExportInfos(file) { + file.getExportInfos().forEach(exportInfo => { + if (exportInfo.getArkExport() === undefined) { + let arkExport = findArkExport(exportInfo); + exportInfo.setArkExport(arkExport); + if (arkExport) { + exportInfo.setExportClauseType(arkExport.getExportType()); + } + } + }); + file.getNamespaces().forEach(namespace => { + namespace.getExportInfos().forEach(exportInfo => { + if (exportInfo.getArkExport() === undefined) { + let arkExport = findArkExport(exportInfo); + exportInfo.setArkExport(arkExport); + arkExport !== null ? exportInfo.setExportClauseType(arkExport.getExportType()) : true; + } + }); + }); + } + static inferImportInfos(file) { + file.getImportInfos().forEach(importInfo => { + importInfo.getLazyExportInfo(); + }); + } + static inferFile(file) { + this.inferImportInfos(file); + ModelUtils.getAllClassesInFile(file).forEach(arkClass => { + TypeInference.inferGenericType(arkClass.getGenericsTypes(), arkClass); + const defaultArkMethod = arkClass.getDefaultArkMethod(); + if (defaultArkMethod) { + TypeInference.inferTypeInMethod(defaultArkMethod); + } + arkClass.getFields().forEach(arkField => TypeInference.inferTypeInArkField(arkField)); + const methods = arkClass.getMethods().sort((a, b) => { + const name = a.getName().split(NAME_DELIMITER).reverse().join(); + const anotherName = b.getName().split(NAME_DELIMITER).reverse().join(); + if (name.startsWith(anotherName)) { + return 1; + } + else if (anotherName.startsWith(name)) { + return -1; + } + return 0; + }); + arkClass.getAllHeritageClasses(); + methods.forEach(arkMethod => TypeInference.inferTypeInMethod(arkMethod)); + }); + this.inferExportInfos(file); + } + static inferStaticInvokeExpr(expr, arkMethod) { + const arkClass = arkMethod.getDeclaringArkClass(); + const methodName = expr.getMethodSignature().getMethodSubSignature().getMethodName(); + expr.getArgs().forEach(arg => TypeInference.inferValueType(arg, arkMethod)); + if (methodName === IMPORT) { + const arg = expr.getArg(0); + let type; + if (arg instanceof Constant) { + type = TypeInference.inferDynamicImportType(arg.getValue(), arkClass); + } + if (type) { + expr.getMethodSignature().getMethodSubSignature().setReturnType(type); + } + return expr; + } + else if (methodName === SUPER_NAME) { + const superClass = arkClass.getSuperClass(); + if (superClass !== null) { + const newMethodSignature = new MethodSignature(superClass.getSignature(), expr.getMethodSignature().getMethodSubSignature()); + expr.setMethodSignature(newMethodSignature); + } + return expr; + } + const className = expr.getMethodSignature().getDeclaringClassSignature().getClassName(); + if (className && className !== UNKNOWN_CLASS_NAME) { + const baseType = TypeInference.inferBaseType(className, arkClass); + if (baseType) { + let result = this.inferInvokeExpr(expr, baseType, methodName, arkClass.getDeclaringArkFile().getScene()); + if (result) { + this.inferArgs(result, arkMethod); + return result; + } + } + return expr; + } + return this.inferStaticInvokeExprByMethodName(methodName, arkMethod, expr); + } + static inferStaticInvokeExprByMethodName(methodName, arkMethod, expr) { + const arkClass = arkMethod.getDeclaringArkClass(); + const arkExport = ModelUtils.getStaticMethodWithName(methodName, arkClass) ?? + arkMethod.getFunctionLocal(methodName) ?? + ModelUtils.findDeclaredLocal(new Local(methodName), arkMethod) ?? + ModelUtils.getArkExportInImportInfoWithName(methodName, arkClass.getDeclaringArkFile()) ?? + arkClass.getDeclaringArkFile().getScene().getSdkGlobal(methodName); + let method; + let signature; + if (arkExport instanceof ArkMethod) { + method = arkExport; + } + else if (arkExport instanceof ArkClass) { + method = arkExport.getMethodWithName(CONSTRUCTOR_NAME); + } + else if (arkExport instanceof Local) { + const type = TypeInference.replaceAliasType(arkExport.getType()); + if (type instanceof ClassType) { + const cls = arkClass.getDeclaringArkFile().getScene().getClass(type.getClassSignature()); + method = cls?.getMethodWithName(CONSTRUCTOR_NAME) ?? cls?.getMethodWithName(CALL_SIGNATURE_NAME); + } + else if (type instanceof FunctionType) { + signature = type.getMethodSignature(); + } + } + else if (arkExport instanceof AliasType && arkExport.getOriginalType() instanceof FunctionType) { + signature = arkExport.getOriginalType().getMethodSignature(); + } + if (method) { + signature = method.matchMethodSignature(expr.getArgs()); + TypeInference.inferSignatureReturnType(signature, method); + } + if (signature) { + if (arkExport instanceof Local) { + expr = new ArkPtrInvokeExpr(signature, arkExport, expr.getArgs(), expr.getRealGenericTypes()); + } + else { + expr.setMethodSignature(signature); + } + this.inferArgs(expr, arkMethod); + } + return expr; + } + static inferInstanceInvokeExpr(expr, arkMethod) { + const arkClass = arkMethod.getDeclaringArkClass(); + TypeInference.inferRealGenericTypes(expr.getRealGenericTypes(), arkClass); + this.inferBase(expr, arkMethod); + const baseType = TypeInference.replaceAliasType(expr.getBase().getType()); + let methodName = expr.getMethodSignature().getMethodSubSignature().getMethodName(); + if (methodName.startsWith(NAME_PREFIX)) { + const declaringStmt = arkMethod.getBody()?.getLocals().get(methodName)?.getDeclaringStmt(); + if (declaringStmt instanceof ArkAssignStmt && declaringStmt.getRightOp() instanceof ArkInstanceFieldRef) { + const rightOp = declaringStmt.getRightOp(); + methodName = rightOp.getBase().getName() + '.' + rightOp.getFieldName(); + } + } + const scene = arkClass.getDeclaringArkFile().getScene(); + if (methodName === 'forEach' && baseType instanceof ArrayType) { + this.processForEach(expr.getArg(0), baseType, scene); + return expr; + } + expr.getArgs().forEach(arg => TypeInference.inferValueType(arg, arkMethod)); + let result = this.inferInvokeExpr(expr, baseType, methodName, scene) ?? this.processExtendFunc(expr, arkMethod, methodName); + if (result) { + this.inferArgs(result, arkMethod); + return result; + } + logger$z.warn('invoke ArkInstanceInvokeExpr MethodSignature type fail: ', expr.toString()); + return expr; + } + /** + * process arkUI function with Annotation @Extend @Styles @AnimatableExtend + * @param expr + * @param arkMethod + * @param methodName + */ + static processExtendFunc(expr, arkMethod, methodName) { + const type = TypeInference.inferBaseType(methodName, arkMethod.getDeclaringArkClass()); + if (type instanceof FunctionType) { + const methodSignature = type.getMethodSignature(); + // because of last stmt is ArkReturnVoidStmt, the ArkInvokeStmt at -2 before ArkReturnVoidStmt. + const endIndex = -2; + const endStmt = arkMethod.getDeclaringArkFile().getScene().getMethod(methodSignature)?.getCfg()?.getStmts().at(endIndex); + if (endStmt instanceof ArkInvokeStmt) { + methodSignature.getMethodSubSignature().setReturnType(endStmt.getInvokeExpr().getType()); + } + expr.setMethodSignature(methodSignature); + return expr; + } + return null; + } + static inferFieldRef(ref, arkMethod) { + this.inferBase(ref, arkMethod); + const baseType = TypeInference.replaceAliasType(ref.getBase().getType()); + if (baseType instanceof ArrayType && ref.getFieldName() !== 'length') { + return new ArkArrayRef(ref.getBase(), ValueUtil.createConst(ref.getFieldName())); + } + let newFieldSignature = this.generateNewFieldSignature(ref, arkMethod.getDeclaringArkClass(), baseType); + if (newFieldSignature) { + if (newFieldSignature.isStatic()) { + return new ArkStaticFieldRef(newFieldSignature); + } + ref.setFieldSignature(newFieldSignature); + } + return ref; + } + static inferBase(instance, arkMethod) { + const base = instance.getBase(); + if (base.getName() === THIS_NAME) { + const name = instance instanceof ArkInstanceFieldRef ? instance.getFieldName() : + instance.getMethodSignature().getMethodSubSignature().getMethodName(); + if (name.includes('.')) { + return; + } + const declaringArkClass = arkMethod.getDeclaringArkClass(); + if (declaringArkClass.isAnonymousClass()) { + let newBase = this.inferThisLocal(arkMethod); + if (newBase) { + instance.setBase(newBase); + } + } + else if (base.getType() instanceof UnknownType) { + base.setType(new ClassType(declaringArkClass.getSignature(), declaringArkClass.getRealTypes())); + } + } + else { + this.inferLocal(instance.getBase(), arkMethod); + } + } + static inferThisLocal(arkMethod) { + const arkClass = arkMethod.getDeclaringArkClass(); + if (!arkClass.isAnonymousClass()) { + return null; + } + const value = arkMethod.getBody()?.getUsedGlobals()?.get(THIS_NAME); + if (value instanceof Local) { + return value; + } + else { + const thisType = TypeInference.inferBaseType(arkClass.getSignature().getDeclaringClassName(), arkClass); + if (thisType instanceof ClassType) { + const newBase = new Local(THIS_NAME, thisType); + let usedGlobals = arkMethod.getBody()?.getUsedGlobals(); + if (!usedGlobals) { + usedGlobals = new Map(); + arkMethod.getBody()?.setUsedGlobals(usedGlobals); + } + usedGlobals.set(THIS_NAME, newBase); + return newBase; + } + } + return null; + } + static inferArgs(expr, arkMethod) { + const scene = arkMethod.getDeclaringArkFile().getScene(); + const parameters = expr.getMethodSignature().getMethodSubSignature().getParameters(); + let realTypes = []; + const len = expr.getArgs().length; + for (let index = 0; index < len; index++) { + const arg = expr.getArg(index); + if (index >= parameters.length) { + break; + } + const argType = arg.getType(); + const paramType = parameters[index].getType(); + this.inferArg(expr, argType, paramType, scene, realTypes); + } + if (realTypes.length > 0 && !expr.getRealGenericTypes()) { + expr.setRealGenericTypes(realTypes); + } + } + static inferArg(expr, argType, paramType, scene, realTypes) { + if (paramType instanceof UnionType) { + paramType.getTypes().forEach(t => this.inferArg(expr, argType, t, scene, realTypes)); + } + else if (paramType instanceof AliasType) { + this.inferArg(expr, argType, paramType.getOriginalType(), scene, realTypes); + } + else if (paramType instanceof ArrayType && argType instanceof ArrayType) { + this.inferArg(expr, argType.getBaseType(), paramType.getBaseType(), scene, realTypes); + } + else if (expr instanceof ArkInstanceInvokeExpr && expr.getBase().getType() instanceof ArrayType) { + if (paramType instanceof ArrayType && paramType.getBaseType() instanceof GenericType) { + this.inferArg(expr, argType, expr.getBase().getType().getBaseType(), scene, realTypes); + } + } + if (paramType instanceof ClassType && scene.getProjectSdkMap().has(paramType.getClassSignature().getDeclaringFileSignature().getProjectName())) { + this.inferArgTypeWithSdk(paramType, scene, argType); + } + else if (paramType instanceof GenericType || paramType instanceof AnyType) { + realTypes.push(argType); + } + else if (paramType instanceof FunctionType && argType instanceof FunctionType) { + if (paramType.getMethodSignature().getParamLength() > 0 && paramType.getMethodSignature().getType() instanceof GenericType) { + const paramMethod = scene.getMethod(expr.getMethodSignature()); + const argMethod = scene.getMethod(argType.getMethodSignature()); + if (paramMethod && paramMethod.getGenericTypes() && argMethod) { + TypeInference.inferTypeInMethod(argMethod); + } + } + const realTypes = expr.getRealGenericTypes(); + TypeInference.inferFunctionType(argType, paramType.getMethodSignature().getMethodSubSignature(), realTypes); + } + } + static inferRightWithSdkType(leftType, rightType, ackClass) { + if (leftType instanceof AliasType) { + this.inferRightWithSdkType(TypeInference.replaceAliasType(leftType), rightType, ackClass); + } + else if (leftType instanceof UnionType) { + leftType.getTypes().forEach(t => this.inferRightWithSdkType(t, rightType, ackClass)); + } + else if (leftType instanceof ClassType) { + IRInference.inferArgTypeWithSdk(leftType, ackClass.getDeclaringArkFile().getScene(), rightType); + } + else if (rightType instanceof ArrayType && leftType instanceof ArrayType) { + const baseType = TypeInference.replaceAliasType(leftType.getBaseType()); + if (baseType instanceof ClassType) { + IRInference.inferArgTypeWithSdk(baseType, ackClass.getDeclaringArkFile().getScene(), rightType.getBaseType()); + } + } + } + static inferArgTypeWithSdk(sdkType, scene, argType) { + if (!scene.getProjectSdkMap().has(sdkType.getClassSignature().getDeclaringFileSignature().getProjectName())) { + return; + } + if (argType instanceof UnionType) { + argType.getTypes().forEach(t => this.inferArgTypeWithSdk(sdkType, scene, t)); + } + else if (argType instanceof ClassType && argType.getClassSignature().getClassName().startsWith(ANONYMOUS_CLASS_PREFIX)) { + this.inferAnonymousClass(scene.getClass(argType.getClassSignature()), sdkType.getClassSignature()); + } + else if (argType instanceof FunctionType) { + const param = scene.getClass(sdkType.getClassSignature())?.getMethodWithName(CALL_SIGNATURE_NAME)?.getSignature().getMethodSubSignature(); + const realTypes = sdkType.getRealGenericTypes(); + TypeInference.inferFunctionType(argType, param, realTypes); + } + } + static inferInvokeExpr(expr, baseType, methodName, scene) { + if (baseType instanceof AliasType) { + return this.inferInvokeExpr(expr, baseType.getOriginalType(), methodName, scene); + } + else if (baseType instanceof UnionType) { + for (let type of baseType.flatType()) { + if (type instanceof UndefinedType || type instanceof NullType) { + continue; + } + let result = this.inferInvokeExpr(expr, type, methodName, scene); + if (result) { + return result; + } + } + } + if (baseType instanceof ClassType) { + return this.inferInvokeExprWithDeclaredClass(expr, baseType, methodName, scene); + } + else if (baseType instanceof AnnotationNamespaceType) { + const namespace = scene.getNamespace(baseType.getNamespaceSignature()); + if (namespace) { + const foundMethod = ModelUtils.findPropertyInNamespace(methodName, namespace); + if (foundMethod instanceof ArkMethod) { + let signature = foundMethod.matchMethodSignature(expr.getArgs()); + TypeInference.inferSignatureReturnType(signature, foundMethod); + expr.setMethodSignature(signature); + return expr instanceof ArkInstanceInvokeExpr ? new ArkStaticInvokeExpr(signature, expr.getArgs(), expr.getRealGenericTypes()) : expr; + } + } + } + else if (baseType instanceof FunctionType) { + return IRInference.inferInvokeExprWithFunction(methodName, expr, baseType, scene); + } + else if (baseType instanceof ArrayType) { + return IRInference.inferInvokeExprWithArray(methodName, expr, baseType, scene); + } + return null; + } + static inferInvokeExprWithArray(methodName, expr, baseType, scene) { + if (methodName === Builtin.ITERATOR_FUNCTION) { + const returnType = expr.getMethodSignature().getMethodSubSignature().getReturnType(); + if (returnType instanceof ClassType && returnType.getClassSignature().getDeclaringFileSignature().getProjectName() === Builtin.DUMMY_PROJECT_NAME) { + expr.setRealGenericTypes([baseType.getBaseType()]); + return expr; + } + } + else { + const arrayInterface = scene.getSdkGlobal(Builtin.ARRAY); + if (arrayInterface instanceof ArkClass) { + return this.inferInvokeExpr(expr, new ClassType(arrayInterface.getSignature(), [baseType.getBaseType()]), methodName, scene); + } + } + return null; + } + static inferInvokeExprWithFunction(methodName, expr, baseType, scene) { + if (methodName === CALL_SIGNATURE_NAME) { + expr.setMethodSignature(baseType.getMethodSignature()); + return expr; + } + const funcInterface = scene.getSdkGlobal(FUNCTION); + if (funcInterface instanceof ArkClass) { + const method = ModelUtils.findPropertyInClass(methodName, funcInterface); + if (method instanceof ArkMethod) { + expr.setRealGenericTypes([baseType]); + expr.setMethodSignature(method.getSignature()); + return expr; + } + } + return null; + } + static inferInvokeExprWithDeclaredClass(expr, baseType, methodName, scene) { + if (Builtin.isBuiltinClass(baseType.getClassSignature().getClassName())) { + expr.setMethodSignature(new MethodSignature(baseType.getClassSignature(), expr.getMethodSignature().getMethodSubSignature())); + } + let declaredClass = scene.getClass(baseType.getClassSignature()); + if (!declaredClass) { + const globalClass = scene.getSdkGlobal(baseType.getClassSignature().getClassName()); + if (globalClass instanceof ArkClass) { + declaredClass = globalClass; + } + } + const method = declaredClass ? ModelUtils.findPropertyInClass(methodName, declaredClass) : null; + if (method instanceof ArkMethod) { + const methodSignature = method.matchMethodSignature(expr.getArgs()); + TypeInference.inferSignatureReturnType(methodSignature, method); + expr.setMethodSignature(this.replaceMethodSignature(expr.getMethodSignature(), methodSignature)); + expr.setRealGenericTypes(IRInference.getRealTypes(method, declaredClass, baseType)); + if (method.isStatic() && expr instanceof ArkInstanceInvokeExpr) { + return new ArkStaticInvokeExpr(methodSignature, expr.getArgs(), expr.getRealGenericTypes()); + } + return expr; + } + else if (method instanceof ArkField) { + const type = method.getType(); + let methodSignature; + if (type instanceof FunctionType) { + methodSignature = type.getMethodSignature(); + } + else if (type instanceof ClassType && type.getClassSignature().getClassName().endsWith(CALL_BACK)) { + const callback = scene.getClass(type.getClassSignature())?.getMethodWithName(CALL_SIGNATURE_NAME); + if (callback) { + methodSignature = callback.getSignature(); + } + } + if (methodSignature) { + const ptr = expr instanceof ArkInstanceInvokeExpr + ? new ArkInstanceFieldRef(expr.getBase(), method.getSignature()) + : new ArkStaticFieldRef(method.getSignature()); + expr = new ArkPtrInvokeExpr(methodSignature, ptr, expr.getArgs(), expr.getRealGenericTypes()); + } + return expr; + } + else if (methodName === CONSTRUCTOR_NAME) { + //sdk隐式构造 + const subSignature = new MethodSubSignature(methodName, [], new ClassType(baseType.getClassSignature())); + expr.setMethodSignature(new MethodSignature(baseType.getClassSignature(), subSignature)); + return expr; + } + else if (methodName === Builtin.ITERATOR_NEXT) { + //sdk隐式构造 + const returnType = expr.getMethodSignature().getMethodSubSignature().getReturnType(); + if (returnType instanceof ClassType && returnType.getClassSignature().getDeclaringFileSignature().getProjectName() === Builtin.DUMMY_PROJECT_NAME) { + returnType.setRealGenericTypes(baseType.getRealGenericTypes()); + return expr; + } + } + return null; + } + static getRealTypes(method, declaredClass, baseType) { + let realTypes; + if (method.getDeclaringArkClass() === declaredClass) { + realTypes = baseType.getRealGenericTypes(); + } + else if (declaredClass?.getRealTypes()) { + realTypes = declaredClass?.getRealTypes(); + } + else if (declaredClass?.hasComponentDecorator()) { + realTypes = [new ClassType(declaredClass?.getSignature())]; + } + return realTypes; + } + static replaceMethodSignature(init, declared) { + const className = init.getDeclaringClassSignature().getClassName(); + let classSignature; + if (declared.getDeclaringClassSignature().getClassName().endsWith('Interface')) { + classSignature = new AliasClassSignature(className, declared.getDeclaringClassSignature()); + } + let newSubSignature; + if (classSignature || newSubSignature) { + return new MethodSignature(classSignature ?? declared.getDeclaringClassSignature(), declared.getMethodSubSignature()); + } + return declared; + } + static processForEach(arg, baseType, scene) { + const argType = arg.getType(); + if (argType instanceof FunctionType) { + const argMethodSignature = argType.getMethodSignature(); + const argMethod = scene.getMethod(argMethodSignature); + if (argMethod != null && argMethod.getBody()) { + const body = argMethod.getBody(); + const firstStmt = body.getCfg().getStartingStmt(); + if (firstStmt instanceof ArkAssignStmt && firstStmt.getRightOp() instanceof ArkParameterRef) { + const parameterRef = firstStmt.getRightOp(); + parameterRef.setType(baseType.getBaseType()); + const argMethodParams = argMethod.getSignature().getMethodSubSignature().getParameters(); + const actualParam = argMethodParams[argMethodParams.length - 1]; + actualParam.setType(baseType.getBaseType()); + } + TypeInference.inferTypeInMethod(argMethod); + } + } + else { + logger$z.warn(`arg of forEach must be callable`); + } + } + static inferLocal(base, arkMethod) { + const arkClass = arkMethod.getDeclaringArkClass(); + let baseType = base.getType(); + if (baseType instanceof UnclearReferenceType) { + baseType = TypeInference.inferUnclearRefName(baseType.getName(), arkClass); + } + else if (TypeInference.isUnclearType(baseType)) { + const declaringStmt = base.getDeclaringStmt(); + if (!declaringStmt || !declaringStmt.getOriginalText() || declaringStmt.getOriginalText()?.startsWith(base.getName())) { + baseType = ModelUtils.findDeclaredLocal(base, arkMethod)?.getType() ?? TypeInference.inferBaseType(base.getName(), arkClass); + } + } + if (baseType instanceof UnionType || (baseType && !TypeInference.isUnclearType(baseType))) { + base.setType(baseType); + } + } + static generateNewFieldSignature(ref, arkClass, baseType) { + if (baseType instanceof UnionType) { + for (let type of baseType.flatType()) { + if (type instanceof UndefinedType || type instanceof NullType) { + continue; + } + let newFieldSignature = this.generateNewFieldSignature(ref, arkClass, type); + if (!TypeInference.isUnclearType(newFieldSignature?.getType())) { + return newFieldSignature; + } + } + return null; + } + else if (baseType instanceof AliasType) { + return this.generateNewFieldSignature(ref, arkClass, baseType.getOriginalType()); + } + const fieldName = ref.getFieldName().replace(/[\"|\']/g, ''); + const propertyAndType = TypeInference.inferFieldType(baseType, fieldName, arkClass); + let propertyType = propertyAndType?.[1]; + if (!propertyType || propertyType instanceof UnknownType) { + const newType = TypeInference.inferBaseType(fieldName, arkClass); + if (newType) { + propertyType = newType; + } + } + else if (TypeInference.isUnclearType(propertyType)) { + const newType = TypeInference.inferUnclearedType(propertyType, arkClass); + if (newType) { + propertyType = newType; + } + } + let staticFlag; + let signature; + if (baseType instanceof ClassType) { + const property = propertyAndType?.[0]; + if (property instanceof ArkField && property.getCategory() !== FieldCategory.ENUM_MEMBER) { + return property.getSignature(); + } + staticFlag = + baseType.getClassSignature().getClassName() === DEFAULT_ARK_CLASS_NAME || + ((property instanceof ArkField || property instanceof ArkMethod) && property.isStatic()); + signature = property instanceof ArkMethod ? property.getSignature().getDeclaringClassSignature() : baseType.getClassSignature(); + } + else if (baseType instanceof AnnotationNamespaceType) { + staticFlag = true; + signature = baseType.getNamespaceSignature(); + } + else { + return null; + } + return new FieldSignature(fieldName, signature, propertyType ?? ref.getType(), staticFlag); + } + static inferAnonymousClass(anon, declaredSignature, set = new Set()) { + if (!anon) { + return; + } + const key = anon.getSignature().toString(); + if (set.has(key)) { + return; + } + else { + set.add(key); + } + const scene = anon.getDeclaringArkFile().getScene(); + const declaredClass = scene.getClass(declaredSignature); + if (!declaredClass) { + return; + } + for (const anonField of anon.getFields()) { + const property = ModelUtils.findPropertyInClass(anonField.getName(), declaredClass); + if (property instanceof ArkField) { + this.assignAnonField(property, anonField, scene, set); + } + else if (property instanceof ArkMethod) { + const type = anonField.getType(); + if (type instanceof FunctionType) { + this.assignAnonMethod(scene.getMethod(type.getMethodSignature()), property); + } + anonField.setSignature(new FieldSignature(anonField.getName(), property.getDeclaringArkClass().getSignature(), new FunctionType(property.getSignature()))); + } + } + for (const anonMethod of anon.getMethods()) { + this.assignAnonMethod(anonMethod, declaredClass.getMethodWithName(anonMethod.getName())); + } + } + static assignAnonMethod(anonMethod, declaredMethod) { + if (declaredMethod && anonMethod) { + anonMethod.setDeclareSignatures(declaredMethod.matchMethodSignature(anonMethod.getSubSignature().getParameters())); + } + } + static assignAnonField(property, anonField, scene, set) { + function deepInfer(anonType, declaredSignature) { + if (anonType instanceof ClassType && anonType.getClassSignature().getClassName().startsWith(ANONYMOUS_CLASS_PREFIX)) { + IRInference.inferAnonymousClass(scene.getClass(anonType.getClassSignature()), declaredSignature, set); + } + } + const type = property.getSignature().getType(); + const lastStmt = anonField.getInitializer().at(-1); + if (lastStmt instanceof ArkAssignStmt) { + const rightType = lastStmt.getRightOp().getType(); + if (type instanceof ClassType) { + deepInfer(rightType, type.getClassSignature()); + } + else if (type instanceof ArrayType && type.getBaseType() instanceof ClassType && rightType instanceof ArrayType) { + const baseType = rightType.getBaseType(); + const classSignature = type.getBaseType().getClassSignature(); + if (baseType instanceof UnionType) { + baseType.getTypes().forEach(t => deepInfer(t, classSignature)); + } + else { + deepInfer(rightType.getBaseType(), classSignature); + } + } + else if (type instanceof FunctionType && rightType instanceof FunctionType) { + TypeInference.inferFunctionType(rightType, type.getMethodSignature().getMethodSubSignature(), type.getRealGenericTypes()); + } + const leftOp = lastStmt.getLeftOp(); + if (leftOp instanceof AbstractFieldRef) { + leftOp.setFieldSignature(property.getSignature()); + } + } + anonField.setSignature(property.getSignature()); + } + static inferAliasTypeExpr(expr, arkMethod) { + const originalObject = expr.getOriginalObject(); + let model; + if (originalObject instanceof Local) { + model = ModelUtils.findArkModelByRefName(originalObject.getName(), arkMethod.getDeclaringArkClass()); + } + else if (originalObject instanceof AbstractTypeExpr) { + originalObject.inferType(arkMethod); + model = originalObject; + } + else if (originalObject instanceof Type) { + const type = TypeInference.inferUnclearedType(originalObject, arkMethod.getDeclaringArkClass()); + // If original Object is ClassType, AliasType or UnclearReferenceType with real generic types, + // the type after infer should be revert back to the object itself. + if (type instanceof ClassType) { + const scene = arkMethod.getDeclaringArkFile().getScene(); + model = ModelUtils.findArkModelBySignature(type.getClassSignature(), scene); + } + else if (type instanceof AliasType) { + const scene = arkMethod.getDeclaringArkFile().getScene(); + model = ModelUtils.findArkModelBySignature(type.getSignature(), scene); + } + else if (type) { + model = type; + } + if (expr.getRealGenericTypes() !== undefined && originalObject instanceof UnclearReferenceType) { + expr.setRealGenericTypes(originalObject.getGenericTypes()); + } + } + if (AliasTypeExpr.isAliasTypeOriginalModel(model)) { + expr.setOriginalObject(model); + } + return expr; + } + static inferTypeQueryExpr(expr, arkMethod) { + let gTypes = expr.getGenerateTypes(); + if (gTypes) { + for (let i = 0; i < gTypes.length; i++) { + const newType = TypeInference.inferUnclearedType(gTypes[i], arkMethod.getDeclaringArkClass()); + if (newType) { + gTypes[i] = newType; + } + } + } + const opValue = expr.getOpValue(); + let opValueType; + if (opValue instanceof ArkBaseModel) { + opValueType = ModelUtils.parseArkBaseModel2Type(opValue) ?? UnknownType.getInstance(); + } + else { + opValueType = opValue.getType(); + } + if (!TypeInference.isUnclearType(opValueType)) { + return; + } + if (opValue instanceof Local) { + const newOpValueType = TypeInference.inferBaseType(opValue.getName(), arkMethod.getDeclaringArkClass()); + const scene = arkMethod.getDeclaringArkFile().getScene(); + if (newOpValueType instanceof ClassType) { + const newOpValue = ModelUtils.findArkModelBySignature(newOpValueType.getClassSignature(), scene); + if (newOpValue instanceof ArkBaseModel) { + expr.setOpValue(newOpValue); + } + } + else if (newOpValueType instanceof FunctionType) { + const newOpValue = ModelUtils.findArkModelBySignature(newOpValueType.getMethodSignature(), scene); + if (newOpValue instanceof ArkBaseModel) { + expr.setOpValue(newOpValue); + } + } + else { + this.inferLocal(opValue, arkMethod); + } + } + else if (opValue instanceof AbstractRef || opValue instanceof AbstractExpr) { + expr.setOpValue(opValue.inferType(arkMethod)); + } + } + static inferKeyofTypeExpr(expr, arkMethod) { + const opType = expr.getOpType(); + if (TypeInference.isUnclearType(opType)) { + if (opType instanceof TypeQueryExpr) { + this.inferTypeQueryExpr(opType, arkMethod); + } + else { + const type = TypeInference.inferUnclearedType(opType, arkMethod.getDeclaringArkClass()); + if (type) { + expr.setOpType(type); + } + } + } + } + static inferParameterRef(ref, arkMethod) { + const paramType = ref.getType(); + if (paramType instanceof UnknownType || paramType instanceof UnclearReferenceType) { + const signature = arkMethod.getDeclareSignatures()?.at(0) ?? arkMethod.getSignature(); + const type1 = signature.getMethodSubSignature().getParameters()[ref.getIndex()]?.getType(); + if (!TypeInference.isUnclearType(type1)) { + ref.setType(type1); + return ref; + } + } + else if (paramType instanceof LexicalEnvType) { + paramType + .getClosures() + .filter(c => TypeInference.isUnclearType(c.getType())) + .forEach(e => this.inferLocal(e, arkMethod)); + return ref; + } + let type = TypeInference.inferUnclearedType(paramType, arkMethod.getDeclaringArkClass()); + if (type) { + ref.setType(type); + } + return ref; + } +} + +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * abstract type expr represents the type operations of types or values. + * AbstractTypeExpr is different from AbstractExpr. + * @category core/base/typeExpr + * @extends Type + * @example + * ```typescript + * let a = number; + * type A = typeof a; + * let b: keyof typeof a; + * ``` + */ +class AbstractTypeExpr extends Type { + inferType(arkMethod) { + return; + } +} +/** + * typeQuery type expr represents the get type of value with typeof. + * @category core/base/typeExpr + * @extends AbstractTypeExpr + * @example + ```typescript + // opValue is a and type A is number + let a = number; + type A = typeof a; + ``` + */ +class TypeQueryExpr extends AbstractTypeExpr { + opValue; + genericTypes; + constructor(opValue, generateTypes) { + super(); + this.opValue = opValue; + this.genericTypes = generateTypes; + } + setOpValue(opValue) { + this.opValue = opValue; + } + getOpValue() { + return this.opValue; + } + setGenerateTypes(types) { + this.genericTypes = types; + } + getGenerateTypes() { + return this.genericTypes; + } + addGenericType(gType) { + if (!this.genericTypes) { + this.genericTypes = []; + } + this.genericTypes.push(gType); + } + getUses() { + const opValue = this.getOpValue(); + if (opValue instanceof ArkBaseModel) { + return []; + } + let uses = []; + uses.push(opValue); + uses.push(...opValue.getUses()); + return uses; + } + getType() { + const opValue = this.getOpValue(); + if (opValue instanceof ArkBaseModel) { + return ModelUtils.parseArkBaseModel2Type(opValue) ?? UnknownType.getInstance(); + } + return opValue.getType(); + } + getTypeString() { + const opValue = this.getOpValue(); + const gTypes = this.getGenerateTypes(); + const genericStr = gTypes && gTypes.length > 0 ? `<${gTypes.join(',')}>` : ''; + if (opValue instanceof ArkClass || opValue instanceof ArkMethod) { + return `typeof ${opValue.getSignature().toString()}${genericStr}`; + } + return `typeof ${opValue.toString()}${genericStr}`; + } + inferType(arkMethod) { + IRInference.inferTypeQueryExpr(this, arkMethod); + } +} +/** + * keyof type expr represents the type operator with keyof. + * It should be an internal expr. + * the final type should be transferred to union type, unless it cannot find out all types within the union type. + * @category core/base/typeExpr + * @extends AbstractTypeExpr + * @example + ```typescript + // opType is {a: 1, b: 2} and type of A is KeyofTypeExpr, which can be transferred to union type {'a', 'b'} + type A = keyof {a: 1, b: 2}; + + // opType is number and type of B is KeyofTypeExpr, which can be transferred to union type "toString" | "toFixed" | "toExponential" | ... + type B = keyof number; + ``` + */ +class KeyofTypeExpr extends AbstractTypeExpr { + opType; + constructor(opType) { + super(); + this.opType = opType; + } + getOpType() { + return this.opType; + } + setOpType(opType) { + this.opType = opType; + } + getUses() { + let uses = []; + if (this.getOpType() instanceof TypeQueryExpr) { + uses.push(...this.getOpType().getUses()); + } + return uses; + } + getType() { + return this; + } + getTypeString() { + if (this.getOpType() instanceof UnionType || this.getOpType() instanceof IntersectionType) { + return `keyof (${this.getOpType().toString()})`; + } + return `keyof ${this.getOpType().toString()}`; + } + inferType(arkMethod) { + IRInference.inferKeyofTypeExpr(this, arkMethod); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @category core/base/stmt + */ +class Stmt { + text; // just for debug + originalText; + originalPosition = LineColPosition.DEFAULT; + cfg; + operandOriginalPositions; // operandOriginalPositions correspond with + // def and uses one by one + metadata; + getMetadata(kind) { + return this.metadata?.getMetadata(kind); + } + setMetadata(kind, value) { + if (!this.metadata) { + this.metadata = new ArkMetadata(); + } + return this.metadata?.setMetadata(kind, value); + } + /** Return a list of values which are uesd in this statement */ + getUses() { + return []; + } + replaceUse(oldUse, newUse) { + const stmtUseReplacer = new StmtUseReplacer(oldUse, newUse); + stmtUseReplacer.caseStmt(this); + } + /** + * Return the definition which is uesd in this statement. Generally, the definition is the left value of `=` in + * 3AC. For example, the definition in 3AC of `value = parameter0: @project-1/sample-1.ets: AnonymousClass-0` is + * `value`, and the definition in `$temp0 = staticinvoke <@_ProjectName/_FileName: xxx.create()>()` is `\$temp0`. + * @returns The definition in 3AC (may be a **null**). + * @example + * 1. get the def in stmt. + ```typescript + for (const block of this.blocks) { + for (const stmt of block.getStmts()) { + const defValue = stmt.getDef(); + ... + } + } + ``` + */ + getDef() { + return null; + } + replaceDef(oldDef, newDef) { + const stmtDefReplacer = new StmtDefReplacer(oldDef, newDef); + stmtDefReplacer.caseStmt(this); + } + getDefAndUses() { + const defAndUses = []; + const def = this.getDef(); + if (def) { + defAndUses.push(def); + } + defAndUses.push(...this.getUses()); + return defAndUses; + } + /** + * Get the CFG (i.e., control flow graph) of an {@link ArkBody} in which the statement is. + * A CFG contains a set of basic blocks and statements corresponding to each basic block. + * Note that, "source code" and "three-address" are two types of {@link Stmt} in ArkAnalyzer. + * Source code {@link Stmt} represents the statement of ets/ts source code, while three-address code {@link Stmt} + * represents the statement after it has been converted into three-address code. Since the source code {@link + * Stmt} does not save its CFG reference, it returns **null**, while the `getCfg()` of the third address code + * {@link Stmt} will return its CFG reference. + * @returns The CFG (i.e., control flow graph) of an {@link ArkBody} in which the statement is. + * @example + * 1. get the ArkFile based on stmt. + ```typescript + const arkFile = stmt.getCfg()?.getDeclaringMethod().getDeclaringArkFile(); + ``` + 2. get the ArkMethod based on stmt. + ```typescript + let sourceMethod: ArkMethod = stmt.getCfg()?.getDeclaringMethod(); + ``` + */ + getCfg() { + return this.cfg; + } + setCfg(cfg) { + this.cfg = cfg; + } + /** + * Return true if the following statement may not execute after this statement. + * The ArkIfStmt and ArkGotoStmt will return true. + */ + isBranch() { + return false; + } + /** Return the number of statements which this statement may go to */ + getExpectedSuccessorCount() { + return 1; + } + containsInvokeExpr() { + for (const use of this.getUses()) { + if (use instanceof AbstractInvokeExpr) { + return true; + } + } + return false; + } + /** + * Returns the method's invocation expression (including method signature and its arguments) + * in the current statement. An **undefined** will be returned if there is no method used in this statement. + * @returns the method's invocation expression from the statement. An **undefined** will be returned if there is + * no method can be found in this statement. + * @example + * 1. get invoke expr based on stmt. + ```typescript + let invoke = stmt.getInvokeExpr(); + ``` + */ + getInvokeExpr() { + for (const use of this.getUses()) { + if (use instanceof AbstractInvokeExpr) { + return use; + } + } + return undefined; + } + /** + * Returns an array of expressions in the statement. + * @returns An array of expressions in the statement. + * @example + * 1. Traverse expression of statement. + + ```typescript + for (const expr of stmt.getExprs()) { + ... + } + ``` + */ + getExprs() { + let exprs = []; + for (const use of this.getUses()) { + if (use instanceof AbstractExpr) { + exprs.push(use); + } + } + return exprs; + } + getTypeExprs() { + let typeExprs = []; + for (const value of this.getDefAndUses()) { + const valueType = value.getType(); + if (valueType instanceof AbstractTypeExpr) { + typeExprs.push(valueType); + } + } + return typeExprs; + } + containsArrayRef() { + for (const use of this.getUses()) { + if (use instanceof ArkArrayRef) { + return true; + } + } + if (this.getDef() instanceof ArkArrayRef) { + return true; + } + return false; + } + getArrayRef() { + for (const use of this.getUses()) { + if (use instanceof ArkArrayRef) { + return use; + } + } + if (this.getDef() instanceof ArkArrayRef) { + return undefined; + } + return undefined; + } + containsFieldRef() { + for (const use of this.getUses()) { + if (use instanceof AbstractFieldRef) { + return true; + } + } + if (this.getDef() instanceof AbstractFieldRef) { + return true; + } + return false; + } + getFieldRef() { + for (const use of this.getUses()) { + if (use instanceof AbstractFieldRef) { + return use; + } + } + if (this.getDef() instanceof AbstractFieldRef) { + return undefined; + } + return undefined; + } + setOriginPositionInfo(originPositionInfo) { + this.originalPosition = originPositionInfo; + } + /** + * Returns the original position of the statement. + * The position consists of two parts: line number and column number. + * In the source file, the former (i.e., line number) indicates which line the statement is in, + * and the latter (i.e., column number) indicates the position of the statement in the line. + * The position is described as `LineColPosition(lineNo,colNum)` in ArkAnalyzer, + * and its default value is LineColPosition(-1,-1). + * @returns The original location of the statement. + * @example + * 1. Get the stmt position info to make some condition judgements. + ```typescript + for (const stmt of stmts) { + if (stmt.getOriginPositionInfo().getLineNo() === -1) { + stmt.setOriginPositionInfo(originalStmt.getOriginPositionInfo()); + this.stmtToOriginalStmt.set(stmt, originalStmt); + } + } + ``` + */ + getOriginPositionInfo() { + return this.originalPosition; + } + setText(text) { + this.text = text; + } + setOriginalText(originalText) { + this.originalText = originalText; + } + getOriginalText() { + return this.originalText; + } + setOperandOriginalPositions(operandOriginalPositions) { + this.operandOriginalPositions = operandOriginalPositions; + } + getOperandOriginalPositions() { + return this.operandOriginalPositions; + } + getOperandOriginalPosition(indexOrOperand) { + let index = -1; + if (typeof indexOrOperand !== 'number') { + index = IRUtils.findOperandIdx(this, indexOrOperand); + } + else { + index = indexOrOperand; + } + if (!this.operandOriginalPositions || index < 0 || index > this.operandOriginalPositions.length) { + return null; + } + return this.operandOriginalPositions[index]; + } +} +class ArkAssignStmt extends Stmt { + leftOp; + rightOp; + constructor(leftOp, rightOp) { + super(); + this.leftOp = leftOp; + this.rightOp = rightOp; + } + /** + * Returns the left operand of the assigning statement. + * @returns The left operand of the assigning statement. + * @example + * 1. If the statement is `a=b;`, the right operand is `a`; if the statement is `dd = cc + 5;`, the right operand + * is `cc`. + */ + getLeftOp() { + return this.leftOp; + } + setLeftOp(newLeftOp) { + this.leftOp = newLeftOp; + } + /** + * Returns the right operand of the assigning statement. + * @returns The right operand of the assigning statement. + * @example + * 1. If the statement is `a=b;`, the right operand is `b`; if the statement is `dd = cc + 5;`, the right operand + * is `cc + 5`. + * 2. Get the rightOp from stmt. + ```typescript + const rightOp = stmt.getRightOp(); + ``` + */ + getRightOp() { + return this.rightOp; + } + setRightOp(rightOp) { + this.rightOp = rightOp; + } + toString() { + const str = this.getLeftOp() + ' = ' + this.getRightOp(); + return str; + } + getDef() { + return this.leftOp; + } + getUses() { + let uses = []; + uses.push(...this.leftOp.getUses()); + uses.push(this.rightOp); + uses.push(...this.rightOp.getUses()); + return uses; + } +} +class ArkInvokeStmt extends Stmt { + invokeExpr; + constructor(invokeExpr) { + super(); + this.invokeExpr = invokeExpr; + } + replaceInvokeExpr(newExpr) { + this.invokeExpr = newExpr; + } + getInvokeExpr() { + return this.invokeExpr; + } + toString() { + const str = this.invokeExpr.toString(); + return str; + } + getUses() { + let uses = []; + uses.push(this.invokeExpr); + uses.push(...this.invokeExpr.getUses()); + return uses; + } +} +class ArkIfStmt extends Stmt { + conditionExpr; + constructor(conditionExpr) { + super(); + this.conditionExpr = conditionExpr; + } + /** + * The condition expression consisit of two values as operands and one binary operator as operator. + * The operator can indicate the relation between the two values, e.g., `<`, `<=`,`>`, `>=`, `==`, `!=`, `===`, + * `!==`. + * @returns a condition expression. + * @example + * 1. When a statement is `if (a > b)`, the operands are `a` and `b`, the operator is `<`. Therefore, the condition + * expression is `a > b`. + * 2. get a conditon expr from a condition statement. + ```typescript + let expr = (this.original as ArkIfStmt).getConditionExpr(); + ``` + */ + getConditionExpr() { + return this.conditionExpr; + } + setConditionExpr(newConditionExpr) { + this.conditionExpr = newConditionExpr; + } + isBranch() { + return true; + } + getExpectedSuccessorCount() { + return 2; + } + toString() { + const str = 'if ' + this.conditionExpr; + return str; + } + getUses() { + let uses = []; + uses.push(this.conditionExpr); + uses.push(...this.conditionExpr.getUses()); + return uses; + } +} +class ArkReturnStmt extends Stmt { + op; + constructor(op) { + super(); + this.op = op; + } + getExpectedSuccessorCount() { + return 0; + } + getOp() { + return this.op; + } + setReturnValue(returnValue) { + this.op = returnValue; + } + toString() { + const str = 'return ' + this.op; + return str; + } + getUses() { + let uses = []; + uses.push(this.op); + uses.push(...this.op.getUses()); + return uses; + } +} +class ArkReturnVoidStmt extends Stmt { + constructor() { + super(); + } + getExpectedSuccessorCount() { + return 0; + } + toString() { + const str = 'return'; + return str; + } +} +class ArkThrowStmt extends Stmt { + op; + constructor(op) { + super(); + this.op = op; + } + getOp() { + return this.op; + } + setOp(newOp) { + this.op = newOp; + } + toString() { + const str = 'throw ' + this.op; + return str; + } + getUses() { + let uses = []; + uses.push(this.op); + uses.push(...this.op.getUses()); + return uses; + } +} +/** + * Statement of type alias definition combines with the left hand as {@link AliasType} and right hand as {@link AliasTypeExpr}. + * @category core/base/stmt + * @extends Stmt + * @example + ```typescript + type A = string; + type B = import('./abc').TypeB; + + let c = 123; + declare type C = typeof c; + ``` + */ +class ArkAliasTypeDefineStmt extends Stmt { + aliasType; + aliasTypeExpr; + constructor(aliasType, typeAliasExpr) { + super(); + this.aliasType = aliasType; + this.aliasTypeExpr = typeAliasExpr; + } + getAliasType() { + return this.aliasType; + } + getAliasTypeExpr() { + return this.aliasTypeExpr; + } + getAliasName() { + return this.getAliasType().getName(); + } + toString() { + let str = `type ${this.getAliasType().toString()} = ${this.getAliasTypeExpr().toString()}`; + if (this.getAliasType().containsModifier(ModifierType.DECLARE)) { + str = 'declare ' + str; + } + if (this.getAliasType().containsModifier(ModifierType.EXPORT)) { + str = 'export ' + str; + } + return str; + } + getExprs() { + return [this.getAliasTypeExpr()]; + } + getTypeExprs() { + function getTypeExprsInType(originalObject) { + let typeExprs = []; + if (originalObject instanceof AbstractTypeExpr) { + typeExprs.push(originalObject); + } + else if (originalObject instanceof ArrayType) { + typeExprs.push(...getTypeExprsInType(originalObject.getBaseType())); + } + else if (originalObject instanceof UnionType || originalObject instanceof IntersectionType || originalObject instanceof TupleType) { + for (const member of originalObject.getTypes()) { + typeExprs.push(...getTypeExprsInType(member)); + } + } + return typeExprs; + } + const originalObject = this.getAliasTypeExpr().getOriginalObject(); + if (originalObject instanceof Type) { + return getTypeExprsInType(originalObject); + } + return []; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$y = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'Ref'); +/** + * @category core/base/ref + */ +class AbstractRef { + inferType(arkMethod) { + return this; + } +} +class ArkArrayRef extends AbstractRef { + base; // 数组变量 + index; // 索引 + constructor(base, index) { + super(); + this.base = base; + this.index = index; + } + /** + * Returns the base of this array reference. Array reference refers to access to array elements. + * Array references usually consist of an local variable and an index. + * For example, `a[i]` is a typical array reference, where `a` is the base (i.e., local variable) + * pointing to the actual memory location where the array is stored + * and `i` is the index indicating access to the `i-th` element from array `a`. + * @returns the base of this array reference. + * @example + * 1. Get the base and the specific elements. + + ```typescript + // Create an array + let myArray: number[] = [10, 20, 30, 40]; + // Create an ArrayRef object representing a reference to myArray[2] + let arrayRef = new ArkArrayRef(myArray, 2); + // Use the getBase() method to get the base of the array + let baseArray = arrayRef.getBase(); + + console.log("Base array:", baseArray); // Output: Base array: [10, 20, 30, 40] + + // Use baseArray and obeject index of ArrayRef to access to specific array elements + let element = baseArray[arrayRef.index]; + console.log("Element at index", arrayRef.index, ":", element); // Output: Element at index 2 : 30 + ``` + */ + getBase() { + return this.base; + } + setBase(newBase) { + this.base = newBase; + } + /** + * Returns the index of this array reference. + * In TypeScript, an array reference means that the variable stores + * the memory address of the array rather than the actual data of the array. + * @returns The index of this array reference. + */ + getIndex() { + return this.index; + } + setIndex(newIndex) { + this.index = newIndex; + } + getType() { + let baseType = TypeInference.replaceTypeWithReal(this.base.getType()); + if (baseType instanceof ArrayType) { + return baseType.getBaseType(); + } + else { + logger$y.warn(`the type of base in ArrayRef is not ArrayType`); + return UnknownType.getInstance(); + } + } + getUses() { + let uses = []; + uses.push(this.base); + uses.push(...this.base.getUses()); + uses.push(this.index); + uses.push(...this.index.getUses()); + return uses; + } + toString() { + return this.base + '[' + this.index + ']'; + } +} +class AbstractFieldRef extends AbstractRef { + fieldSignature; + constructor(fieldSignature) { + super(); + this.fieldSignature = fieldSignature; + } + /** + * Returns the the field name as a **string**. + * @returns The the field name. + */ + getFieldName() { + return this.fieldSignature.getFieldName(); + } + /** + * Returns a field signature, which consists of a class signature, + * a **string** field name, and a **boolean** label indicating whether it is static or not. + * @returns The field signature. + * @example + * 1. Compare two Fields + + ```typescript + const fieldSignature = new FieldSignature(); + fieldSignature.setFieldName(...); + const fieldRef = new ArkInstanceFieldRef(baseValue as Local, fieldSignature); + ... + if (fieldRef.getFieldSignature().getFieldName() === + targetField.getFieldSignature().getFieldName()) { + ... + } + ``` + */ + getFieldSignature() { + return this.fieldSignature; + } + setFieldSignature(newFieldSignature) { + this.fieldSignature = newFieldSignature; + } + getType() { + return this.fieldSignature.getType(); + } +} +class ArkInstanceFieldRef extends AbstractFieldRef { + base; // which obj this field belong to + constructor(base, fieldSignature) { + super(fieldSignature); + this.base = base; + } + /** + * Returns the local of field, showing which object this field belongs to. + * A {@link Local} consists of : + * - Name: the **string** name of local value, e.g., "$temp0". + * - Type: the type of value. + * @returns The object that the field belongs to. + * @example + * 1. Get a base. + + ```typescript + if (expr instanceof ArkInstanceFieldRef) { + ... + let base = expr.getBase(); + if (base.getName() == 'this') { + ... + } + ... + } + ``` + */ + getBase() { + return this.base; + } + setBase(newBase) { + this.base = newBase; + } + getUses() { + let uses = []; + uses.push(this.base); + uses.push(...this.base.getUses()); + return uses; + } + toString() { + return this.base.toString() + '.<' + this.getFieldSignature() + '>'; + } + inferType(arkMethod) { + return IRInference.inferFieldRef(this, arkMethod); + } +} +class ArkStaticFieldRef extends AbstractFieldRef { + constructor(fieldSignature) { + super(fieldSignature); + } + getUses() { + return []; + } + toString() { + return this.getFieldSignature().toString(); + } +} +class ArkParameterRef extends AbstractRef { + index; + paramType; + constructor(index, paramType) { + super(); + this.index = index; + this.paramType = paramType; + } + getIndex() { + return this.index; + } + setIndex(index) { + this.index = index; + } + getType() { + return this.paramType; + } + setType(newType) { + this.paramType = newType; + } + inferType(arkMethod) { + return IRInference.inferParameterRef(this, arkMethod); + } + getUses() { + return []; + } + toString() { + return 'parameter' + this.index + ': ' + this.paramType; + } +} +class ArkThisRef extends AbstractRef { + type; + constructor(type) { + super(); + this.type = type; + } + getType() { + return this.type; + } + getUses() { + return []; + } + toString() { + return 'this: ' + this.type; + } +} +class ArkCaughtExceptionRef extends AbstractRef { + type; + constructor(type) { + super(); + this.type = type; + } + getType() { + return this.type; + } + getUses() { + return []; + } + toString() { + return 'caughtexception: ' + this.type; + } +} +class GlobalRef extends AbstractRef { + name; + ref; + usedStmts; + constructor(name, ref) { + super(); + this.name = name; + this.ref = ref ?? null; + this.usedStmts = []; + } + getName() { + return this.name; + } + getUses() { + return this.ref?.getUses() || []; + } + getType() { + return this.ref?.getType() || UnknownType.getInstance(); + } + getRef() { + return this.ref || null; + } + setRef(value) { + this.ref = value; + } + getUsedStmts() { + return this.usedStmts; + } + addUsedStmts(usedStmts) { + if (usedStmts instanceof Stmt) { + this.usedStmts.push(usedStmts); + } + else { + usedStmts.forEach(stmt => this.usedStmts.push(stmt)); + } + } + toString() { + return this.getName(); + } +} +class ClosureFieldRef extends AbstractRef { + base; + fieldName; + type; + constructor(base, fieldName, type) { + super(); + this.base = base; + this.fieldName = fieldName; + this.type = type; + } + getUses() { + return []; + } + getBase() { + return this.base; + } + getType() { + return this.type; + } + getFieldName() { + return this.fieldName; + } + toString() { + return this.base.toString() + '.' + this.getFieldName(); + } + inferType(arkMethod) { + if (TypeInference.isUnclearType(this.type)) { + let type = this.base.getType(); + if (type instanceof LexicalEnvType) { + type = type + .getClosures() + .find(c => c.getName() === this.fieldName) + ?.getType(); + } + if (type && !TypeInference.isUnclearType(type)) { + this.type = type; + } + } + return this; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @category core/model + */ +class ArkMethod extends ArkBaseModel { + code; + declaringArkClass; + // used for the nested function to locate its outer function + outerMethod; + genericTypes; + methodDeclareSignatures; + methodDeclareLineCols; + methodSignature; + lineCol; + body; + viewTree; + arkui_viewTree; + bodyBuilder; + isGeneratedFlag = false; + asteriskToken = false; + questionToken = false; + constructor() { + super(); + } + /** + * Returns the program language of the file where this method defined. + */ + getLanguage() { + return this.getDeclaringArkClass().getLanguage(); + } + getExportType() { + return ExportType.METHOD; + } + getName() { + return this.getSignature().getMethodSubSignature().getMethodName(); + } + /** + * Returns the codes of method as a **string.** + * @returns the codes of method. + */ + getCode() { + return this.code; + } + setCode(code) { + this.code = code; + } + /** + * Get all lines of the method's declarations or null if the method has no seperated declaration. + * @returns null or the lines of the method's declarations with number type. + */ + getDeclareLines() { + if (this.methodDeclareLineCols === undefined) { + return null; + } + let lines = []; + this.methodDeclareLineCols.forEach(lineCol => { + lines.push(getLineNo(lineCol)); + }); + return lines; + } + /** + * Get all columns of the method's declarations or null if the method has no seperated declaration. + * @returns null or the columns of the method's declarations with number type. + */ + getDeclareColumns() { + if (this.methodDeclareLineCols === undefined) { + return null; + } + let columns = []; + this.methodDeclareLineCols.forEach(lineCol => { + columns.push(getColNo(lineCol)); + }); + return columns; + } + /** + * Set lines and columns of the declarations with number type inputs and then encoded them to LineCol type. + * The length of lines and columns should be the same otherwise they cannot be encoded together. + * @param lines - the number of lines. + * @param columns - the number of columns. + * @returns + */ + setDeclareLinesAndCols(lines, columns) { + if (lines?.length !== columns?.length) { + return; + } + this.methodDeclareLineCols = []; + lines.forEach((line, index) => { + let lineCol = 0; + lineCol = setLine(lineCol, line); + lineCol = setCol(lineCol, columns[index]); + this.methodDeclareLineCols.push(lineCol); + }); + } + /** + * Set lineCols of the declarations directly with LineCol type input. + * @param lineCols - the encoded lines and columns with LineCol type. + * @returns + */ + setDeclareLineCols(lineCols) { + this.methodDeclareLineCols = lineCols; + } + /** + * Get encoded lines and columns of the method's declarations or null if the method has no seperated declaration. + * @returns null or the encoded lines and columns of the method's declarations with LineCol type. + */ + getDeclareLineCols() { + return this.methodDeclareLineCols ?? null; + } + /** + * Get line of the method's implementation or null if the method has no implementation. + * @returns null or the number of the line. + */ + getLine() { + if (this.lineCol === undefined) { + return null; + } + return getLineNo(this.lineCol); + } + /** + * Set line of the implementation with line number input. + * The line number will be encoded together with the original column number. + * @param line - the line number of the method implementation. + * @returns + */ + setLine(line) { + if (this.lineCol === undefined) { + this.lineCol = 0; + } + this.lineCol = setLine(this.lineCol, line); + } + /** + * Get column of the method's implementation or null if the method has no implementation. + * @returns null or the number of the column. + */ + getColumn() { + if (this.lineCol === undefined) { + return null; + } + return getColNo(this.lineCol); + } + /** + * Set column of the implementation with column number input. + * The column number will be encoded together with the original line number. + * @param column - the column number of the method implementation. + * @returns + */ + setColumn(column) { + if (this.lineCol === undefined) { + this.lineCol = 0; + } + this.lineCol = setCol(this.lineCol, column); + } + /** + * Get encoded line and column of the method's implementation or null if the method has no implementation. + * @returns null or the encoded line and column of the method's implementation with LineCol type. + */ + getLineCol() { + return this.lineCol ?? null; + } + /** + * Set lineCol of the implementation directly with LineCol type input. + * @param lineCol - the encoded line and column with LineCol type. + * @returns + */ + setLineCol(lineCol) { + this.lineCol = lineCol; + } + /** + * Returns the declaring class of the method. + * @returns The declaring class of the method. + */ + getDeclaringArkClass() { + return this.declaringArkClass; + } + setDeclaringArkClass(declaringArkClass) { + this.declaringArkClass = declaringArkClass; + } + getDeclaringArkFile() { + return this.declaringArkClass.getDeclaringArkFile(); + } + isDefaultArkMethod() { + return this.getName() === DEFAULT_ARK_METHOD_NAME; + } + isAnonymousMethod() { + return this.getName().startsWith(ANONYMOUS_METHOD_PREFIX); + } + getParameters() { + return this.getSignature().getMethodSubSignature().getParameters(); + } + getReturnType() { + return this.getSignature().getType(); + } + /** + * Get all declare signatures. + * The results could be null if there is no seperated declaration of the method. + * @returns null or the method declare signatures. + */ + getDeclareSignatures() { + return this.methodDeclareSignatures ?? null; + } + /** + * Get the index of the matched method declare signature among all declare signatures. + * The index will be -1 if there is no matched signature found. + * @param targetSignature - the target declare signature want to search. + * @returns -1 or the index of the matched signature. + */ + getDeclareSignatureIndex(targetSignature) { + let declareSignatures = this.methodDeclareSignatures; + if (declareSignatures === undefined) { + return -1; + } + for (let i = 0; i < declareSignatures.length; i++) { + if (declareSignatures[i].isMatch(targetSignature)) { + return i; + } + } + return -1; + } + /** + * Get the method signature of the implementation. + * The signature could be null if the method is only a declaration which body is undefined. + * @returns null or the method implementation signature. + */ + getImplementationSignature() { + return this.methodSignature ?? null; + } + /** + * Get the method signature of the implementation or the first declaration if there is no implementation. + * For a method, the implementation and declaration signatures must not be undefined at the same time. + * A {@link MethodSignature} includes: + * - Class Signature: indicates which class this method belong to. + * - Method SubSignature: indicates the detail info of this method such as method name, parameters, returnType, etc. + * @returns The method signature. + * @example + * 1. Get the signature of method mtd. + + ```typescript + let signature = mtd.getSignature(); + // ... ... + ``` + */ + getSignature() { + return this.methodSignature ?? this.methodDeclareSignatures[0]; + } + /** + * Set signatures of all declarations. + * It will reset the declaration signatures if they are already defined before. + * @param signatures - one signature or a list of signatures. + * @returns + */ + setDeclareSignatures(signatures) { + if (Array.isArray(signatures)) { + this.methodDeclareSignatures = signatures; + } + else { + this.methodDeclareSignatures = [signatures]; + } + } + /** + * Reset signature of one declaration with the specified index. + * Will do nothing if the index doesn't exist. + * @param signature - new signature want to set. + * @param index - index of signature want to set. + * @returns + */ + setDeclareSignatureWithIndex(signature, index) { + if (this.methodDeclareSignatures === undefined || this.methodDeclareSignatures.length <= index) { + return; + } + this.methodDeclareSignatures[index] = signature; + } + /** + * Set signature of implementation. + * It will reset the implementation signature if it is already defined before. + * @param signature - signature of implementation. + * @returns + */ + setImplementationSignature(signature) { + this.methodSignature = signature; + } + getSubSignature() { + return this.getSignature().getMethodSubSignature(); + } + getGenericTypes() { + return this.genericTypes; + } + isGenericsMethod() { + return this.genericTypes !== undefined; + } + setGenericTypes(genericTypes) { + this.genericTypes = genericTypes; + } + getBodyBuilder() { + return this.bodyBuilder; + } + /** + * Get {@link ArkBody} of a Method. + * A {@link ArkBody} contains the CFG and actual instructions or operations to be executed for a method. + * It is analogous to the body of a function or method in high-level programming languages, + * which contains the statements and expressions that define what the function does. + * @returns The {@link ArkBody} of a method. + * @example + * 1. Get cfg or stmt through ArkBody. + + ```typescript + let cfg = this.scene.getMethod()?.getBody().getCfg(); + const body = arkMethod.getBody() + ``` + + 2. Get local variable through ArkBody. + + ```typescript + arkClass.getDefaultArkMethod()?.getBody().getLocals.forEach(local=>{...}) + let locals = arkFile().getDefaultClass().getDefaultArkMethod()?.getBody()?.getLocals(); + ``` + */ + getBody() { + return this.body; + } + setBody(body) { + this.body = body; + } + /** + * Get the CFG (i.e., control flow graph) of a method. + * The CFG is a graphical representation of all possible control flow paths within a method's body. + * A CFG consists of blocks, statements and goto control jumps. + * @returns The CFG (i.e., control flow graph) of a method. + * @example + * 1. get stmt through ArkBody cfg. + + ```typescript + body = arkMethod.getBody(); + const cfg = body.getCfg(); + for (const threeAddressStmt of cfg.getStmts()) { + ... ... + } + ``` + + 2. get blocks through ArkBody cfg. + + ```typescript + const body = arkMethod.getBody(); + const blocks = [...body.getCfg().getBlocks()]; + for (let i=0; i stmts.push(stmt)); + } + let results = []; + for (let stmt of stmts) { + if (stmt instanceof ArkAssignStmt) { + if (stmt.getRightOp() instanceof ArkParameterRef) { + results.push(stmt.getLeftOp()); + } + } + if (results.length === this.getParameters().length) { + return results; + } + } + return results; + } + getThisInstance() { + // 获取方法体中This实例 + let stmts = []; + if (this.getCfg()) { + const cfg = this.getCfg(); + cfg.getStmts().forEach(stmt => stmts.push(stmt)); + } + for (let stmt of stmts) { + if (stmt instanceof ArkAssignStmt) { + if (stmt.getRightOp() instanceof ArkThisRef) { + return stmt.getLeftOp(); + } + } + } + return null; + } + getReturnValues() { + // 获取方法体中return值实例 + let resultValues = []; + this.getCfg() + ?.getStmts() + .forEach(stmt => { + if (stmt instanceof ArkReturnStmt) { + resultValues.push(stmt.getOp()); + } + }); + return resultValues; + } + isConstructor() { + return this.getName() === CONSTRUCTOR_NAME; + } + getReturnStmt() { + return this.getCfg() + .getStmts() + .filter(stmt => stmt instanceof ArkReturnStmt); + } + setViewTree(viewTree) { + this.viewTree = viewTree; + } + setArkUIViewTree(arkui_viewTree) { + this.arkui_viewTree = arkui_viewTree; + } + getArkUIViewTree() { + return this.arkui_viewTree; + } + getViewTree() { + return this.viewTree; + } + hasViewTree() { + return this.viewTree !== undefined; + } + hasArkUIViewTree() { + return this.arkui_viewTree !== undefined; + } + setBodyBuilder(bodyBuilder) { + this.bodyBuilder = bodyBuilder; + if (this.getDeclaringArkFile().getScene().buildClassDone()) { + this.buildBody(); + } + } + freeBodyBuilder() { + this.bodyBuilder = undefined; + } + buildBody() { + if (this.bodyBuilder) { + const arkBody = this.bodyBuilder.build(); + if (arkBody) { + this.setBody(arkBody); + arkBody.getCfg().setDeclaringMethod(this); + if (this.getOuterMethod() === undefined) { + this.bodyBuilder.handleGlobalAndClosure(); + } + } + } + } + isGenerated() { + return this.isGeneratedFlag; + } + setIsGeneratedFlag(isGeneratedFlag) { + this.isGeneratedFlag = isGeneratedFlag; + } + getAsteriskToken() { + return this.asteriskToken; + } + setAsteriskToken(asteriskToken) { + this.asteriskToken = asteriskToken; + } + validate() { + const declareSignatures = this.getDeclareSignatures(); + const declareLineCols = this.getDeclareLineCols(); + const signature = this.getImplementationSignature(); + const lineCol = this.getLineCol(); + if (declareSignatures === null && signature === null) { + return { + errCode: ArkErrorCode.METHOD_SIGNATURE_UNDEFINED, + errMsg: 'methodDeclareSignatures and methodSignature are both undefined.', + }; + } + if ((declareSignatures === null) !== (declareLineCols === null)) { + return { + errCode: ArkErrorCode.METHOD_SIGNATURE_LINE_UNMATCHED, + errMsg: 'methodDeclareSignatures and methodDeclareLineCols are not matched.', + }; + } + if (declareSignatures !== null && declareLineCols !== null && declareSignatures.length !== declareLineCols.length) { + return { + errCode: ArkErrorCode.METHOD_SIGNATURE_LINE_UNMATCHED, + errMsg: 'methodDeclareSignatures and methodDeclareLineCols are not matched.', + }; + } + if ((signature === null) !== (lineCol === null)) { + return { + errCode: ArkErrorCode.METHOD_SIGNATURE_LINE_UNMATCHED, + errMsg: 'methodSignature and lineCol are not matched.', + }; + } + return this.validateFields(['declaringArkClass']); + } + matchMethodSignature(args) { + const signatures = this.methodDeclareSignatures?.filter(f => { + const parameters = f.getMethodSubSignature().getParameters(); + const max = parameters.length; + let min = 0; + while (min < max && !parameters[min].isOptional()) { + min++; + } + return args.length >= min && args.length <= max; + }); + return (signatures?.find(p => this.isMatched(p.getMethodSubSignature().getParameters(), args)) ?? + signatures?.[0] ?? + this.getSignature()); + } + isMatched(parameters, args, isArrowFunc = false) { + for (let i = 0; i < parameters.length; i++) { + if (!args[i]) { + return isArrowFunc ? true : parameters[i].isOptional(); + } + const isMatched = this.matchParam(parameters[i].getType(), args[i]); + if (!isMatched) { + return false; + } + } + return true; + } + matchParam(paramType, arg) { + arg = ArkMethod.parseArg(arg); + const argType = arg.getType(); + if (paramType instanceof AliasType && !(argType instanceof AliasType)) { + paramType = TypeInference.replaceAliasType(paramType); + } + if (paramType instanceof UnionType) { + return !!paramType.getTypes().find(p => this.matchParam(p, arg)); + } + else if (argType instanceof FunctionType && paramType instanceof FunctionType) { + if (argType.getMethodSignature().getParamLength() > paramType.getMethodSignature().getParamLength()) { + return false; + } + const parameters = paramType.getMethodSignature().getMethodSubSignature().getParameters(); + const args = argType.getMethodSignature().getMethodSubSignature().getParameters().filter(p => !p.getName().startsWith(LEXICAL_ENV_NAME_PREFIX)); + return this.isMatched(parameters, args, true); + } + else if (paramType instanceof ClassType && paramType.getClassSignature().getClassName().includes(CALL_BACK)) { + return argType instanceof FunctionType; + } + else if (paramType instanceof LiteralType && arg instanceof Constant) { + return (arg.getValue().replace(/[\"|\']/g, '') === + paramType + .getLiteralName() + .toString() + .replace(/[\"|\']/g, '')); + } + else if (paramType instanceof ClassType && argType instanceof EnumValueType) { + return paramType.getClassSignature() === argType.getFieldSignature().getDeclaringSignature(); + } + else if (paramType instanceof EnumValueType) { + if (argType instanceof EnumValueType) { + return paramType.getFieldSignature() === argType.getFieldSignature(); + } + else if (argType.constructor === paramType.getConstant()?.getType().constructor && arg instanceof Constant) { + return paramType.getConstant()?.getValue() === arg.getValue(); + } + } + return argType.constructor === paramType.constructor; + } + static parseArg(arg) { + if (arg instanceof Local) { + const stmt = arg.getDeclaringStmt(); + const argType = arg.getType(); + if (argType instanceof EnumValueType && argType.getConstant()) { + arg = argType.getConstant(); + } + else if (stmt instanceof ArkAssignStmt && stmt.getRightOp() instanceof Constant) { + arg = stmt.getRightOp(); + } + } + return arg; + } + getOuterMethod() { + return this.outerMethod; + } + setOuterMethod(method) { + this.outerMethod = method; + } + getFunctionLocal(name) { + const local = this.getBody()?.getLocals().get(name); + return local?.getType() instanceof FunctionType ? local : null; + } + setQuestionToken(questionToken) { + this.questionToken = questionToken; + } + getQuestionToken() { + return this.questionToken; + } + // For class method, if there is no public/private/protected access modifier, it is actually public + isPublic() { + if (!this.containsModifier(ModifierType.PUBLIC) && + !this.containsModifier(ModifierType.PRIVATE) && + !this.containsModifier(ModifierType.PROTECTED) && + !this.getDeclaringArkClass().isDefaultArkClass() && + !this.isGenerated() && + !this.isAnonymousMethod() && + this.getName() !== CONSTRUCTOR_NAME && + this.getDeclaringArkClass().getCategory() === ClassCategory.CLASS) { + return true; + } + return this.containsModifier(ModifierType.PUBLIC); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var ClassCategory; +(function (ClassCategory) { + ClassCategory[ClassCategory["CLASS"] = 0] = "CLASS"; + ClassCategory[ClassCategory["STRUCT"] = 1] = "STRUCT"; + ClassCategory[ClassCategory["INTERFACE"] = 2] = "INTERFACE"; + ClassCategory[ClassCategory["ENUM"] = 3] = "ENUM"; + ClassCategory[ClassCategory["TYPE_LITERAL"] = 4] = "TYPE_LITERAL"; + ClassCategory[ClassCategory["OBJECT"] = 5] = "OBJECT"; +})(ClassCategory || (ClassCategory = {})); +/** + * @category core/model + */ +class ArkClass extends ArkBaseModel { + category; + code; + lineCol = 0; + declaringArkFile; + declaringArkNamespace; + classSignature; + /** + * The keys of the `heritageClasses` map represent the names of superclass and interfaces. + * The superclass name is placed first; if it does not exist, an empty string `''` will occupy this position. + * The values of the `heritageClasses` map will be replaced with `ArkClass` or `null` during type inference. + */ + heritageClasses = new Map(); + genericsTypes; + realTypes; + defaultMethod = null; + // name to model + methods = new Map(); + fields = new Map(); + extendedClasses = new Map(); + staticMethods = new Map(); + staticFields = new Map(); + instanceInitMethod = new ArkMethod(); + staticInitMethod = new ArkMethod(); + anonymousMethodNumber = 0; + indexSignatureNumber = 0; + viewTree; + arkui_viewTree; + constructor() { + super(); + } + /** + * Returns the program language of the file where this class defined. + */ + getLanguage() { + return this.getDeclaringArkFile().getLanguage(); + } + /** + * Returns the **string**name of this class. + * @returns The name of this class. + */ + getName() { + return this.classSignature.getClassName(); + } + /** + * Returns the codes of class as a **string.** + * @returns the codes of class. + */ + getCode() { + return this.code; + } + setCode(code) { + this.code = code; + } + /** + * Returns the line position of this class. + * @returns The line position of this class. + */ + getLine() { + return getLineNo(this.lineCol); + } + setLine(line) { + this.lineCol = setLine(this.lineCol, line); + } + /** + * Returns the column position of this class. + * @returns The column position of this class. + */ + getColumn() { + return getColNo(this.lineCol); + } + setColumn(column) { + this.lineCol = setCol(this.lineCol, column); + } + getCategory() { + return this.category; + } + setCategory(category) { + this.category = category; + } + /** + * Returns the declaring file. + * @returns A file defined by ArkAnalyzer. + * @example + * 1. Get the {@link ArkFile} which the ArkClass is in. + + ```typescript + const arkFile = arkClass.getDeclaringArkFile(); + ``` + */ + getDeclaringArkFile() { + return this.declaringArkFile; + } + setDeclaringArkFile(declaringArkFile) { + this.declaringArkFile = declaringArkFile; + } + /** + * Returns the declaring namespace of this class, which may also be an **undefined**. + * @returns The declaring namespace (may be **undefined**) of this class. + */ + getDeclaringArkNamespace() { + return this.declaringArkNamespace; + } + setDeclaringArkNamespace(declaringArkNamespace) { + this.declaringArkNamespace = declaringArkNamespace; + } + isDefaultArkClass() { + return this.getName() === DEFAULT_ARK_CLASS_NAME; + } + isAnonymousClass() { + return this.getName().startsWith(ANONYMOUS_CLASS_PREFIX); + } + /** + * Returns the signature of current class (i.e., {@link ClassSignature}). + * The {@link ClassSignature} can uniquely identify a class, according to which we can find the class from the scene. + * @returns The class signature. + */ + getSignature() { + return this.classSignature; + } + setSignature(classSig) { + this.classSignature = classSig; + } + getSuperClassName() { + return this.heritageClasses.keys().next().value || ''; + } + addHeritageClassName(className) { + this.heritageClasses.set(className, undefined); + } + /** + * Returns the superclass of this class. + * @returns The superclass of this class. + */ + getSuperClass() { + const heritageClass = this.getHeritageClass(this.getSuperClassName()); + if (heritageClass && heritageClass.getCategory() !== ClassCategory.INTERFACE) { + return heritageClass; + } + return null; + } + getHeritageClass(heritageClassName) { + if (!heritageClassName) { + return null; + } + let superClass = this.heritageClasses.get(heritageClassName); + if (superClass === undefined) { + let type = TypeInference.inferUnclearRefName(heritageClassName, this); + if (type) { + type = TypeInference.replaceAliasType(type); + } + if (type instanceof ClassType && (superClass = this.declaringArkFile.getScene().getClass(type.getClassSignature()))) { + superClass.addExtendedClass(this); + const realGenericTypes = type.getRealGenericTypes(); + if (realGenericTypes) { + this.realTypes = realGenericTypes; + } + } + this.heritageClasses.set(heritageClassName, superClass || null); + } + return superClass || null; + } + getAllHeritageClasses() { + const result = []; + this.heritageClasses.forEach((v, k) => { + const heritage = v ?? this.getHeritageClass(k); + if (heritage) { + result.push(heritage); + } + }); + return result; + } + getExtendedClasses() { + return this.extendedClasses; + } + addExtendedClass(extendedClass) { + this.extendedClasses.set(extendedClass.getName(), extendedClass); + } + getImplementedInterfaceNames() { + if (this.category === ClassCategory.INTERFACE) { + return []; + } + return Array.from(this.heritageClasses.keys()).slice(1); + } + hasImplementedInterface(interfaceName) { + return this.heritageClasses.has(interfaceName) && this.getSuperClassName() !== interfaceName; + } + getImplementedInterface(interfaceName) { + const heritageClass = this.getHeritageClass(interfaceName); + if (heritageClass && heritageClass.getCategory() === ClassCategory.INTERFACE) { + return heritageClass; + } + return null; + } + /** + * Get the field according to its field signature. + * If no field cound be found, **null**will be returned. + * @param fieldSignature - the field's signature. + * @returns A field. If there is no field in this class, the return will be a **null**. + */ + getField(fieldSignature) { + const fieldName = fieldSignature.getFieldName(); + let fieldSearched = this.getFieldWithName(fieldName); + if (!fieldSearched) { + fieldSearched = this.getStaticFieldWithName(fieldName); + } + return fieldSearched; + } + getFieldWithName(fieldName) { + return this.fields.get(fieldName) || null; + } + getStaticFieldWithName(fieldName) { + return this.staticFields.get(fieldName) || null; + } + /** + * Returns an **array** of fields in the class. + * @returns an **array** of fields in the class. + */ + getFields() { + const allFields = Array.from(this.fields.values()); + allFields.push(...this.staticFields.values()); + return allFields; + } + addField(field) { + if (field.isStatic()) { + this.staticFields.set(field.getName(), field); + } + else { + this.fields.set(field.getName(), field); + } + } + addFields(fields) { + fields.forEach(field => { + this.addField(field); + }); + } + getRealTypes() { + return this.realTypes ? Array.from(this.realTypes) : undefined; + } + getGenericsTypes() { + return this.genericsTypes ? Array.from(this.genericsTypes) : undefined; + } + addGenericType(gType) { + if (!this.genericsTypes) { + this.genericsTypes = []; + } + this.genericsTypes.push(gType); + } + /** + * Returns all methods defined in the specific class in the form of an array. + * @param generated - indicating whether this API returns the methods that are dynamically + * generated at runtime. If it is not specified as true or false, the return will not include the generated method. + * @returns An array of all methods in this class. + * @example + * 1. Get methods defined in class `BookService`. + + ```typescript + let classes: ArkClass[] = scene.getClasses(); + let serviceClass : ArkClass = classes[1]; + let methods: ArkMethod[] = serviceClass.getMethods(); + let methodNames: string[] = methods.map(mthd => mthd.name); + console.log(methodNames); + ``` + */ + getMethods(generated) { + const allMethods = Array.from(this.methods.values()).filter(f => (!generated && !f.isGenerated()) || generated); + allMethods.push(...this.staticMethods.values()); + return [...new Set(allMethods)]; + } + getMethod(methodSignature) { + const methodName = methodSignature.getMethodSubSignature().getMethodName(); + const methodSearched = this.getMethodWithName(methodName) ?? this.getStaticMethodWithName(methodName); + if (methodSearched === null) { + return null; + } + const implSignature = methodSearched.getImplementationSignature(); + if (implSignature !== null && implSignature.isMatch(methodSignature)) { + return methodSearched; + } + const declareSignatures = methodSearched.getDeclareSignatures(); + if (declareSignatures !== null) { + for (let i = 0; i < declareSignatures.length; i++) { + if (declareSignatures[i].isMatch(methodSignature)) { + return methodSearched; + } + } + } + return null; + } + getMethodWithName(methodName) { + return this.methods.get(methodName) || null; + } + getStaticMethodWithName(methodName) { + return this.staticMethods.get(methodName) || null; + } + /** + * add a method in class. + * when a nested method with declare name, add both the declare origin name and signature name + * %${declare name}$${outer method name} in class. + */ + addMethod(method, originName) { + const name = originName ?? method.getName(); + if (method.isStatic()) { + this.staticMethods.set(name, method); + } + else { + this.methods.set(name, method); + } + if (!originName && !method.isAnonymousMethod() && name.startsWith(NAME_PREFIX)) { + const index = name.indexOf(NAME_DELIMITER); + if (index > 1) { + const originName = name.substring(1, index); + this.addMethod(method, originName); + } + } + } + setDefaultArkMethod(defaultMethod) { + this.defaultMethod = defaultMethod; + this.addMethod(defaultMethod); + } + getDefaultArkMethod() { + return this.defaultMethod; + } + setViewTree(viewTree) { + this.viewTree = viewTree; + } + setArkUIViewTree(arkui_viewTree) { + this.arkui_viewTree = arkui_viewTree; + } + /** + * Returns the view tree of the ArkClass. + * @returns The view tree of the ArkClass. + * @example + * 1. get viewTree of ArkClass. + + ```typescript + for (let arkFiles of scene.getFiles()) { + for (let arkClasss of arkFiles.getClasses()) { + if (arkClasss.hasViewTree()) { + arkClasss.getViewTree(); + } + } + } + ``` + */ + getViewTree() { + return this.viewTree; + } + getArkUIViewTree() { + return this.arkui_viewTree; + } + generateArkUIViewTreeXpath(node, parentXpath) { + if (ETS_COMPILER_OPTIONS.ets.components.includes(node.name)) { + node.xpath = `${parentXpath}/${node.name}`; + } + else { + node.xpath = parentXpath; + } + for (const child of node.children) { + this.generateArkUIViewTreeXpath(child, node.xpath); + } + } + /** + * Check whether the view tree is defined. + * If it is defined, the return value is true, otherwise it is false. + * @returns True if the view tree is defined; false otherwise. + * @example + * 1. Judge viewTree of ArkClass. + + ```typescript + for (let arkFiles of scene.getFiles()) { + for (let arkClasss of arkFiles.getClasses()) { + if (arkClasss.hasViewTree()) { + arkClasss.getViewTree(); + } + } + } + ``` + */ + hasViewTree() { + return this.viewTree !== undefined; + } + hasArkUIViewTree() { + return this.arkui_viewTree !== undefined; + } + getStaticFields(classMap) { + const fields = []; + let classes = []; + if (this.declaringArkNamespace) { + classes = classMap.get(this.declaringArkNamespace.getNamespaceSignature()); + } + else { + classes = classMap.get(this.declaringArkFile.getFileSignature()); + } + for (const arkClass of classes) { + for (const field of arkClass.getFields()) { + if (field.isStatic()) { + fields.push(field); + } + } + } + return fields; + } + getGlobalVariable(globalMap) { + if (this.declaringArkNamespace) { + return globalMap.get(this.declaringArkNamespace.getNamespaceSignature()); + } + return globalMap.get(this.declaringArkFile.getFileSignature()); + } + getAnonymousMethodNumber() { + return this.anonymousMethodNumber++; + } + getIndexSignatureNumber() { + return this.indexSignatureNumber++; + } + getExportType() { + return ExportType.CLASS; + } + getInstanceInitMethod() { + return this.instanceInitMethod; + } + getStaticInitMethod() { + return this.staticInitMethod; + } + setInstanceInitMethod(arkMethod) { + this.instanceInitMethod = arkMethod; + } + setStaticInitMethod(arkMethod) { + this.staticInitMethod = arkMethod; + } + removeField(field) { + if (field.isStatic()) { + return this.staticFields.delete(field.getName()); + } + return this.fields.delete(field.getName()); + } + removeMethod(method) { + let rtn = false; + if (method.isStatic()) { + rtn = this.staticMethods.delete(method.getName()); + } + else { + rtn = this.methods.delete(method.getName()); + } + rtn &&= this.getDeclaringArkFile().getScene().removeMethod(method); + return rtn; + } + validate() { + return this.validateFields(['declaringArkFile', 'category', 'classSignature']); + } + checkIsUIAbility() { + if (this.getSuperClassName() == "UIAbility") { + return true; + } + return false; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @category core/model + */ +class ArkNamespace extends ArkBaseModel { + sourceCodes = ['']; + lineCols = []; + declaringArkFile; + declaringArkNamespace = null; + declaringInstance; + exportInfos = new Map(); + defaultClass; + // name to model + namespaces = new Map(); // don't contain nested namespace + classes = new Map(); + namespaceSignature; + anonymousClassNumber = 0; + constructor() { + super(); + } + /** + * Returns the program language of the file where this namespace defined. + */ + getLanguage() { + return this.getDeclaringArkFile().getLanguage(); + } + addNamespace(namespace) { + this.namespaces.set(namespace.getName(), namespace); + } + getNamespace(namespaceSignature) { + const namespaceName = namespaceSignature.getNamespaceName(); + return this.getNamespaceWithName(namespaceName); + } + getNamespaceWithName(namespaceName) { + return this.namespaces.get(namespaceName) || null; + } + getNamespaces() { + return Array.from(this.namespaces.values()); + } + setSignature(namespaceSignature) { + this.namespaceSignature = namespaceSignature; + } + getSignature() { + return this.namespaceSignature; + } + getNamespaceSignature() { + return this.namespaceSignature; + } + getName() { + return this.namespaceSignature.getNamespaceName(); + } + getCode() { + return this.sourceCodes[0]; + } + setCode(sourceCode) { + this.sourceCodes[0] = sourceCode; + } + /* + * Get multiple sourceCodes when the arkNamespace is merged from multiple namespace with the same name + */ + getCodes() { + return this.sourceCodes; + } + /* + * Set multiple sourceCodes when the arkNamespace is merged from multiple namespace with the same name + */ + setCodes(sourceCodes) { + this.sourceCodes = []; + this.sourceCodes.push(...sourceCodes); + } + addCode(sourceCode) { + this.sourceCodes.push(sourceCode); + } + getLine() { + return getLineNo(this.lineCols[0]); + } + setLine(line) { + this.lineCols[0] = setLine(this.lineCols[0], line); + } + getColumn() { + return getColNo(this.lineCols[0]); + } + setColumn(column) { + this.lineCols[0] = setCol(this.lineCols[0], column); + } + getLineColPairs() { + const lineColPairs = []; + this.lineCols.forEach(lineCol => { + lineColPairs.push([getLineNo(lineCol), getColNo(lineCol)]); + }); + return lineColPairs; + } + setLineCols(lineColPairs) { + this.lineCols = []; + lineColPairs.forEach(lineColPair => { + this.lineCols.push(setLineCol(lineColPair[0], lineColPair[1])); + }); + } + getDeclaringInstance() { + return this.declaringInstance; + } + setDeclaringInstance(declaringInstance) { + this.declaringInstance = declaringInstance; + } + getDeclaringArkFile() { + return this.declaringArkFile; + } + setDeclaringArkFile(declaringArkFile) { + this.declaringArkFile = declaringArkFile; + } + getDeclaringArkNamespace() { + return this.declaringArkNamespace; + } + setDeclaringArkNamespace(declaringArkNamespace) { + this.declaringArkNamespace = declaringArkNamespace; + } + getClass(classSignature) { + const className = classSignature instanceof AliasClassSignature ? classSignature.getOriginName() : classSignature.getClassName(); + return this.getClassWithName(className); + } + getClassWithName(Class) { + return this.classes.get(Class) || null; + } + getClasses() { + return Array.from(new Set(this.classes.values())); + } + addArkClass(arkClass, originName) { + const name = originName ?? arkClass.getName(); + this.classes.set(name, arkClass); + if (!originName && !arkClass.isAnonymousClass()) { + const index = name.indexOf(NAME_DELIMITER); + if (index > 0) { + const originName = name.substring(0, index); + this.addArkClass(arkClass, originName); + } + } + } + getExportInfos() { + const exportInfos = []; + this.exportInfos.forEach((value, key) => { + if (key !== ALL || value.getFrom()) { + exportInfos.push(value); + } + }); + return exportInfos; + } + getExportInfoBy(name) { + return this.exportInfos.get(name); + } + addExportInfo(exportInfo) { + this.exportInfos.set(exportInfo.getExportClauseName(), exportInfo); + } + getDefaultClass() { + return this.defaultClass; + } + setDefaultClass(defaultClass) { + this.defaultClass = defaultClass; + } + getAllMethodsUnderThisNamespace() { + let methods = []; + this.classes.forEach(cls => { + methods.push(...cls.getMethods()); + }); + this.namespaces.forEach(ns => { + methods.push(...ns.getAllMethodsUnderThisNamespace()); + }); + return methods; + } + getAllClassesUnderThisNamespace() { + let classes = []; + classes.push(...this.classes.values()); + this.namespaces.forEach(ns => { + classes.push(...ns.getAllClassesUnderThisNamespace()); + }); + return classes; + } + getAllNamespacesUnderThisNamespace() { + let namespaces = []; + namespaces.push(...this.namespaces.values()); + this.namespaces.forEach(ns => { + namespaces.push(...ns.getAllNamespacesUnderThisNamespace()); + }); + return namespaces; + } + getAnonymousClassNumber() { + return this.anonymousClassNumber++; + } + getExportType() { + return ExportType.NAME_SPACE; + } + removeArkClass(arkClass) { + let rtn = this.classes.delete(arkClass.getName()); + rtn &&= this.getDeclaringArkFile().getScene().removeClass(arkClass); + return rtn; + } + removeNamespace(namespace) { + let rtn = this.namespaces.delete(namespace.getName()); + rtn &&= this.getDeclaringArkFile().getScene().removeNamespace(namespace); + return rtn; + } + validate() { + return this.validateFields(['declaringArkFile', 'declaringInstance', 'namespaceSignature', 'defaultClass']); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var Language; +(function (Language) { + Language[Language["TYPESCRIPT"] = 0] = "TYPESCRIPT"; + Language[Language["ARKTS1_1"] = 1] = "ARKTS1_1"; + Language[Language["ARKTS1_2"] = 2] = "ARKTS1_2"; + Language[Language["JAVASCRIPT"] = 3] = "JAVASCRIPT"; + Language[Language["UNKNOWN"] = -1] = "UNKNOWN"; +})(Language || (Language = {})); +/** + * @category core/model + */ +class ArkFile { + language; + absoluteFilePath = ''; + projectDir = ''; + code = ''; + defaultClass; + // name to model + namespaces = new Map(); // don't contain nested namespaces + classes = new Map(); // don't contain class in namespace + importInfoMap = new Map(); + exportInfoMap = new Map(); + scene; + moduleScene; + fileSignature = FileSignature.DEFAULT; + ohPackageJson5Path = []; + anonymousClassNumber = 0; + constructor(language) { + this.language = language; + } + /** + * Returns the program language of the file. + */ + getLanguage() { + return this.language; + } + setLanguage(language) { + this.language = language; + } + /** + * Returns the **string** name of the file, which also acts as the file's relative path. + * @returns The file's name (also means its relative path). + */ + getName() { + return this.fileSignature.getFileName(); + } + setScene(scene) { + this.scene = scene; + } + /** + * Returns the scene (i.e., {@link Scene}) built for the project. The {@link Scene} is the core class of ArkAnalyzer, + * through which users can access all the information of the analyzed code (project), + * including file list, class list, method list, property list, etc. + * @returns The scene of the file. + */ + getScene() { + return this.scene; + } + getModuleScene() { + return this.moduleScene; + } + setModuleScene(moduleScene) { + this.moduleScene = moduleScene; + } + setProjectDir(projectDir) { + this.projectDir = projectDir; + } + getProjectDir() { + return this.projectDir; + } + /** + * Get a file path. + * @returns The absolute file path. + * @example + * 1. Read source code based on file path. + + ```typescript + let str = fs.readFileSync(arkFile.getFilePath(), 'utf8'); + ``` + */ + getFilePath() { + return this.absoluteFilePath; + } + setFilePath(absoluteFilePath) { + this.absoluteFilePath = absoluteFilePath; + } + setCode(code) { + this.code = code; + } + /** + * Returns the codes of file as a **string.** + * @returns the codes of file. + */ + getCode() { + return this.code; + } + addArkClass(arkClass, originName) { + const name = originName ?? arkClass.getName(); + this.classes.set(name, arkClass); + if (!originName && !arkClass.isAnonymousClass()) { + const index = name.indexOf(NAME_DELIMITER); + if (index > 0) { + const originName = name.substring(0, index); + this.addArkClass(arkClass, originName); + } + } + } + getDefaultClass() { + return this.defaultClass; + } + setDefaultClass(defaultClass) { + this.defaultClass = defaultClass; + } + getNamespace(namespaceSignature) { + const namespaceName = namespaceSignature.getNamespaceName(); + return this.getNamespaceWithName(namespaceName); + } + getNamespaceWithName(namespaceName) { + return this.namespaces.get(namespaceName) || null; + } + getNamespaces() { + return Array.from(this.namespaces.values()); + } + /** + * Returns the class based on its class signature. If the class could not be found, **null** will be returned. + * @param classSignature - the class signature. + * @returns A class. If there is no class, the return will be a **null**. + */ + getClass(classSignature) { + const className = classSignature instanceof AliasClassSignature ? classSignature.getOriginName() : classSignature.getClassName(); + return this.getClassWithName(className); + } + getClassWithName(Class) { + return this.classes.get(Class) || null; + } + getClasses() { + return Array.from(new Set(this.classes.values())); + } + addNamespace(namespace) { + this.namespaces.set(namespace.getName(), namespace); + } + /** + * Returns an **array** of import information. + * The import information includes: clause's name, type, modifiers, location where it is imported from, etc. + * @returns An **array** of import information. + */ + getImportInfos() { + return Array.from(this.importInfoMap.values()); + } + getImportInfoBy(name) { + return this.importInfoMap.get(name); + } + addImportInfo(importInfo) { + this.importInfoMap.set(importInfo.getImportClauseName(), importInfo); + } + removeImportInfo(importInfo) { + return this.importInfoMap.delete(importInfo.getImportClauseName()); + } + removeNamespace(namespace) { + let rtn = this.namespaces.delete(namespace.getName()); + rtn &&= this.getScene().removeNamespace(namespace); + return rtn; + } + removeArkClass(arkClass) { + let rtn = this.classes.delete(arkClass.getName()); + rtn &&= this.getScene().removeClass(arkClass); + return rtn; + } + getExportInfos() { + const exportInfos = []; + this.exportInfoMap.forEach((value, key) => { + if (key !== ALL || value.getFrom()) { + exportInfos.push(value); + } + }); + return exportInfos; + } + /** + * Find out the {@link ExportInfo} of this {@link ArkFile} by the given export name. + * It returns an {@link ExportInfo} or 'undefined' if it failed to find. + * @param name + * @returns + * @example + ```typescript + // abc.ts ArkFile + export class A { + ... + } + + export namespace B { + export namespace C { + export class D {} + } + } + + // xyz.ts call getExportInfoBy + let arkFile = scene.getFile(fileSignature); + + // a is the export class A defined in abc.ts + let a = arkFile.getExportInfoBy('A'); + + // b is the export class D within namespace C defined in abc.ts + let b = arkFile.getExportInfoBy('B.C.D'); + ``` + */ + getExportInfoBy(name) { + const separator = '.'; + const names = name.split(separator); + if (names.length === 1) { + return this.exportInfoMap.get(names[0]); + } + let index = 0; + let currExportInfo = this.exportInfoMap.get(names[index]); + if (currExportInfo === undefined) { + return undefined; + } + for (let i = 1; i < names.length; i++) { + const arkExport = currExportInfo.getArkExport(); + if (arkExport && arkExport instanceof ArkNamespace) { + currExportInfo = arkExport.getExportInfoBy(names[i]); + if (currExportInfo === undefined) { + return undefined; + } + } + } + return currExportInfo; + } + addExportInfo(exportInfo, key) { + this.exportInfoMap.set(key ?? exportInfo.getExportClauseName(), exportInfo); + } + removeExportInfo(exportInfo, key) { + if (key) { + this.exportInfoMap.delete(key); + return; + } + this.exportInfoMap.delete(exportInfo.getExportClauseName()); + } + getProjectName() { + return this.fileSignature.getProjectName(); + } + getModuleName() { + return this.moduleScene?.getModuleName(); + } + setOhPackageJson5Path(ohPackageJson5Path) { + this.ohPackageJson5Path = ohPackageJson5Path; + } + getOhPackageJson5Path() { + return this.ohPackageJson5Path; + } + /** + * Returns the file signature of this file. A file signature consists of project's name and file's name. + * @returns The file signature of this file. + */ + getFileSignature() { + return this.fileSignature; + } + setFileSignature(fileSignature) { + this.fileSignature = fileSignature; + } + getAllNamespacesUnderThisFile() { + let namespaces = []; + namespaces.push(...this.namespaces.values()); + this.namespaces.forEach(ns => { + namespaces.push(...ns.getAllNamespacesUnderThisNamespace()); + }); + return namespaces; + } + getAnonymousClassNumber() { + return this.anonymousClassNumber++; + } + checkIsUIAbility() { + // 去遍历所有的类 有没有类是继承 UIAbility 的 + for (const arkclass of this.getClasses()) { + if (arkclass.checkIsUIAbility() == true) + return true; + } + return false; + } + checkIsPage() { + if (this.getFilePath().includes("pages/")) { + return true; + } + return false; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$x = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'FileUtils'); +class FileUtils { + static FILE_FILTER = { + ignores: ['.git', '.preview', '.hvigor', '.idea', 'test', 'ohosTest'], + include: /(? { + const moduleName = content.name; + if (moduleName && moduleName.startsWith('@')) { + const modulePath = path$1.dirname(filePath); + moduleMap.set(moduleName, new ModulePath(modulePath, content.main ? path$1.resolve(modulePath, content.main) : '')); + } + }); + ohPkgContentMap.forEach((content, filePath) => { + if (!content.dependencies) { + return; + } + Object.entries(content.dependencies).forEach(([name, value]) => { + if (moduleMap.get(name)) { + return; + } + const modulePath = path$1.resolve(path$1.dirname(filePath), value.replace('file:', '')); + const key = path$1.resolve(modulePath, OH_PACKAGE_JSON5); + const target = ohPkgContentMap.get(key); + if (target) { + moduleMap.set(name, new ModulePath(modulePath, target.main ? path$1.resolve(modulePath, target.main) : '')); + } + }); + }); + return moduleMap; + } + static getFileLanguage(file, fileTags) { + if (fileTags && fileTags.has(file)) { + return fileTags.get(file); + } + const extension = path$1.extname(file).toLowerCase(); + switch (extension) { + case '.ts': + return Language.TYPESCRIPT; + case '.ets': + return Language.ARKTS1_1; + case '.js': + return Language.JAVASCRIPT; + default: + return Language.UNKNOWN; + } + } +} +class ModulePath { + path; + main; + constructor(path, main) { + this.path = transfer2UnixPath(path); + this.main = transfer2UnixPath(main); + } +} +function getFileRecursively(srcDir, fileName, visited = new Set()) { + let res = ''; + if (!fs$1.existsSync(srcDir) || !fs$1.statSync(srcDir).isDirectory()) { + logger$x.warn(`Input directory ${srcDir} is not exist`); + return res; + } + const filesUnderThisDir = fs$1.readdirSync(srcDir, { withFileTypes: true }); + const realSrc = fs$1.realpathSync(srcDir); + if (visited.has(realSrc)) { + return res; + } + visited.add(realSrc); + filesUnderThisDir.forEach(file => { + if (res !== '') { + return res; + } + if (file.name === fileName) { + res = path$1.resolve(srcDir, file.name); + return res; + } + const tmpDir = path$1.resolve(srcDir, '../'); + res = getFileRecursively(tmpDir, fileName, visited); + return res; + }); + return res; +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @category core/base + */ +class Decorator { + kind; + content = ''; + param = ''; + constructor(name) { + this.kind = name; + } + getKind() { + return this.kind; + } + setContent(content) { + this.content = content; + } + getContent() { + return this.content; + } + setParam(param) { + this.param = param; + } + getParam() { + return this.param; + } + toString() { + return `@${this.content}`; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class ArkBody { + locals; + usedGlobals; + cfg; + aliasTypeMap; + traps; + constructor(locals, cfg, aliasTypeMap, traps) { + this.cfg = cfg; + this.aliasTypeMap = aliasTypeMap; + this.locals = new Map(); + locals.forEach(local => this.locals.set(local.getName(), local)); + this.traps = traps; + } + getLocals() { + return this.locals; + } + setLocals(locals) { + if (!this.locals) { + this.locals = new Map(); + } + locals.forEach(local => this.locals.set(local.getName(), local)); + } + addLocal(name, local) { + this.locals.set(name, local); + } + getUsedGlobals() { + return this.usedGlobals; + } + setUsedGlobals(globals) { + this.usedGlobals = globals; + } + getCfg() { + return this.cfg; + } + setCfg(cfg) { + this.cfg = cfg; + } + getAliasTypeMap() { + return this.aliasTypeMap; + } + getAliasTypeByName(name) { + const aliasTypeInfo = this.aliasTypeMap?.get(name); + if (aliasTypeInfo) { + return aliasTypeInfo[0]; + } + return null; + } + getTraps() { + return this.traps; + } + getExportLocalByName(name) { + const local = this.locals?.get(name); + if (local) { + local.setSignature(new LocalSignature(name, this.cfg.getDeclaringMethod().getSignature())); + return local; + } + return null; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$w = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'BasicBlock'); +/** + * @category core/graph + * A `BasicBlock` is composed of: + * - ID: a **number** that uniquely identify the basic block, initialized as -1. + * - Statements: an **array** of statements in the basic block. + * - Predecessors: an **array** of basic blocks in front of the current basic block. More accurately, these basic + * blocks can reach the current block through edges. + * - Successors: an **array** of basic blocks after the current basic block. More accurately, the current block can + * reach these basic blocks through edges. + */ +class BasicBlock { + id = -1; + stmts = []; + predecessorBlocks = []; + successorBlocks = []; + exceptionalSuccessorBlocks; + constructor() { } + getId() { + return this.id; + } + setId(id) { + this.id = id; + } + /** + * Returns an array of the statements in a basic block. + * @returns An array of statements in a basic block. + */ + getStmts() { + return this.stmts; + } + addStmt(stmt) { + this.stmts.push(stmt); + } + /** + * Adds the given stmt at the beginning of the basic block. + * @param stmt + */ + addHead(stmt) { + if (stmt instanceof Stmt) { + this.stmts.unshift(stmt); + } + else { + this.stmts.unshift(...stmt); + } + } + /** + * Adds the given stmt at the end of the basic block. + * @param stmt + */ + addTail(stmt) { + if (stmt instanceof Stmt) { + this.stmts.push(stmt); + } + else { + stmt.forEach(stmt => this.stmts.push(stmt)); + } + } + /** + * Inserts toInsert in the basic block after point. + * @param toInsert + * @param point + * @returns The number of successfully inserted statements + */ + insertAfter(toInsert, point) { + let index = this.stmts.indexOf(point); + if (index < 0) { + return 0; + } + return this.insertPos(index + 1, toInsert); + } + /** + * Inserts toInsert in the basic block befor point. + * @param toInsert + * @param point + * @returns The number of successfully inserted statements + */ + insertBefore(toInsert, point) { + let index = this.stmts.indexOf(point); + if (index < 0) { + return 0; + } + return this.insertPos(index, toInsert); + } + /** + * Removes the given stmt from this basic block. + * @param stmt + * @returns + */ + remove(stmt) { + let index = this.stmts.indexOf(stmt); + if (index < 0) { + return; + } + this.stmts.splice(index, 1); + } + /** + * Removes the first stmt from this basic block. + */ + removeHead() { + this.stmts.splice(0, 1); + } + /** + * Removes the last stmt from this basic block. + */ + removeTail() { + this.stmts.splice(this.stmts.length - 1, 1); + } + getHead() { + if (this.stmts.length === 0) { + return null; + } + return this.stmts[0]; + } + getTail() { + let size = this.stmts.length; + if (size === 0) { + return null; + } + return this.stmts[size - 1]; + } + /** + * Returns successors of the current basic block, whose types are also basic blocks (i.e.{@link BasicBlock}). + * @returns Successors of the current basic block. + * @example + * 1. get block successors. + + ```typescript + const body = arkMethod.getBody(); + const blocks = [...body.getCfg().getBlocks()] + for (let i = 0; i < blocks.length; i++) { + const block = blocks[i] + ... + for (const next of block.getSuccessors()) { + ... + } + } + ``` + */ + getSuccessors() { + return this.successorBlocks; + } + /** + * Returns predecessors of the current basic block, whose types are also basic blocks. + * @returns An array of basic blocks. + */ + getPredecessors() { + return this.predecessorBlocks; + } + addPredecessorBlock(block) { + this.predecessorBlocks.push(block); + } + setPredecessorBlock(idx, block) { + if (idx < this.predecessorBlocks.length) { + this.predecessorBlocks[idx] = block; + return true; + } + return false; + } + setSuccessorBlock(idx, block) { + if (idx < this.successorBlocks.length) { + this.successorBlocks[idx] = block; + return true; + } + return false; + } + // Temp just for SSA + addStmtToFirst(stmt) { + this.addHead(stmt); + } + // Temp just for SSA + addSuccessorBlock(block) { + this.successorBlocks.push(block); + } + removePredecessorBlock(block) { + let index = this.predecessorBlocks.indexOf(block); + if (index < 0) { + return false; + } + this.predecessorBlocks.splice(index, 1); + return true; + } + removeSuccessorBlock(block) { + let index = this.successorBlocks.indexOf(block); + if (index < 0) { + return false; + } + this.successorBlocks.splice(index, 1); + return true; + } + toString() { + let strs = []; + for (const stmt of this.stmts) { + strs.push(stmt.toString() + '\n'); + } + return strs.join(''); + } + validate() { + let branchStmts = []; + for (const stmt of this.stmts) { + if (stmt instanceof ArkIfStmt || stmt instanceof ArkReturnStmt || stmt instanceof ArkReturnVoidStmt) { + branchStmts.push(stmt); + } + } + if (branchStmts.length > 1) { + let errMsg = `More than one branch or return stmts in the block: ${branchStmts.map(value => value.toString()).join('\n')}`; + logger$w.error(errMsg); + return { + errCode: ArkErrorCode.BB_MORE_THAN_ONE_BRANCH_RET_STMT, + errMsg: errMsg, + }; + } + if (branchStmts.length === 1 && branchStmts[0] !== this.stmts[this.stmts.length - 1]) { + let errMsg = `${branchStmts[0].toString()} not at the end of block.`; + logger$w.error(errMsg); + return { + errCode: ArkErrorCode.BB_BRANCH_RET_STMT_NOT_AT_END, + errMsg: errMsg, + }; + } + return { errCode: ArkErrorCode.OK }; + } + insertPos(index, toInsert) { + if (toInsert instanceof Stmt) { + this.stmts.splice(index, 0, toInsert); + return 1; + } + this.stmts.splice(index, 0, ...toInsert); + return toInsert.length; + } + getExceptionalSuccessorBlocks() { + return this.exceptionalSuccessorBlocks; + } + addExceptionalSuccessorBlock(block) { + if (!this.exceptionalSuccessorBlocks) { + this.exceptionalSuccessorBlocks = []; + } + this.exceptionalSuccessorBlocks.push(block); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class DefUseChain { + value; + def; + use; + constructor(value, def, use) { + this.value = value; + this.def = def; + this.use = use; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$v = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'BasicBlock'); +/** + * @category core/graph + */ +class Cfg { + blocks = new Set(); + stmtToBlock = new Map(); + startingStmt; + defUseChains = []; + declaringMethod = new ArkMethod(); + constructor() { } + getStmts() { + let stmts = new Array(); + for (const block of this.blocks) { + block.getStmts().forEach(s => stmts.push(s)); + } + return stmts; + } + /** + * Inserts toInsert in the basic block in CFG after point. + * @param toInsert + * @param point + * @returns The number of successfully inserted statements + */ + insertAfter(toInsert, point) { + const block = this.stmtToBlock.get(point); + if (!block) { + return 0; + } + this.updateStmt2BlockMap(block, toInsert); + return block.insertAfter(toInsert, point); + } + /** + * Inserts toInsert in the basic block in CFG befor point. + * @param toInsert + * @param point + * @returns The number of successfully inserted statements + */ + insertBefore(toInsert, point) { + const block = this.stmtToBlock.get(point); + if (!block) { + return 0; + } + this.updateStmt2BlockMap(block, toInsert); + return block.insertBefore(toInsert, point); + } + /** + * Removes the given stmt from the basic block in CFG. + * @param stmt + * @returns + */ + remove(stmt) { + const block = this.stmtToBlock.get(stmt); + if (!block) { + return; + } + this.stmtToBlock.delete(stmt); + block.remove(stmt); + } + /** + * Update stmtToBlock Map + * @param block + * @param changed + */ + updateStmt2BlockMap(block, changed) { + if (!changed) { + for (const stmt of block.getStmts()) { + this.stmtToBlock.set(stmt, block); + } + } + else if (changed instanceof Stmt) { + this.stmtToBlock.set(changed, block); + } + else { + for (const insert of changed) { + this.stmtToBlock.set(insert, block); + } + } + } + // TODO: 添加block之间的边 + addBlock(block) { + this.blocks.add(block); + for (const stmt of block.getStmts()) { + this.stmtToBlock.set(stmt, block); + } + } + getBlocks() { + return this.blocks; + } + getStartingBlock() { + return this.stmtToBlock.get(this.startingStmt); + } + getStartingStmt() { + return this.startingStmt; + } + setStartingStmt(newStartingStmt) { + this.startingStmt = newStartingStmt; + } + getDeclaringMethod() { + return this.declaringMethod; + } + setDeclaringMethod(method) { + this.declaringMethod = method; + } + getDefUseChains() { + return this.defUseChains; + } + // TODO: 整理成类似jimple的输出 + toString() { + return 'cfg'; + } + buildDefUseStmt(locals) { + for (const block of this.blocks) { + for (const stmt of block.getStmts()) { + const defValue = stmt.getDef(); + if (defValue && defValue instanceof Local && defValue.getDeclaringStmt() === null) { + defValue.setDeclaringStmt(stmt); + } + for (const value of stmt.getUses()) { + this.buildUseStmt(value, locals, stmt); + } + } + } + } + buildUseStmt(value, locals, stmt) { + if (value instanceof Local) { + value.addUsedStmt(stmt); + } + else if (value instanceof ArkStaticInvokeExpr) { + for (let local of locals) { + if (local.getName() === value.getMethodSignature().getMethodSubSignature().getMethodName()) { + local.addUsedStmt(stmt); + return; + } + } + } + } + handleDefUseForValue(value, block, stmt, stmtIndex) { + const name = value.toString(); + const defStmts = []; + // 判断本block之前有无对应def + for (let i = stmtIndex - 1; i >= 0; i--) { + const beforeStmt = block.getStmts()[i]; + if (beforeStmt.getDef() && beforeStmt.getDef()?.toString() === name) { + defStmts.push(beforeStmt); + break; + } + } + // 本block有对应def直接结束,否则找所有的前序block + if (defStmts.length !== 0) { + this.defUseChains.push(new DefUseChain(value, defStmts[0], stmt)); + return; + } + const needWalkBlocks = [...block.getPredecessors()]; + const walkedBlocks = new Set(); + while (needWalkBlocks.length > 0) { + const predecessor = needWalkBlocks.pop(); + if (!predecessor) { + return; + } + const predecessorStmts = predecessor.getStmts(); + let predecessorHasDef = false; + for (let i = predecessorStmts.length - 1; i >= 0; i--) { + const beforeStmt = predecessorStmts[i]; + if (beforeStmt.getDef() && beforeStmt.getDef()?.toString() === name) { + defStmts.push(beforeStmt); + predecessorHasDef = true; + break; + } + } + walkedBlocks.add(predecessor); + if (predecessorHasDef) { + continue; + } + for (const morePredecessor of predecessor.getPredecessors()) { + if (!walkedBlocks.has(morePredecessor) && !needWalkBlocks.includes(morePredecessor)) { + needWalkBlocks.unshift(morePredecessor); + } + } + } + for (const def of defStmts) { + this.defUseChains.push(new DefUseChain(value, def, stmt)); + } + } + buildDefUseChain() { + for (const block of this.blocks) { + for (let stmtIndex = 0; stmtIndex < block.getStmts().length; stmtIndex++) { + const stmt = block.getStmts()[stmtIndex]; + for (const value of stmt.getUses()) { + this.handleDefUseForValue(value, block, stmt, stmtIndex); + } + } + } + } + getUnreachableBlocks() { + let unreachable = new Set(); + let startBB = this.getStartingBlock(); + if (!startBB) { + return unreachable; + } + let postOrder = this.dfsPostOrder(startBB); + for (const bb of this.blocks) { + if (!postOrder.has(bb)) { + unreachable.add(bb); + } + } + return unreachable; + } + validate() { + let startBB = this.getStartingBlock(); + if (!startBB) { + let errMsg = `Not found starting block}`; + logger$v.error(errMsg); + return { + errCode: ArkErrorCode.CFG_NOT_FOUND_START_BLOCK, + errMsg: errMsg, + }; + } + let unreachable = this.getUnreachableBlocks(); + if (unreachable.size !== 0) { + let errMsg = `Unreachable blocks: ${Array.from(unreachable) + .map(value => value.toString()) + .join('\n')}`; + logger$v.error(errMsg); + return { + errCode: ArkErrorCode.CFG_HAS_UNREACHABLE_BLOCK, + errMsg: errMsg, + }; + } + return { errCode: ArkErrorCode.OK }; + } + dfsPostOrder(node, visitor = new Set(), postOrder = new Set()) { + visitor.add(node); + for (const succ of node.getSuccessors()) { + if (visitor.has(succ)) { + continue; + } + this.dfsPostOrder(succ, visitor, postOrder); + } + postOrder.add(node); + return postOrder; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$u = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'ArkFieldBuilder'); +function buildProperty2ArkField(member, sourceFile, cls) { + let field = new ArkField(); + field.setCategory(mapSyntaxKindToFieldOriginType(member.kind)); + field.setCode(member.getText(sourceFile)); + field.setDeclaringArkClass(cls); + field.setOriginPosition(LineColPosition.buildFromNode(member, sourceFile)); + let fieldName = member.getText(sourceFile); + if (member.name && ts.isComputedPropertyName(member.name)) { + if (ts.isIdentifier(member.name.expression)) { + fieldName = member.name.expression.text; + } + else if (ts.isPropertyAccessExpression(member.name.expression)) { + fieldName = handlePropertyAccessExpression(member.name.expression); + } + else { + logger$u.warn(`Other property expression type found: ${member.name.expression.getText()}!`); + } + } + else if (member.name && (ts.isIdentifier(member.name) || ts.isLiteralExpression(member.name))) { + fieldName = member.name.text; + } + else if (member.name && ts.isPrivateIdentifier(member.name)) { + let propertyName = member.name.text; + fieldName = propertyName.substring(1); + field.addModifier(ModifierType.PRIVATE); + } + else { + logger$u.warn(`Other type of property name found: ${member.getText()}!`); + } + let fieldType = UnknownType.getInstance(); + if (ts.isPropertyDeclaration(member) || ts.isPropertySignature(member)) { + if (member.modifiers) { + field.addModifier(buildModifiers(member)); + } + field.addModifier(0); + field.setDecorators(buildDecorators(member, sourceFile)); + field.setQuestionToken(member.questionToken !== undefined); + if (member.type) { + fieldType = buildGenericType(tsNode2Type(member.type, sourceFile, cls), field); + } + } + if (ts.isEnumMember(member)) { + field.addModifier(ModifierType.STATIC); + fieldType = new ClassType(cls.getSignature()); + } + field.setSignature(new FieldSignature(fieldName, cls.getSignature(), fieldType, field.isStatic())); + if (ts.isPropertyDeclaration(member) && member.exclamationToken) { + field.setExclamationToken(true); + } + IRUtils.setComments(field, member, sourceFile, cls.getDeclaringArkFile().getScene().getOptions()); + cls.addField(field); + return field; +} +function buildIndexSignature2ArkField(member, sourceFile, cls) { + const field = new ArkField(); + field.setCode(member.getText(sourceFile)); + field.setCategory(mapSyntaxKindToFieldOriginType(member.kind)); + field.setDeclaringArkClass(cls); + field.setOriginPosition(LineColPosition.buildFromNode(member, sourceFile)); + if (member.modifiers) { + let modifier = buildModifiers(member); + field.addModifier(modifier); + } + const fieldName = '[' + member.parameters[0].getText(sourceFile) + ']'; + const fieldType = buildGenericType(tsNode2Type(member.type, sourceFile, field), field); + const fieldSignature = new FieldSignature(fieldName, cls.getSignature(), fieldType, true); + field.setSignature(fieldSignature); + IRUtils.setComments(field, member, sourceFile, cls.getDeclaringArkFile().getScene().getOptions()); + cls.addField(field); +} +function buildGetAccessor2ArkField(member, mthd, sourceFile) { + let cls = mthd.getDeclaringArkClass(); + let field = new ArkField(); + field.setDeclaringArkClass(cls); + field.setCode(member.getText(sourceFile)); + field.setCategory(mapSyntaxKindToFieldOriginType(member.kind)); + field.setOriginPosition(LineColPosition.buildFromNode(member, sourceFile)); + let fieldName = member.getText(sourceFile); + if (ts.isIdentifier(member.name) || ts.isLiteralExpression(member.name)) { + fieldName = member.name.text; + } + else if (ts.isComputedPropertyName(member.name)) { + if (ts.isIdentifier(member.name.expression)) { + let propertyName = member.name.expression.text; + fieldName = propertyName; + } + else if (ts.isPropertyAccessExpression(member.name.expression)) { + fieldName = handlePropertyAccessExpression(member.name.expression); + } + else if (ts.isLiteralExpression(member.name.expression)) { + fieldName = member.name.expression.text; + } + else { + logger$u.warn('Other type of computed property name found!'); + } + } + else { + logger$u.warn('Please contact developers to support new type of GetAccessor name!'); + } + const fieldType = mthd.getReturnType(); + const fieldSignature = new FieldSignature(fieldName, cls.getSignature(), fieldType, false); + field.setSignature(fieldSignature); + cls.addField(field); +} +function mapSyntaxKindToFieldOriginType(syntaxKind) { + let fieldOriginType = null; + switch (syntaxKind) { + case ts.SyntaxKind.PropertyDeclaration: + fieldOriginType = FieldCategory.PROPERTY_DECLARATION; + break; + case ts.SyntaxKind.PropertyAssignment: + fieldOriginType = FieldCategory.PROPERTY_ASSIGNMENT; + break; + case ts.SyntaxKind.ShorthandPropertyAssignment: + fieldOriginType = FieldCategory.SHORT_HAND_PROPERTY_ASSIGNMENT; + break; + case ts.SyntaxKind.SpreadAssignment: + fieldOriginType = FieldCategory.SPREAD_ASSIGNMENT; + break; + case ts.SyntaxKind.PropertySignature: + fieldOriginType = FieldCategory.PROPERTY_SIGNATURE; + break; + case ts.SyntaxKind.EnumMember: + fieldOriginType = FieldCategory.ENUM_MEMBER; + break; + case ts.SyntaxKind.IndexSignature: + fieldOriginType = FieldCategory.INDEX_SIGNATURE; + break; + case ts.SyntaxKind.GetAccessor: + fieldOriginType = FieldCategory.GET_ACCESSOR; + break; + } + return fieldOriginType; +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$t = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'ArkClassBuilder'); +function buildDefaultArkClassFromArkFile(arkFile, defaultClass, astRoot) { + defaultClass.setDeclaringArkFile(arkFile); + defaultClass.setCategory(ClassCategory.CLASS); + buildDefaultArkClass(defaultClass, astRoot); +} +function buildDefaultArkClassFromArkNamespace(arkNamespace, defaultClass, nsNode, sourceFile) { + defaultClass.setDeclaringArkNamespace(arkNamespace); + defaultClass.setDeclaringArkFile(arkNamespace.getDeclaringArkFile()); + buildDefaultArkClass(defaultClass, sourceFile, nsNode); +} +function buildNormalArkClassFromArkMethod(clsNode, cls, sourceFile, declaringMethod) { + const namespace = cls.getDeclaringArkNamespace(); + if (namespace) { + buildNormalArkClassFromArkNamespace(clsNode, namespace, cls, sourceFile, declaringMethod); + } + else { + buildNormalArkClassFromArkFile(clsNode, cls.getDeclaringArkFile(), cls, sourceFile, declaringMethod); + } +} +function buildNormalArkClassFromArkFile(clsNode, arkFile, cls, sourceFile, declaringMethod) { + cls.setDeclaringArkFile(arkFile); + cls.setCode(clsNode.getText(sourceFile)); + const { line, character } = ts.getLineAndCharacterOfPosition(sourceFile, clsNode.getStart(sourceFile)); + cls.setLine(line + 1); + cls.setColumn(character + 1); + buildNormalArkClass(clsNode, cls, sourceFile, declaringMethod); + arkFile.addArkClass(cls); +} +function buildNormalArkClassFromArkNamespace(clsNode, arkNamespace, cls, sourceFile, declaringMethod) { + cls.setDeclaringArkNamespace(arkNamespace); + cls.setDeclaringArkFile(arkNamespace.getDeclaringArkFile()); + cls.setCode(clsNode.getText(sourceFile)); + const { line, character } = ts.getLineAndCharacterOfPosition(sourceFile, clsNode.getStart(sourceFile)); + cls.setLine(line + 1); + cls.setColumn(character + 1); + buildNormalArkClass(clsNode, cls, sourceFile, declaringMethod); + arkNamespace.addArkClass(cls); +} +function buildDefaultArkClass(cls, sourceFile, node) { + const defaultArkClassSignature = new ClassSignature(DEFAULT_ARK_CLASS_NAME, cls.getDeclaringArkFile().getFileSignature(), cls.getDeclaringArkNamespace()?.getSignature() || null); + cls.setSignature(defaultArkClassSignature); + genDefaultArkMethod(cls, sourceFile, node); +} +function genDefaultArkMethod(cls, sourceFile, node) { + let defaultMethod = new ArkMethod(); + buildDefaultArkMethodFromArkClass(cls, defaultMethod, sourceFile, node); + cls.setDefaultArkMethod(defaultMethod); +} +function buildNormalArkClass(clsNode, cls, sourceFile, declaringMethod) { + switch (clsNode.kind) { + case ts.SyntaxKind.StructDeclaration: + buildStruct2ArkClass(clsNode, cls, sourceFile, declaringMethod); + break; + case ts.SyntaxKind.ClassDeclaration: + buildClass2ArkClass(clsNode, cls, sourceFile, declaringMethod); + break; + case ts.SyntaxKind.ClassExpression: + buildClass2ArkClass(clsNode, cls, sourceFile, declaringMethod); + break; + case ts.SyntaxKind.InterfaceDeclaration: + buildInterface2ArkClass(clsNode, cls, sourceFile, declaringMethod); + break; + case ts.SyntaxKind.EnumDeclaration: + buildEnum2ArkClass(clsNode, cls, sourceFile, declaringMethod); + break; + case ts.SyntaxKind.TypeLiteral: + buildTypeLiteralNode2ArkClass(clsNode, cls, sourceFile, declaringMethod); + break; + case ts.SyntaxKind.ObjectLiteralExpression: + buildObjectLiteralExpression2ArkClass(clsNode, cls, sourceFile, declaringMethod); + break; + } + IRUtils.setComments(cls, clsNode, sourceFile, cls.getDeclaringArkFile().getScene().getOptions()); +} +function init4InstanceInitMethod(cls) { + const instanceInit = new ArkMethod(); + instanceInit.setDeclaringArkClass(cls); + instanceInit.setIsGeneratedFlag(true); + const methodSubSignature = ArkSignatureBuilder.buildMethodSubSignatureFromMethodName(INSTANCE_INIT_METHOD_NAME); + methodSubSignature.setReturnType(VoidType.getInstance()); + const methodSignature = new MethodSignature(instanceInit.getDeclaringArkClass().getSignature(), methodSubSignature); + instanceInit.setImplementationSignature(methodSignature); + instanceInit.setLineCol(0); + checkAndUpdateMethod(instanceInit, cls); + cls.addMethod(instanceInit); + cls.setInstanceInitMethod(instanceInit); +} +function init4StaticInitMethod(cls) { + const staticInit = new ArkMethod(); + staticInit.setDeclaringArkClass(cls); + staticInit.setIsGeneratedFlag(true); + const methodSubSignature = ArkSignatureBuilder.buildMethodSubSignatureFromMethodName(STATIC_INIT_METHOD_NAME); + methodSubSignature.setReturnType(VoidType.getInstance()); + const methodSignature = new MethodSignature(staticInit.getDeclaringArkClass().getSignature(), methodSubSignature); + staticInit.setImplementationSignature(methodSignature); + staticInit.setLineCol(0); + checkAndUpdateMethod(staticInit, cls); + cls.addMethod(staticInit); + cls.setStaticInitMethod(staticInit); +} +function buildStruct2ArkClass(clsNode, cls, sourceFile, declaringMethod) { + const className = genClassName(clsNode.name ? clsNode.name.text : '', cls, declaringMethod); + const classSignature = new ClassSignature(className, cls.getDeclaringArkFile().getFileSignature(), cls.getDeclaringArkNamespace()?.getSignature() || null); + cls.setSignature(classSignature); + if (clsNode.typeParameters) { + buildTypeParameters(clsNode.typeParameters, sourceFile, cls).forEach(typeParameter => { + cls.addGenericType(typeParameter); + }); + } + initHeritage(buildHeritageClauses(clsNode.heritageClauses), cls); + cls.setModifiers(buildModifiers(clsNode)); + cls.setDecorators(buildDecorators(clsNode, sourceFile)); + cls.setCategory(ClassCategory.STRUCT); + init4InstanceInitMethod(cls); + init4StaticInitMethod(cls); + buildArkClassMembers(clsNode, cls, sourceFile); +} +function buildClass2ArkClass(clsNode, cls, sourceFile, declaringMethod) { + const className = genClassName(clsNode.name ? clsNode.name.text : '', cls, declaringMethod); + const classSignature = new ClassSignature(className, cls.getDeclaringArkFile().getFileSignature(), cls.getDeclaringArkNamespace()?.getSignature() || null); + cls.setSignature(classSignature); + if (clsNode.typeParameters) { + buildTypeParameters(clsNode.typeParameters, sourceFile, cls).forEach(typeParameter => { + cls.addGenericType(typeParameter); + }); + } + initHeritage(buildHeritageClauses(clsNode.heritageClauses), cls); + cls.setModifiers(buildModifiers(clsNode)); + cls.setDecorators(buildDecorators(clsNode, sourceFile)); + cls.setCategory(ClassCategory.CLASS); + init4InstanceInitMethod(cls); + init4StaticInitMethod(cls); + buildArkClassMembers(clsNode, cls, sourceFile); +} +function initHeritage(heritageClauses, cls) { + let superName = ''; + for (let [key, value] of heritageClauses) { + if (value === ts.SyntaxKind[ts.SyntaxKind.ExtendsKeyword]) { + superName = key; + break; + } + } + cls.addHeritageClassName(superName); + for (let key of heritageClauses.keys()) { + cls.addHeritageClassName(key); + } +} +function buildInterface2ArkClass(clsNode, cls, sourceFile, declaringMethod) { + const className = genClassName(clsNode.name ? clsNode.name.text : '', cls, declaringMethod); + const classSignature = new ClassSignature(className, cls.getDeclaringArkFile().getFileSignature(), cls.getDeclaringArkNamespace()?.getSignature() || null); + cls.setSignature(classSignature); + if (clsNode.typeParameters) { + buildTypeParameters(clsNode.typeParameters, sourceFile, cls).forEach(typeParameter => { + cls.addGenericType(typeParameter); + }); + } + initHeritage(buildHeritageClauses(clsNode.heritageClauses), cls); + cls.setModifiers(buildModifiers(clsNode)); + cls.setDecorators(buildDecorators(clsNode, sourceFile)); + cls.setCategory(ClassCategory.INTERFACE); + buildArkClassMembers(clsNode, cls, sourceFile); +} +function buildEnum2ArkClass(clsNode, cls, sourceFile, declaringMethod) { + const className = genClassName(clsNode.name ? clsNode.name.text : '', cls, declaringMethod); + const classSignature = new ClassSignature(className, cls.getDeclaringArkFile().getFileSignature(), cls.getDeclaringArkNamespace()?.getSignature() || null); + cls.setSignature(classSignature); + cls.setModifiers(buildModifiers(clsNode)); + cls.setDecorators(buildDecorators(clsNode, sourceFile)); + cls.setCategory(ClassCategory.ENUM); + init4StaticInitMethod(cls); + buildArkClassMembers(clsNode, cls, sourceFile); +} +function buildTypeLiteralNode2ArkClass(clsNode, cls, sourceFile, declaringMethod) { + const className = genClassName('', cls, declaringMethod); + const classSignature = new ClassSignature(className, cls.getDeclaringArkFile().getFileSignature(), cls.getDeclaringArkNamespace()?.getSignature() || null); + cls.setSignature(classSignature); + cls.setCategory(ClassCategory.TYPE_LITERAL); + if (ts.isTypeAliasDeclaration(clsNode.parent) && clsNode.parent.typeParameters) { + buildTypeParameters(clsNode.parent.typeParameters, sourceFile, cls).forEach(typeParameter => { + cls.addGenericType(typeParameter); + }); + } + buildArkClassMembers(clsNode, cls, sourceFile); +} +function buildObjectLiteralExpression2ArkClass(clsNode, cls, sourceFile, declaringMethod) { + const className = genClassName('', cls, declaringMethod); + const classSignature = new ClassSignature(className, cls.getDeclaringArkFile().getFileSignature(), cls.getDeclaringArkNamespace()?.getSignature() || null); + cls.setSignature(classSignature); + cls.setCategory(ClassCategory.OBJECT); + let arkMethods = []; + init4InstanceInitMethod(cls); + const instanceIRTransformer = new ArkIRTransformer(sourceFile, cls.getInstanceInitMethod()); + const instanceFieldInitializerStmts = []; + clsNode.properties.forEach(property => { + if (ts.isPropertyAssignment(property) || ts.isShorthandPropertyAssignment(property) || ts.isSpreadAssignment(property)) { + const arkField = buildProperty2ArkField(property, sourceFile, cls); + if (ts.isPropertyAssignment(property)) { + getInitStmts(instanceIRTransformer, arkField, property.initializer); + arkField.getInitializer().forEach(stmt => instanceFieldInitializerStmts.push(stmt)); + } + } + else { + let arkMethod = new ArkMethod(); + arkMethod.setDeclaringArkClass(cls); + buildArkMethodFromArkClass(property, cls, arkMethod, sourceFile); + } + }); + buildInitMethod(cls.getInstanceInitMethod(), instanceFieldInitializerStmts, instanceIRTransformer.getThisLocal()); + arkMethods.forEach(mtd => { + checkAndUpdateMethod(mtd, cls); + cls.addMethod(mtd); + }); +} +function genClassName(declaringName, cls, declaringMethod) { + if (!declaringName) { + const declaringArkNamespace = cls.getDeclaringArkNamespace(); + const num = declaringArkNamespace ? declaringArkNamespace.getAnonymousClassNumber() : cls.getDeclaringArkFile().getAnonymousClassNumber(); + declaringName = ANONYMOUS_CLASS_PREFIX + num; + } + const suffix = declaringMethod ? ANONYMOUS_CLASS_DELIMITER + declaringMethod.getDeclaringArkClass().getName() + '.' + declaringMethod.getName() : ''; + return declaringName + suffix; +} +function buildArkClassMembers(clsNode, cls, sourceFile) { + if (ts.isObjectLiteralExpression(clsNode)) { + return; + } + buildMethodsForClass(clsNode, cls, sourceFile); + const staticBlockMethodSignatures = buildStaticBlocksForClass(clsNode, cls, sourceFile); + let instanceIRTransformer; + let staticIRTransformer; + if (ts.isClassDeclaration(clsNode) || ts.isClassExpression(clsNode) || ts.isStructDeclaration(clsNode)) { + instanceIRTransformer = new ArkIRTransformer(sourceFile, cls.getInstanceInitMethod()); + staticIRTransformer = new ArkIRTransformer(sourceFile, cls.getStaticInitMethod()); + } + if (ts.isEnumDeclaration(clsNode)) { + staticIRTransformer = new ArkIRTransformer(sourceFile, cls.getStaticInitMethod()); + } + const staticInitStmts = []; + const instanceInitStmts = []; + let staticBlockId = 0; + clsNode.members.forEach(member => { + if (ts.isMethodDeclaration(member) || + ts.isConstructorDeclaration(member) || + ts.isMethodSignature(member) || + ts.isConstructSignatureDeclaration(member) || + ts.isAccessor(member) || + ts.isCallSignatureDeclaration(member)) { + // these node types have been handled at the beginning of this function by calling buildMethodsForClass + return; + } + else if (ts.isPropertyDeclaration(member) || ts.isPropertySignature(member)) { + const arkField = buildProperty2ArkField(member, sourceFile, cls); + if (ts.isClassDeclaration(clsNode) || ts.isClassExpression(clsNode) || ts.isStructDeclaration(clsNode)) { + if (arkField.isStatic()) { + getInitStmts(staticIRTransformer, arkField, member.initializer); + arkField.getInitializer().forEach(stmt => staticInitStmts.push(stmt)); + } + else { + if (!instanceIRTransformer) { + console.log(clsNode.getText(sourceFile)); + } + getInitStmts(instanceIRTransformer, arkField, member.initializer); + arkField.getInitializer().forEach(stmt => instanceInitStmts.push(stmt)); + } + } + } + else if (ts.isEnumMember(member)) { + const arkField = buildProperty2ArkField(member, sourceFile, cls); + getInitStmts(staticIRTransformer, arkField, member.initializer); + arkField.getInitializer().forEach(stmt => staticInitStmts.push(stmt)); + } + else if (ts.isIndexSignatureDeclaration(member)) { + buildIndexSignature2ArkField(member, sourceFile, cls); + } + else if (ts.isClassStaticBlockDeclaration(member)) { + const currStaticBlockMethodSig = staticBlockMethodSignatures[staticBlockId++]; + const staticBlockInvokeExpr = new ArkStaticInvokeExpr(currStaticBlockMethodSig, []); + staticInitStmts.push(new ArkInvokeStmt(staticBlockInvokeExpr)); + } + else if (ts.isSemicolonClassElement(member)) { + logger$t.trace('Skip these members.'); + } + else { + logger$t.warn(`Please contact developers to support new member in class: ${cls.getSignature().toString()}, member: ${member.getText()}!`); + } + }); + if (ts.isClassDeclaration(clsNode) || ts.isClassExpression(clsNode) || ts.isStructDeclaration(clsNode)) { + buildInitMethod(cls.getInstanceInitMethod(), instanceInitStmts, instanceIRTransformer.getThisLocal()); + buildInitMethod(cls.getStaticInitMethod(), staticInitStmts, staticIRTransformer.getThisLocal()); + } + if (ts.isEnumDeclaration(clsNode)) { + buildInitMethod(cls.getStaticInitMethod(), staticInitStmts, staticIRTransformer.getThisLocal()); + } +} +function buildMethodsForClass(clsNode, cls, sourceFile) { + clsNode.members.forEach(member => { + if (ts.isMethodDeclaration(member) || + ts.isConstructorDeclaration(member) || + ts.isMethodSignature(member) || + ts.isConstructSignatureDeclaration(member) || + ts.isAccessor(member) || + ts.isCallSignatureDeclaration(member)) { + let mthd = new ArkMethod(); + buildArkMethodFromArkClass(member, cls, mthd, sourceFile); + if (ts.isGetAccessor(member)) { + buildGetAccessor2ArkField(member, mthd, sourceFile); + } + else if (ts.isConstructorDeclaration(member)) { + buildParameterProperty2ArkField(member.parameters, cls, sourceFile); + } + } + }); +} +// params of constructor method may have modifiers such as public or private to directly define class properties with constructor +function buildParameterProperty2ArkField(params, cls, sourceFile) { + if (params.length === 0) { + return; + } + params.forEach(parameter => { + if (parameter.modifiers === undefined || !ts.isIdentifier(parameter.name)) { + return; + } + let field = new ArkField(); + field.setDeclaringArkClass(cls); + field.setCode(parameter.getText(sourceFile)); + field.setCategory(FieldCategory.PARAMETER_PROPERTY); + field.setOriginPosition(LineColPosition.buildFromNode(parameter, sourceFile)); + let fieldName = parameter.name.text; + let fieldType; + if (parameter.type) { + fieldType = buildGenericType(tsNode2Type(parameter.type, sourceFile, field), field); + } + else { + fieldType = UnknownType.getInstance(); + } + const fieldSignature = new FieldSignature(fieldName, cls.getSignature(), fieldType, false); + field.setSignature(fieldSignature); + field.setModifiers(buildModifiers(parameter)); + cls.addField(field); + }); +} +function buildStaticBlocksForClass(clsNode, cls, sourceFile) { + let staticInitBlockId = 0; + const staticBlockMethodSignatures = []; + clsNode.members.forEach(member => { + if (ts.isClassStaticBlockDeclaration(member)) { + const staticBlockMethod = new ArkMethod(); + staticBlockMethod.setDeclaringArkClass(cls); + staticBlockMethod.setIsGeneratedFlag(true); + staticBlockMethod.setCode(member.getText(sourceFile)); + const methodName = STATIC_BLOCK_METHOD_NAME_PREFIX + staticInitBlockId++; + const methodSubSignature = new MethodSubSignature(methodName, [], VoidType.getInstance(), true); + const methodSignature = new MethodSignature(cls.getSignature(), methodSubSignature); + staticBlockMethodSignatures.push(methodSignature); + staticBlockMethod.setImplementationSignature(methodSignature); + const { line, character } = ts.getLineAndCharacterOfPosition(sourceFile, member.getStart(sourceFile)); + staticBlockMethod.setLine(line + 1); + staticBlockMethod.setColumn(character + 1); + let bodyBuilder = new BodyBuilder(staticBlockMethod.getSignature(), member, staticBlockMethod, sourceFile); + staticBlockMethod.setBodyBuilder(bodyBuilder); + cls.addMethod(staticBlockMethod); + } + }); + return staticBlockMethodSignatures; +} +function getInitStmts(transformer, field, initNode) { + if (initNode) { + const stmts = []; + let { value: initValue, valueOriginalPositions: initPositions, stmts: initStmts } = transformer.tsNodeToValueAndStmts(initNode); + initStmts.forEach(stmt => stmts.push(stmt)); + if (IRUtils.moreThanOneAddress(initValue)) { + ({ value: initValue, valueOriginalPositions: initPositions, stmts: initStmts } = transformer.generateAssignStmtForValue(initValue, initPositions)); + initStmts.forEach(stmt => stmts.push(stmt)); + } + const fieldRef = new ArkInstanceFieldRef(transformer.getThisLocal(), field.getSignature()); + const fieldRefPositions = [FullPosition.DEFAULT, FullPosition.DEFAULT]; + const assignStmt = new ArkAssignStmt(fieldRef, initValue); + assignStmt.setOperandOriginalPositions([...fieldRefPositions, ...initPositions]); + stmts.push(assignStmt); + const fieldSourceCode = field.getCode(); + const fieldOriginPosition = field.getOriginPosition(); + for (const stmt of stmts) { + stmt.setOriginPositionInfo(fieldOriginPosition); + stmt.setOriginalText(fieldSourceCode); + } + field.setInitializer(stmts); + if (field.getType() instanceof UnknownType) { + field.getSignature().setType(initValue.getType()); + } + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$s = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'ArkValueTransformer'); +class ArkValueTransformer { + conditionalOperatorNo = 0; + tempLocalNo = 0; + sourceFile; + locals = new Map(); + globals; + thisLocal; + declaringMethod; + arkIRTransformer; + aliasTypeMap = new Map(); + builderMethodContextFlag = false; + constructor(arkIRTransformer, sourceFile, declaringMethod) { + this.arkIRTransformer = arkIRTransformer; + this.sourceFile = sourceFile; + this.thisLocal = new Local(THIS_NAME, declaringMethod.getDeclaringArkClass().getSignature().getType()); + this.locals.set(this.thisLocal.getName(), this.thisLocal); + this.declaringMethod = declaringMethod; + } + getLocals() { + return new Set(this.locals.values()); + } + getThisLocal() { + return this.thisLocal; + } + getAliasTypeMap() { + return this.aliasTypeMap; + } + addNewLocal(localName, localType = UnknownType.getInstance()) { + let local = new Local(localName, localType); + this.locals.set(localName, local); + return local; + } + getGlobals() { + return this.globals ?? null; + } + addNewGlobal(name, ref) { + let globalRef = new GlobalRef(name, ref); + this.globals = this.globals ?? new Map(); + this.globals.set(name, globalRef); + return globalRef; + } + tsNodeToValueAndStmts(node) { + if (ts__namespace.isBinaryExpression(node)) { + return this.binaryExpressionToValueAndStmts(node); + } + else if (ts__namespace.isCallExpression(node)) { + return this.callExpressionToValueAndStmts(node); + } + else if (ts__namespace.isVariableDeclarationList(node)) { + return this.variableDeclarationListToValueAndStmts(node); + } + else if (ts__namespace.isIdentifier(node)) { + return this.identifierToValueAndStmts(node); + } + else if (ts__namespace.isPropertyAccessExpression(node)) { + return this.propertyAccessExpressionToValue(node); + } + else if (ts__namespace.isPrefixUnaryExpression(node)) { + return this.prefixUnaryExpressionToValueAndStmts(node); + } + else if (ts__namespace.isPostfixUnaryExpression(node)) { + return this.postfixUnaryExpressionToValueAndStmts(node); + } + else if (ts__namespace.isTemplateExpression(node)) { + return this.templateExpressionToValueAndStmts(node); + } + else if (ts__namespace.isTaggedTemplateExpression(node)) { + return this.taggedTemplateExpressionToValueAndStmts(node); + } + else if (ts__namespace.isAwaitExpression(node)) { + return this.awaitExpressionToValueAndStmts(node); + } + else if (ts__namespace.isYieldExpression(node)) { + return this.yieldExpressionToValueAndStmts(node); + } + else if (ts__namespace.isDeleteExpression(node)) { + return this.deleteExpressionToValueAndStmts(node); + } + else if (ts__namespace.isVoidExpression(node)) { + return this.voidExpressionToValueAndStmts(node); + } + else if (ts__namespace.isElementAccessExpression(node)) { + return this.elementAccessExpressionToValueAndStmts(node); + } + else if (ts__namespace.isNewExpression(node)) { + return this.newExpressionToValueAndStmts(node); + } + else if (ts__namespace.isParenthesizedExpression(node)) { + return this.parenthesizedExpressionToValueAndStmts(node); + } + else if (ts__namespace.isAsExpression(node)) { + return this.asExpressionToValueAndStmts(node); + } + else if (ts__namespace.isNonNullExpression(node)) { + return this.nonNullExpressionToValueAndStmts(node); + } + else if (ts__namespace.isTypeAssertionExpression(node)) { + return this.typeAssertionToValueAndStmts(node); + } + else if (ts__namespace.isTypeOfExpression(node)) { + return this.typeOfExpressionToValueAndStmts(node); + } + else if (ts__namespace.isArrayLiteralExpression(node)) { + return this.arrayLiteralExpressionToValueAndStmts(node); + } + else if (this.isLiteralNode(node)) { + return this.literalNodeToValueAndStmts(node); + } + else if (ts__namespace.isArrowFunction(node) || ts__namespace.isFunctionExpression(node)) { + return this.callableNodeToValueAndStmts(node); + } + else if (ts__namespace.isClassExpression(node)) { + return this.classExpressionToValueAndStmts(node); + } + else if (ts__namespace.isEtsComponentExpression(node)) { + return this.etsComponentExpressionToValueAndStmts(node); + } + else if (ts__namespace.isObjectLiteralExpression(node)) { + return this.objectLiteralExpresionToValueAndStmts(node); + } + else if (node.kind === ts__namespace.SyntaxKind.ThisKeyword) { + return this.thisExpressionToValueAndStmts(node); + } + else if (ts__namespace.isConditionalExpression(node)) { + return this.conditionalExpressionToValueAndStmts(node); + } + return { + value: new Local(node.getText(this.sourceFile)), + valueOriginalPositions: [FullPosition.buildFromNode(node, this.sourceFile)], + stmts: [], + }; + } + tsNodeToSingleAddressValueAndStmts(node) { + const allStmts = []; + let { value, valueOriginalPositions, stmts } = this.tsNodeToValueAndStmts(node); + stmts.forEach(stmt => allStmts.push(stmt)); + if (IRUtils.moreThanOneAddress(value)) { + ({ value, valueOriginalPositions, stmts } = this.arkIRTransformer.generateAssignStmtForValue(value, valueOriginalPositions)); + stmts.forEach(stmt => allStmts.push(stmt)); + } + return { value, valueOriginalPositions, stmts: allStmts }; + } + thisExpressionToValueAndStmts(thisExpression) { + return { + value: this.getThisLocal(), + valueOriginalPositions: [FullPosition.buildFromNode(thisExpression, this.sourceFile)], + stmts: [], + }; + } + conditionalExpressionToValueAndStmts(conditionalExpression) { + const stmts = []; + const currConditionalOperatorIndex = this.conditionalOperatorNo++; + const { value: conditionValue, valueOriginalPositions: conditionPositions, stmts: conditionStmts, } = this.conditionToValueAndStmts(conditionalExpression.condition); + conditionStmts.forEach(stmt => stmts.push(stmt)); + const ifStmt = new ArkIfStmt(conditionValue); + ifStmt.setOperandOriginalPositions(conditionPositions); + stmts.push(ifStmt); + stmts.push(new DummyStmt(ArkIRTransformer.DUMMY_CONDITIONAL_OPERATOR_IF_TRUE_STMT + currConditionalOperatorIndex)); + const { value: whenTrueValue, valueOriginalPositions: whenTruePositions, stmts: whenTrueStmts, } = this.tsNodeToValueAndStmts(conditionalExpression.whenTrue); + whenTrueStmts.forEach(stmt => stmts.push(stmt)); + const resultLocal = this.generateTempLocal(); + const assignStmtWhenTrue = new ArkAssignStmt(resultLocal, whenTrueValue); + const resultLocalPosition = [whenTruePositions[0]]; + assignStmtWhenTrue.setOperandOriginalPositions([...resultLocalPosition, ...whenTruePositions]); + stmts.push(assignStmtWhenTrue); + stmts.push(new DummyStmt(ArkIRTransformer.DUMMY_CONDITIONAL_OPERATOR_IF_FALSE_STMT + currConditionalOperatorIndex)); + const { value: whenFalseValue, valueOriginalPositions: whenFalsePositions, stmts: whenFalseStmts, } = this.tsNodeToValueAndStmts(conditionalExpression.whenFalse); + whenFalseStmts.forEach(stmt => stmts.push(stmt)); + const assignStmt = new ArkAssignStmt(resultLocal, whenFalseValue); + assignStmt.setOperandOriginalPositions([...resultLocalPosition, ...whenFalsePositions]); + stmts.push(assignStmt); + stmts.push(new DummyStmt(ArkIRTransformer.DUMMY_CONDITIONAL_OPERATOR_END_STMT + currConditionalOperatorIndex)); + return { + value: resultLocal, + valueOriginalPositions: resultLocalPosition, + stmts: stmts, + }; + } + objectLiteralExpresionToValueAndStmts(objectLiteralExpression) { + const declaringArkClass = this.declaringMethod.getDeclaringArkClass(); + const declaringArkNamespace = declaringArkClass.getDeclaringArkNamespace(); + const anonymousClass = new ArkClass(); + if (declaringArkNamespace) { + buildNormalArkClassFromArkNamespace(objectLiteralExpression, declaringArkNamespace, anonymousClass, this.sourceFile, this.declaringMethod); + declaringArkNamespace.addArkClass(anonymousClass); + } + else { + const declaringArkFile = declaringArkClass.getDeclaringArkFile(); + buildNormalArkClassFromArkFile(objectLiteralExpression, declaringArkFile, anonymousClass, this.sourceFile, this.declaringMethod); + declaringArkFile.addArkClass(anonymousClass); + } + const objectLiteralExpressionPosition = FullPosition.buildFromNode(objectLiteralExpression, this.sourceFile); + const stmts = []; + const anonymousClassSignature = anonymousClass.getSignature(); + const anonymousClassType = new ClassType(anonymousClassSignature); + const newExpr = new ArkNewExpr(anonymousClassType); + const { value: newExprLocal, valueOriginalPositions: newExprLocalPositions, stmts: newExprStmts, } = this.arkIRTransformer.generateAssignStmtForValue(newExpr, [objectLiteralExpressionPosition]); + newExprStmts.forEach(stmt => stmts.push(stmt)); + const constructorMethodSubSignature = ArkSignatureBuilder.buildMethodSubSignatureFromMethodName(CONSTRUCTOR_NAME); + const constructorMethodSignature = new MethodSignature(anonymousClassSignature, constructorMethodSubSignature); + const constructorInvokeExpr = new ArkInstanceInvokeExpr(newExprLocal, constructorMethodSignature, []); + const assignStmt = new ArkAssignStmt(newExprLocal, constructorInvokeExpr); + const assignStmtPositions = [newExprLocalPositions[0], newExprLocalPositions[0], ...newExprLocalPositions]; + assignStmt.setOperandOriginalPositions(assignStmtPositions); + stmts.push(assignStmt); + return { + value: newExprLocal, + valueOriginalPositions: assignStmtPositions, + stmts: stmts, + }; + } + generateSystemComponentStmt(componentName, args, argPositionsAllFlat, componentExpression, currStmts) { + const stmts = [...currStmts]; + const componentExpressionPosition = FullPosition.buildFromNode(componentExpression, this.sourceFile); + const { value: componentValue, valueOriginalPositions: componentPositions, stmts: componentStmts, } = this.generateComponentCreationStmts(componentName, args, componentExpressionPosition, argPositionsAllFlat); + componentStmts.forEach(stmt => stmts.push(stmt)); + if (ts__namespace.isEtsComponentExpression(componentExpression) && componentExpression.body) { + for (const statement of componentExpression.body.statements) { + this.arkIRTransformer.tsNodeToStmts(statement).forEach(stmt => stmts.push(stmt)); + } + } + stmts.push(this.generateComponentPopStmts(componentName, componentExpressionPosition)); + return { + value: componentValue, + valueOriginalPositions: componentPositions, + stmts: stmts, + }; + } + generateCustomViewStmt(componentName, args, argPositionsAllFlat, componentExpression, currStmts) { + const stmts = [...currStmts]; + const componentExpressionPosition = FullPosition.buildFromNode(componentExpression, this.sourceFile); + const classSignature = ArkSignatureBuilder.buildClassSignatureFromClassName(componentName); + const classType = new ClassType(classSignature); + const newExpr = new ArkNewExpr(classType); + const { value: newExprLocal, valueOriginalPositions: newExprPositions, stmts: newExprStmts, } = this.arkIRTransformer.generateAssignStmtForValue(newExpr, [componentExpressionPosition]); + newExprStmts.forEach(stmt => stmts.push(stmt)); + const constructorMethodSubSignature = ArkSignatureBuilder.buildMethodSubSignatureFromMethodName(CONSTRUCTOR_NAME); + const constructorMethodSignature = new MethodSignature(classSignature, constructorMethodSubSignature); + const instanceInvokeExpr = new ArkInstanceInvokeExpr(newExprLocal, constructorMethodSignature, args); + const assignStmt = new ArkAssignStmt(newExprLocal, instanceInvokeExpr); + const assignStmtPositions = [componentExpressionPosition, componentExpressionPosition, ...newExprPositions, ...argPositionsAllFlat]; + assignStmt.setOperandOriginalPositions(assignStmtPositions); + stmts.push(assignStmt); + const createViewArgs = [newExprLocal]; + const createViewArgPositionsAll = [newExprPositions]; + if (ts__namespace.isEtsComponentExpression(componentExpression) && componentExpression.body) { + const anonymous = ts__namespace.factory.createArrowFunction([], [], [], undefined, undefined, componentExpression.body); + // @ts-expect-error: add pos info for the created ArrowFunction + anonymous.pos = componentExpression.body.pos; + // @ts-expect-error: add end info for the created ArrowFunction + anonymous.end = componentExpression.body.end; + const { value: builderMethod, valueOriginalPositions: builderMethodPositions } = this.callableNodeToValueAndStmts(anonymous); + createViewArgs.push(builderMethod); + createViewArgPositionsAll.push(builderMethodPositions); + } + const { value: componentValue, valueOriginalPositions: componentPositions, stmts: componentStmts, } = this.generateComponentCreationStmts(COMPONENT_CUSTOMVIEW, createViewArgs, componentExpressionPosition, createViewArgPositionsAll.flat()); + componentStmts.forEach(stmt => stmts.push(stmt)); + stmts.push(this.generateComponentPopStmts(COMPONENT_CUSTOMVIEW, componentExpressionPosition)); + return { + value: componentValue, + valueOriginalPositions: componentPositions, + stmts: stmts, + }; + } + generateComponentCreationStmts(componentName, createArgs, componentExpressionPosition, createArgsPositionsAllFlat) { + const createMethodSignature = ArkSignatureBuilder.buildMethodSignatureFromClassNameAndMethodName(componentName, COMPONENT_CREATE_FUNCTION); + const createInvokeExpr = new ArkStaticInvokeExpr(createMethodSignature, createArgs); + const createInvokeExprPositions = [componentExpressionPosition, ...createArgsPositionsAllFlat]; + const { value: componentValue, valueOriginalPositions: componentPositions, stmts: componentStmts, } = this.arkIRTransformer.generateAssignStmtForValue(createInvokeExpr, createInvokeExprPositions); + return { + value: componentValue, + valueOriginalPositions: componentPositions, + stmts: componentStmts, + }; + } + generateComponentPopStmts(componentName, componentExpressionPosition) { + const popMethodSignature = ArkSignatureBuilder.buildMethodSignatureFromClassNameAndMethodName(componentName, COMPONENT_POP_FUNCTION); + const popInvokeExpr = new ArkStaticInvokeExpr(popMethodSignature, []); + const popInvokeExprPositions = [componentExpressionPosition]; + const popInvokeStmt = new ArkInvokeStmt(popInvokeExpr); + popInvokeStmt.setOperandOriginalPositions(popInvokeExprPositions); + return popInvokeStmt; + } + etsComponentExpressionToValueAndStmts(etsComponentExpression) { + const stmts = []; + const componentName = etsComponentExpression.expression.text; + const { args: args, argPositions: argPositions } = this.parseArguments(stmts, etsComponentExpression.arguments); + if (isEtsSystemComponent(componentName)) { + return this.generateSystemComponentStmt(componentName, args, argPositions, etsComponentExpression, stmts); + } + return this.generateCustomViewStmt(componentName, args, argPositions, etsComponentExpression, stmts); + } + classExpressionToValueAndStmts(classExpression) { + const declaringArkClass = this.declaringMethod.getDeclaringArkClass(); + const declaringArkNamespace = declaringArkClass.getDeclaringArkNamespace(); + const newClass = new ArkClass(); + if (declaringArkNamespace) { + buildNormalArkClassFromArkNamespace(classExpression, declaringArkNamespace, newClass, this.sourceFile, this.declaringMethod); + declaringArkNamespace.addArkClass(newClass); + } + else { + const declaringArkFile = declaringArkClass.getDeclaringArkFile(); + buildNormalArkClassFromArkFile(classExpression, declaringArkFile, newClass, this.sourceFile, this.declaringMethod); + declaringArkFile.addArkClass(newClass); + } + const classValue = this.addNewLocal(newClass.getName(), new ClassType(newClass.getSignature())); + return { + value: classValue, + valueOriginalPositions: [FullPosition.buildFromNode(classExpression, this.sourceFile)], + stmts: [], + }; + } + templateExpressionToValueAndStmts(templateExpression) { + const { stmts, stringTextValues, placeholderValues, stringTextPositions, placeholderPositions } = this.collectTemplateValues(templateExpression); + const { placeholderStringLocals, placeholderStringLocalPositions, newStmts } = this.processTemplatePlaceholders(placeholderValues, placeholderPositions, stmts); + return this.combineTemplateParts(stringTextValues, stringTextPositions, placeholderStringLocals, placeholderStringLocalPositions, newStmts); + } + processTemplatePlaceholders(placeholderValues, placeholderPositions, currStmts) { + const placeholderStringLocals = []; + const placeholderStringLocalPositions = []; + const newStmts = [...currStmts]; + for (let i = 0; i < placeholderValues.length; i++) { + let placeholderValue = placeholderValues[i]; + let placeholderPosition = [placeholderPositions[i]]; + let placeholderStmts = []; + if (!(placeholderValue instanceof Local)) { + ({ + value: placeholderValue, + valueOriginalPositions: placeholderPosition, + stmts: placeholderStmts, + } = this.arkIRTransformer.generateAssignStmtForValue(placeholderValue, placeholderPosition)); + } + placeholderStmts.forEach(stmt => newStmts.push(stmt)); + const toStringExpr = new ArkInstanceInvokeExpr(placeholderValue, Builtin.TO_STRING_METHOD_SIGNATURE, []); + const toStringExprPosition = [placeholderPosition[0], placeholderPosition[0]]; + const { value: placeholderStringLocal, valueOriginalPositions: placeholderStringPositions, stmts: toStringStmts, } = this.arkIRTransformer.generateAssignStmtForValue(toStringExpr, toStringExprPosition); + placeholderStringLocals.push(placeholderStringLocal); + placeholderStringLocalPositions.push(placeholderStringPositions[0]); + toStringStmts.forEach(stmt => newStmts.push(stmt)); + } + return { + placeholderStringLocals, + placeholderStringLocalPositions, + newStmts, + }; + } + combineTemplateParts(stringTextValues, stringTextPositions, placeholderStringLocals, placeholderStringLocalPositions, currStmts) { + const templateParts = []; + const templatePartPositions = []; + for (let i = 0; i < placeholderStringLocals.length; i++) { + if (stringTextValues[i] !== ValueUtil.EMPTY_STRING_CONSTANT) { + templateParts.push(stringTextValues[i]); + templatePartPositions.push(stringTextPositions[i]); + } + templateParts.push(placeholderStringLocals[i]); + templatePartPositions.push(placeholderStringLocalPositions[i]); + } + if (stringTextValues[stringTextValues.length - 1] !== ValueUtil.EMPTY_STRING_CONSTANT) { + templateParts.push(stringTextValues[stringTextValues.length - 1]); + templatePartPositions.push(stringTextPositions[stringTextPositions.length - 1]); + } + let currTemplateResult = templateParts[0]; + let currTemplateResultPosition = templatePartPositions[0]; + const finalStmts = [...currStmts]; + for (let i = 1; i < templateParts.length; i++) { + const nextTemplatePartPosition = templatePartPositions[i]; + const normalBinopExpr = new ArkNormalBinopExpr(currTemplateResult, templateParts[i], exports.NormalBinaryOperator.Addition); + const normalBinopExprPositions = [ + FullPosition.merge(currTemplateResultPosition, nextTemplatePartPosition), + currTemplateResultPosition, + nextTemplatePartPosition, + ]; + const { value: combinationValue, valueOriginalPositions: combinationValuePositions, stmts: combinationStmts, } = this.arkIRTransformer.generateAssignStmtForValue(normalBinopExpr, normalBinopExprPositions); + combinationStmts.forEach(stmt => finalStmts.push(stmt)); + currTemplateResult = combinationValue; + currTemplateResultPosition = combinationValuePositions[0]; + } + return { + value: currTemplateResult, + valueOriginalPositions: [currTemplateResultPosition], + stmts: finalStmts, + }; + } + taggedTemplateExpressionToValueAndStmts(taggedTemplateExpression) { + const { stmts, stringTextValues, placeholderValues, stringTextPositions, placeholderPositions } = this.collectTemplateValues(taggedTemplateExpression.template); + const stringTextBaseType = StringType.getInstance(); + const stringTextArrayLen = stringTextValues.length; + const stringTextArrayLenValue = ValueUtil.getOrCreateNumberConst(stringTextArrayLen); + const stringTextArrayLenPosition = FullPosition.DEFAULT; + const { value: templateObjectLocal, valueOriginalPositions: templateObjectLocalPositions, stmts: templateObjectStmts, } = this.generateArrayExprAndStmts(stringTextBaseType, stringTextArrayLenValue, stringTextArrayLenPosition, stringTextArrayLen, stringTextValues, stringTextPositions, stmts, FullPosition.DEFAULT, true); + const placeholderBaseType = AnyType.getInstance(); + const placeholdersArrayLen = placeholderValues.length; + const placeholdersArrayLenValue = ValueUtil.getOrCreateNumberConst(placeholdersArrayLen); + const placeholdersArrayLenPosition = FullPosition.DEFAULT; + const { value: placeholdersLocal, valueOriginalPositions: placeholdersLocalPositions, stmts: placeholdersStmts, } = this.generateArrayExprAndStmts(placeholderBaseType, placeholdersArrayLenValue, placeholdersArrayLenPosition, placeholdersArrayLen, placeholderValues, placeholderPositions, templateObjectStmts, FullPosition.DEFAULT, true); + const taggedFuncArgus = { + realGenericTypes: undefined, + args: [templateObjectLocal, placeholdersLocal], + argPositions: [templateObjectLocalPositions[0], placeholdersLocalPositions[0]], + }; + return this.generateInvokeValueAndStmts(taggedTemplateExpression.tag, taggedFuncArgus, placeholdersStmts, taggedTemplateExpression); + } + collectTemplateValues(templateLiteral) { + const stmts = []; + if (ts__namespace.isNoSubstitutionTemplateLiteral(templateLiteral)) { + const templateLiteralString = templateLiteral.getText(this.sourceFile); + return { + stmts: [], + stringTextValues: [ValueUtil.createStringConst(templateLiteralString)], + placeholderValues: [], + stringTextPositions: [FullPosition.buildFromNode(templateLiteral, this.sourceFile)], + placeholderPositions: [], + }; + } + const head = templateLiteral.head; + const stringTextValues = [ValueUtil.createStringConst(head.rawText || '')]; + const placeholderValues = []; + const stringTextPositions = [FullPosition.buildFromNode(head, this.sourceFile)]; + const placeholderPositions = []; + for (const templateSpan of templateLiteral.templateSpans) { + let { value: exprValue, valueOriginalPositions: exprPositions, stmts: exprStmts } = this.tsNodeToValueAndStmts(templateSpan.expression); + exprStmts.forEach(stmt => stmts.push(stmt)); + if (IRUtils.moreThanOneAddress(exprValue)) { + ({ + value: exprValue, + valueOriginalPositions: exprPositions, + stmts: exprStmts, + } = this.arkIRTransformer.generateAssignStmtForValue(exprValue, exprPositions)); + exprStmts.forEach(stmt => stmts.push(stmt)); + } + placeholderValues.push(exprValue); + placeholderPositions.push(exprPositions[0]); + stringTextPositions.push(FullPosition.buildFromNode(templateSpan.literal, this.sourceFile)); + stringTextValues.push(ValueUtil.createStringConst(templateSpan.literal.rawText || '')); + } + return { + stmts, + stringTextValues, + placeholderValues, + stringTextPositions, + placeholderPositions, + }; + } + identifierToValueAndStmts(identifier, variableDefFlag = false) { + let identifierValue; + let identifierPositions = [FullPosition.buildFromNode(identifier, this.sourceFile)]; + if (identifier.text === UndefinedType.getInstance().getName()) { + identifierValue = ValueUtil.getUndefinedConst(); + } + else { + if (variableDefFlag) { + identifierValue = this.addNewLocal(identifier.text); + } + else { + identifierValue = this.getOrCreateLocal(identifier.text); + } + } + return { + value: identifierValue, + valueOriginalPositions: identifierPositions, + stmts: [], + }; + } + propertyAccessExpressionToValue(propertyAccessExpression) { + const stmts = []; + let { value: baseValue, valueOriginalPositions: basePositions, stmts: baseStmts } = this.tsNodeToValueAndStmts(propertyAccessExpression.expression); + baseStmts.forEach(stmt => stmts.push(stmt)); + if (IRUtils.moreThanOneAddress(baseValue)) { + ({ + value: baseValue, + valueOriginalPositions: basePositions, + stmts: baseStmts, + } = this.arkIRTransformer.generateAssignStmtForValue(baseValue, basePositions)); + baseStmts.forEach(stmt => stmts.push(stmt)); + } + if (!(baseValue instanceof Local)) { + ({ + value: baseValue, + valueOriginalPositions: basePositions, + stmts: baseStmts, + } = this.arkIRTransformer.generateAssignStmtForValue(baseValue, basePositions)); + baseStmts.forEach(stmt => stmts.push(stmt)); + } + const fieldRefPositions = [FullPosition.buildFromNode(propertyAccessExpression, this.sourceFile), ...basePositions]; + // this if for the case: const obj: Object = Object.create(Object.prototype); + if (baseValue instanceof Local && baseValue.getName() === Builtin.OBJECT) { + this.locals.delete(baseValue.getName()); + const fieldSignature = new FieldSignature(propertyAccessExpression.name.getText(this.sourceFile), Builtin.OBJECT_CLASS_SIGNATURE, UnknownType.getInstance(), true); + const fieldRef = new ArkStaticFieldRef(fieldSignature); + return { + value: fieldRef, + valueOriginalPositions: fieldRefPositions, + stmts: stmts, + }; + } + let fieldSignature; + if (baseValue instanceof Local && baseValue.getType() instanceof ClassType) { + fieldSignature = new FieldSignature(propertyAccessExpression.name.getText(this.sourceFile), baseValue.getType().getClassSignature(), UnknownType.getInstance()); + } + else { + fieldSignature = ArkSignatureBuilder.buildFieldSignatureFromFieldName(propertyAccessExpression.name.getText(this.sourceFile)); + } + const fieldRef = new ArkInstanceFieldRef(baseValue, fieldSignature); + return { + value: fieldRef, + valueOriginalPositions: fieldRefPositions, + stmts: stmts, + }; + } + elementAccessExpressionToValueAndStmts(elementAccessExpression) { + const stmts = []; + let { value: baseValue, valueOriginalPositions: basePositions, stmts: baseStmts } = this.tsNodeToValueAndStmts(elementAccessExpression.expression); + baseStmts.forEach(stmt => stmts.push(stmt)); + if (!(baseValue instanceof Local)) { + ({ + value: baseValue, + valueOriginalPositions: basePositions, + stmts: baseStmts, + } = this.arkIRTransformer.generateAssignStmtForValue(baseValue, basePositions)); + baseStmts.forEach(stmt => stmts.push(stmt)); + } + let { value: argumentValue, valueOriginalPositions: arguPositions, stmts: argumentStmts, } = this.tsNodeToValueAndStmts(elementAccessExpression.argumentExpression); + argumentStmts.forEach(stmt => stmts.push(stmt)); + if (IRUtils.moreThanOneAddress(argumentValue)) { + ({ + value: argumentValue, + valueOriginalPositions: arguPositions, + stmts: argumentStmts, + } = this.arkIRTransformer.generateAssignStmtForValue(argumentValue, arguPositions)); + argumentStmts.forEach(stmt => stmts.push(stmt)); + } + let elementAccessExpr; + if (baseValue.getType() instanceof ArrayType) { + elementAccessExpr = new ArkArrayRef(baseValue, argumentValue); + } + else { + // TODO: deal with ArkStaticFieldRef + const fieldSignature = ArkSignatureBuilder.buildFieldSignatureFromFieldName(argumentValue.toString()); + elementAccessExpr = new ArkInstanceFieldRef(baseValue, fieldSignature); + } + // reserve positions for field name + const exprPositions = [FullPosition.buildFromNode(elementAccessExpression, this.sourceFile), ...basePositions, ...arguPositions]; + return { + value: elementAccessExpr, + valueOriginalPositions: exprPositions, + stmts: stmts, + }; + } + callExpressionToValueAndStmts(callExpression) { + const stmts = []; + const argus = this.parseArgumentsOfCallExpression(stmts, callExpression); + return this.generateInvokeValueAndStmts(callExpression.expression, argus, stmts, callExpression); + } + generateInvokeValueAndStmts(functionNameNode, argus, currStmts, callExpression) { + const stmts = [...currStmts]; + let { value: callerValue, valueOriginalPositions: callerPositions, stmts: callerStmts } = this.tsNodeToValueAndStmts(functionNameNode); + callerStmts.forEach(stmt => stmts.push(stmt)); + let invokeValue; + let invokeValuePositions = [FullPosition.buildFromNode(callExpression, this.sourceFile)]; + const { args, argPositions, realGenericTypes } = argus; + if (callerValue instanceof AbstractFieldRef) { + let methodSignature; + const declareSignature = callerValue.getFieldSignature().getDeclaringSignature(); + if (declareSignature instanceof ClassSignature) { + methodSignature = new MethodSignature(declareSignature, ArkSignatureBuilder.buildMethodSubSignatureFromMethodName(callerValue.getFieldName())); + } + else { + methodSignature = ArkSignatureBuilder.buildMethodSignatureFromMethodName(callerValue.getFieldName()); + } + if (callerValue instanceof ArkInstanceFieldRef) { + invokeValue = new ArkInstanceInvokeExpr(callerValue.getBase(), methodSignature, args, realGenericTypes); + invokeValuePositions.push(...callerPositions.slice(1)); + } + else { + invokeValue = new ArkStaticInvokeExpr(methodSignature, args, realGenericTypes); + } + } + else if (callerValue instanceof Local) { + const callerName = callerValue.getName(); + let classSignature = ArkSignatureBuilder.buildClassSignatureFromClassName(callerName); + let cls = ModelUtils.getClass(this.declaringMethod, classSignature); + if (cls?.hasComponentDecorator() && ts__namespace.isCallExpression(callExpression)) { + return this.generateCustomViewStmt(callerName, args, argPositions, callExpression, stmts); + } + else if ((callerName === COMPONENT_FOR_EACH || callerName === COMPONENT_LAZY_FOR_EACH) && ts__namespace.isCallExpression(callExpression)) { + // foreach/lazyforeach will be parsed as ts.callExpression + return this.generateSystemComponentStmt(callerName, args, argPositions, callExpression, stmts); + } + const methodSignature = ArkSignatureBuilder.buildMethodSignatureFromMethodName(callerName); + if (callerValue.getType() instanceof FunctionType) { + invokeValue = new ArkPtrInvokeExpr(methodSignature, callerValue, args, realGenericTypes); + } + else { + invokeValue = new ArkStaticInvokeExpr(methodSignature, args, realGenericTypes); + } + } + else { + ({ + value: callerValue, + valueOriginalPositions: callerPositions, + stmts: callerStmts, + } = this.arkIRTransformer.generateAssignStmtForValue(callerValue, callerPositions)); + callerStmts.forEach(stmt => stmts.push(stmt)); + const methodSignature = ArkSignatureBuilder.buildMethodSignatureFromMethodName(callerValue.getName()); + invokeValue = new ArkStaticInvokeExpr(methodSignature, args, realGenericTypes); + } + invokeValuePositions.push(...argPositions); + return { + value: invokeValue, + valueOriginalPositions: invokeValuePositions, + stmts: stmts, + }; + } + parseArgumentsOfCallExpression(currStmts, callExpression) { + let realGenericTypes; + if (callExpression.typeArguments) { + realGenericTypes = []; + callExpression.typeArguments.forEach(typeArgument => { + realGenericTypes.push(this.resolveTypeNode(typeArgument)); + }); + } + let builderMethodIndexes; + if (ts__namespace.isIdentifier(callExpression.expression)) { + const callerName = callExpression.expression.text; + if (callerName === COMPONENT_FOR_EACH || callerName === COMPONENT_LAZY_FOR_EACH) { + builderMethodIndexes = new Set([1]); + } + } + const { args: args, argPositions: argPositions } = this.parseArguments(currStmts, callExpression.arguments, builderMethodIndexes); + return { + realGenericTypes: realGenericTypes, + args: args, + argPositions: argPositions, + }; + } + parseArguments(currStmts, argumentNodes, builderMethodIndexes) { + const args = []; + const argPositions = []; + if (argumentNodes) { + for (let i = 0; i < argumentNodes.length; i++) { + const argument = argumentNodes[i]; + const prevBuilderMethodContextFlag = this.builderMethodContextFlag; + if (builderMethodIndexes?.has(i)) { + this.builderMethodContextFlag = true; + this.arkIRTransformer.setBuilderMethodContextFlag(true); + } + let { value: argValue, valueOriginalPositions: argPositionsSingle, stmts: argStmts } = this.tsNodeToValueAndStmts(argument); + this.builderMethodContextFlag = prevBuilderMethodContextFlag; + this.arkIRTransformer.setBuilderMethodContextFlag(prevBuilderMethodContextFlag); + argStmts.forEach(s => currStmts.push(s)); + if (IRUtils.moreThanOneAddress(argValue)) { + ({ + value: argValue, + valueOriginalPositions: argPositionsSingle, + stmts: argStmts, + } = this.arkIRTransformer.generateAssignStmtForValue(argValue, argPositionsSingle)); + argStmts.forEach(s => currStmts.push(s)); + } + args.push(argValue); + argPositions.push(argPositionsSingle[0]); + } + } + return { args: args, argPositions: argPositions }; + } + callableNodeToValueAndStmts(callableNode) { + const declaringClass = this.declaringMethod.getDeclaringArkClass(); + const arrowArkMethod = new ArkMethod(); + if (this.builderMethodContextFlag) { + ModelUtils.implicitArkUIBuilderMethods.add(arrowArkMethod); + } + buildArkMethodFromArkClass(callableNode, declaringClass, arrowArkMethod, this.sourceFile, this.declaringMethod); + const callableType = new FunctionType(arrowArkMethod.getSignature()); + const callableValue = this.addNewLocal(arrowArkMethod.getName(), callableType); + return { + value: callableValue, + valueOriginalPositions: [FullPosition.buildFromNode(callableNode, this.sourceFile)], + stmts: [], + }; + } + newExpressionToValueAndStmts(newExpression) { + let className = ''; + if (ts__namespace.isClassExpression(newExpression.expression) && newExpression.expression.name) { + className = newExpression.expression.name.text; + } + else { + className = newExpression.expression.getText(this.sourceFile); + } + if (className === Builtin.ARRAY) { + return this.newArrayExpressionToValueAndStmts(newExpression); + } + const stmts = []; + let realGenericTypes; + if (newExpression.typeArguments) { + realGenericTypes = []; + newExpression.typeArguments.forEach(typeArgument => { + realGenericTypes.push(this.resolveTypeNode(typeArgument)); + }); + } + let classSignature = ArkSignatureBuilder.buildClassSignatureFromClassName(className); + let classType = new ClassType(classSignature, realGenericTypes); + if (className === Builtin.OBJECT) { + classSignature = Builtin.OBJECT_CLASS_SIGNATURE; + classType = Builtin.OBJECT_CLASS_TYPE; + } + const newExpr = new ArkNewExpr(classType); + const { value: newLocal, valueOriginalPositions: newLocalPositions, stmts: newExprStmts, } = this.arkIRTransformer.generateAssignStmtForValue(newExpr, [FullPosition.buildFromNode(newExpression, this.sourceFile)]); + newExprStmts.forEach(stmt => stmts.push(stmt)); + const constructorMethodSubSignature = ArkSignatureBuilder.buildMethodSubSignatureFromMethodName(CONSTRUCTOR_NAME); + const constructorMethodSignature = new MethodSignature(classSignature, constructorMethodSubSignature); + const { args: argValues, argPositions: argPositions } = this.parseArguments(stmts, newExpression.arguments); + const instanceInvokeExpr = new ArkInstanceInvokeExpr(newLocal, constructorMethodSignature, argValues); + const assignStmt = new ArkAssignStmt(newLocal, instanceInvokeExpr); + const assignStmtPositions = [newLocalPositions[0], newLocalPositions[0], ...newLocalPositions, ...argPositions]; + assignStmt.setOperandOriginalPositions(assignStmtPositions); + stmts.push(assignStmt); + return { value: newLocal, valueOriginalPositions: assignStmtPositions, stmts: stmts }; + } + newArrayExpressionToValueAndStmts(newArrayExpression) { + let baseType = UnknownType.getInstance(); + if (newArrayExpression.typeArguments && newArrayExpression.typeArguments.length > 0) { + const argumentType = this.resolveTypeNode(newArrayExpression.typeArguments[0]); + if (!(argumentType instanceof AnyType || argumentType instanceof UnknownType)) { + baseType = argumentType; + } + } + const stmts = []; + const { args: argumentValues, argPositions: argPositions } = this.parseArguments(stmts, newArrayExpression.arguments); + let argumentsLength = newArrayExpression.arguments ? newArrayExpression.arguments.length : 0; + let arrayLengthValue; + let arrayLength = -1; + let arrayLengthPosition = FullPosition.DEFAULT; + if (argumentsLength === 1 && (argumentValues[0].getType() instanceof NumberType || argumentValues[0].getType() instanceof UnknownType)) { + arrayLengthValue = argumentValues[0]; + arrayLengthPosition = argPositions[0]; + } + else { + arrayLengthValue = ValueUtil.getOrCreateNumberConst(argumentsLength); + arrayLength = argumentsLength; + } + if (baseType instanceof UnknownType) { + if (argumentsLength > 1 && !(argumentValues[0].getType() instanceof UnknownType)) { + baseType = argumentValues[0].getType(); + } + else { + baseType = AnyType.getInstance(); + } + } + const newArrayExprPosition = FullPosition.buildFromNode(newArrayExpression, this.sourceFile); + return this.generateArrayExprAndStmts(baseType, arrayLengthValue, arrayLengthPosition, arrayLength, argumentValues, argPositions, stmts, newArrayExprPosition, false); + } + arrayLiteralExpressionToValueAndStmts(arrayLiteralExpression) { + const stmts = []; + const elementTypes = new Set(); + const elementValues = []; + const elementPositions = []; + const arrayLength = arrayLiteralExpression.elements.length; + for (const element of arrayLiteralExpression.elements) { + let { value: elementValue, valueOriginalPositions: elementPosition, stmts: elementStmts } = this.tsNodeToValueAndStmts(element); + elementStmts.forEach(stmt => stmts.push(stmt)); + if (IRUtils.moreThanOneAddress(elementValue)) { + ({ + value: elementValue, + valueOriginalPositions: elementPosition, + stmts: elementStmts, + } = this.arkIRTransformer.generateAssignStmtForValue(elementValue, elementPosition)); + elementStmts.forEach(stmt => stmts.push(stmt)); + } + elementValues.push(elementValue); + elementTypes.add(elementValue.getType()); + elementPositions.push(elementPosition[0]); + } + let baseType = AnyType.getInstance(); + if (elementTypes.size === 1) { + baseType = elementTypes.keys().next().value; + } + else if (elementTypes.size > 1) { + baseType = new UnionType(Array.from(elementTypes)); + } + const newArrayExprPosition = FullPosition.buildFromNode(arrayLiteralExpression, this.sourceFile); + return this.generateArrayExprAndStmts(baseType, ValueUtil.getOrCreateNumberConst(arrayLength), FullPosition.DEFAULT, arrayLength, elementValues, elementPositions, stmts, newArrayExprPosition, true); + } + generateArrayExprAndStmts(baseType, arrayLengthValue, arrayLengthPosition, arrayLength, initializerValues, initializerPositions, currStmts, newArrayExprPosition, fromLiteral) { + const stmts = [...currStmts]; + const newArrayExpr = new ArkNewArrayExpr(baseType, arrayLengthValue, fromLiteral); + const newArrayExprPositions = [newArrayExprPosition, arrayLengthPosition]; + const { value: arrayLocal, valueOriginalPositions: arrayLocalPositions, stmts: arrayStmts, } = this.arkIRTransformer.generateAssignStmtForValue(newArrayExpr, newArrayExprPositions); + arrayStmts.forEach(stmt => stmts.push(stmt)); + for (let i = 0; i < arrayLength; i++) { + const indexValue = ValueUtil.getOrCreateNumberConst(i); + const arrayRef = new ArkArrayRef(arrayLocal, indexValue); + const arrayRefPositions = [arrayLocalPositions[0], ...arrayLocalPositions, FullPosition.DEFAULT]; + const assignStmt = new ArkAssignStmt(arrayRef, initializerValues[i]); + assignStmt.setOperandOriginalPositions([...arrayRefPositions, initializerPositions[i]]); + stmts.push(assignStmt); + } + return { + value: arrayLocal, + valueOriginalPositions: arrayLocalPositions, + stmts: stmts, + }; + } + prefixUnaryExpressionToValueAndStmts(prefixUnaryExpression) { + const stmts = []; + let { value: operandValue, valueOriginalPositions: operandPositions, stmts: operandStmts } = this.tsNodeToValueAndStmts(prefixUnaryExpression.operand); + operandStmts.forEach(stmt => stmts.push(stmt)); + if (IRUtils.moreThanOneAddress(operandValue)) { + ({ + value: operandValue, + valueOriginalPositions: operandPositions, + stmts: operandStmts, + } = this.arkIRTransformer.generateAssignStmtForValue(operandValue, operandPositions)); + operandStmts.forEach(stmt => stmts.push(stmt)); + } + const operatorToken = prefixUnaryExpression.operator; + let exprPositions = [FullPosition.buildFromNode(prefixUnaryExpression, this.sourceFile)]; + if (operatorToken === ts__namespace.SyntaxKind.PlusPlusToken || operatorToken === ts__namespace.SyntaxKind.MinusMinusToken) { + const binaryOperator = operatorToken === ts__namespace.SyntaxKind.PlusPlusToken ? exports.NormalBinaryOperator.Addition : exports.NormalBinaryOperator.Subtraction; + const binopExpr = new ArkNormalBinopExpr(operandValue, ValueUtil.getOrCreateNumberConst(1), binaryOperator); + exprPositions.push(...operandPositions, FullPosition.DEFAULT); + const assignStmt = new ArkAssignStmt(operandValue, binopExpr); + assignStmt.setOperandOriginalPositions([...operandPositions, ...exprPositions]); + stmts.push(assignStmt); + return { + value: operandValue, + valueOriginalPositions: operandPositions, + stmts: stmts, + }; + } + else if (operatorToken === ts__namespace.SyntaxKind.PlusToken) { + return { + value: operandValue, + valueOriginalPositions: operandPositions, + stmts: stmts, + }; + } + else { + let unopExpr; + const operator = ArkIRTransformer.tokenToUnaryOperator(operatorToken); + if (operator) { + unopExpr = new ArkUnopExpr(operandValue, operator); + exprPositions.push(...operandPositions); + } + else { + unopExpr = ValueUtil.getUndefinedConst(); + exprPositions = [FullPosition.DEFAULT]; + } + return { + value: unopExpr, + valueOriginalPositions: exprPositions, + stmts: stmts, + }; + } + } + postfixUnaryExpressionToValueAndStmts(postfixUnaryExpression) { + const stmts = []; + let { value: operandValue, valueOriginalPositions: operandPositions, stmts: exprStmts } = this.tsNodeToValueAndStmts(postfixUnaryExpression.operand); + exprStmts.forEach(stmt => stmts.push(stmt)); + if (IRUtils.moreThanOneAddress(operandValue)) { + ({ + value: operandValue, + valueOriginalPositions: operandPositions, + stmts: exprStmts, + } = this.arkIRTransformer.generateAssignStmtForValue(operandValue, operandPositions)); + exprStmts.forEach(stmt => stmts.push(stmt)); + } + let value; + let exprPositions = [FullPosition.buildFromNode(postfixUnaryExpression, this.sourceFile)]; + const operatorToken = postfixUnaryExpression.operator; + if (operatorToken === ts__namespace.SyntaxKind.PlusPlusToken || operatorToken === ts__namespace.SyntaxKind.MinusMinusToken) { + const binaryOperator = operatorToken === ts__namespace.SyntaxKind.PlusPlusToken ? exports.NormalBinaryOperator.Addition : exports.NormalBinaryOperator.Subtraction; + const binopExpr = new ArkNormalBinopExpr(operandValue, ValueUtil.getOrCreateNumberConst(1), binaryOperator); + exprPositions.push(...operandPositions, FullPosition.DEFAULT); + const assignStmt = new ArkAssignStmt(operandValue, binopExpr); + assignStmt.setOperandOriginalPositions([...operandPositions, ...exprPositions]); + stmts.push(assignStmt); + value = operandValue; + } + else { + value = ValueUtil.getUndefinedConst(); + exprPositions = [FullPosition.DEFAULT]; + } + return { + value: value, + valueOriginalPositions: exprPositions, + stmts: stmts, + }; + } + awaitExpressionToValueAndStmts(awaitExpression) { + const stmts = []; + let { value: promiseValue, valueOriginalPositions: promisePositions, stmts: promiseStmts } = this.tsNodeToValueAndStmts(awaitExpression.expression); + promiseStmts.forEach(stmt => stmts.push(stmt)); + if (IRUtils.moreThanOneAddress(promiseValue)) { + ({ + value: promiseValue, + valueOriginalPositions: promisePositions, + stmts: promiseStmts, + } = this.arkIRTransformer.generateAssignStmtForValue(promiseValue, promisePositions)); + promiseStmts.forEach(stmt => stmts.push(stmt)); + } + const awaitExpr = new ArkAwaitExpr(promiseValue); + const awaitExprPositions = [FullPosition.buildFromNode(awaitExpression, this.sourceFile), ...promisePositions]; + return { + value: awaitExpr, + valueOriginalPositions: awaitExprPositions, + stmts: stmts, + }; + } + yieldExpressionToValueAndStmts(yieldExpression) { + let yieldValue = ValueUtil.getUndefinedConst(); + let yieldPositions = [FullPosition.DEFAULT]; + let stmts = []; + if (yieldExpression.expression) { + ({ value: yieldValue, valueOriginalPositions: yieldPositions, stmts: stmts } = this.tsNodeToValueAndStmts(yieldExpression.expression)); + } + const yieldExpr = new ArkYieldExpr(yieldValue); + const yieldExprPositions = [FullPosition.buildFromNode(yieldExpression, this.sourceFile), ...yieldPositions]; + return { + value: yieldExpr, + valueOriginalPositions: yieldExprPositions, + stmts: stmts, + }; + } + deleteExpressionToValueAndStmts(deleteExpression) { + const { value: exprValue, valueOriginalPositions: exprPositions, stmts: stmts } = this.tsNodeToValueAndStmts(deleteExpression.expression); + const deleteExpr = new ArkDeleteExpr(exprValue); + const deleteExprPositions = [FullPosition.buildFromNode(deleteExpression, this.sourceFile), ...exprPositions]; + return { + value: deleteExpr, + valueOriginalPositions: deleteExprPositions, + stmts: stmts, + }; + } + voidExpressionToValueAndStmts(voidExpression) { + const { value: exprValue, valueOriginalPositions: exprPositions, stmts: stmts } = this.tsNodeToValueAndStmts(voidExpression.expression); + const { stmts: exprStmts } = this.arkIRTransformer.generateAssignStmtForValue(exprValue, exprPositions); + exprStmts.forEach(stmt => stmts.push(stmt)); + return { + value: ValueUtil.getUndefinedConst(), + valueOriginalPositions: [FullPosition.DEFAULT], + stmts: stmts, + }; + } + nonNullExpressionToValueAndStmts(nonNullExpression) { + return this.tsNodeToValueAndStmts(nonNullExpression.expression); + } + parenthesizedExpressionToValueAndStmts(parenthesizedExpression) { + return this.tsNodeToValueAndStmts(parenthesizedExpression.expression); + } + typeOfExpressionToValueAndStmts(typeOfExpression) { + const { value: exprValue, valueOriginalPositions: exprPositions, stmts: exprStmts } = this.tsNodeToValueAndStmts(typeOfExpression.expression); + const typeOfExpr = new ArkTypeOfExpr(exprValue); + const typeOfExprPositions = [FullPosition.buildFromNode(typeOfExpression, this.sourceFile), ...exprPositions]; + return { + value: typeOfExpr, + valueOriginalPositions: typeOfExprPositions, + stmts: exprStmts, + }; + } + asExpressionToValueAndStmts(asExpression) { + const stmts = []; + let { value: exprValue, valueOriginalPositions: exprPositions, stmts: exprStmts } = this.tsNodeToValueAndStmts(asExpression.expression); + exprStmts.forEach(stmt => stmts.push(stmt)); + if (IRUtils.moreThanOneAddress(exprValue)) { + ({ + value: exprValue, + valueOriginalPositions: exprPositions, + stmts: exprStmts, + } = this.arkIRTransformer.generateAssignStmtForValue(exprValue, exprPositions)); + exprStmts.forEach(stmt => stmts.push(stmt)); + } + const castExpr = new ArkCastExpr(exprValue, this.resolveTypeNode(asExpression.type)); + const castExprPositions = [FullPosition.buildFromNode(asExpression, this.sourceFile), ...exprPositions]; + return { + value: castExpr, + valueOriginalPositions: castExprPositions, + stmts: stmts, + }; + } + typeAssertionToValueAndStmts(typeAssertion) { + const { value: exprValue, valueOriginalPositions: exprPositions, stmts: exprStmts } = this.tsNodeToValueAndStmts(typeAssertion.expression); + const castExpr = new ArkCastExpr(exprValue, this.resolveTypeNode(typeAssertion.type)); + const castExprPositions = [FullPosition.buildFromNode(typeAssertion, this.sourceFile), ...exprPositions]; + return { + value: castExpr, + valueOriginalPositions: castExprPositions, + stmts: exprStmts, + }; + } + variableDeclarationListToValueAndStmts(variableDeclarationList) { + const stmts = []; + const isConst = (variableDeclarationList.flags & ts__namespace.NodeFlags.Const) !== 0; + for (const declaration of variableDeclarationList.declarations) { + const { stmts: declaredStmts } = this.variableDeclarationToValueAndStmts(declaration, isConst); + declaredStmts.forEach(s => stmts.push(s)); + } + return { + value: ValueUtil.getUndefinedConst(), + valueOriginalPositions: [FullPosition.DEFAULT], + stmts: stmts, + }; + } + variableDeclarationToValueAndStmts(variableDeclaration, isConst, needRightOp = true) { + const leftOpNode = variableDeclaration.name; + const rightOpNode = variableDeclaration.initializer; + const declarationType = variableDeclaration.type ? this.resolveTypeNode(variableDeclaration.type) : UnknownType.getInstance(); + return this.assignmentToValueAndStmts(leftOpNode, rightOpNode, true, isConst, declarationType, needRightOp); + } + assignmentToValueAndStmts(leftOpNode, rightOpNode, variableDefFlag, isConst, declarationType, needRightOp = true) { + let leftValueAndStmts; + if (ts__namespace.isIdentifier(leftOpNode)) { + leftValueAndStmts = this.identifierToValueAndStmts(leftOpNode, variableDefFlag); + } + else if (ts__namespace.isArrayBindingPattern(leftOpNode) || ts__namespace.isArrayLiteralExpression(leftOpNode)) { + leftValueAndStmts = this.arrayDestructuringToValueAndStmts(leftOpNode, isConst); + } + else if (ts__namespace.isObjectBindingPattern(leftOpNode) || ts__namespace.isObjectLiteralExpression(leftOpNode)) { + leftValueAndStmts = this.objectDestructuringToValueAndStmts(leftOpNode, isConst); + } + else { + leftValueAndStmts = this.tsNodeToValueAndStmts(leftOpNode); + } + const { value: leftValue, valueOriginalPositions: leftPositions, stmts: leftStmts } = leftValueAndStmts; + let stmts = []; + if (needRightOp) { + const { value: rightValue, valueOriginalPositions: rightPositions, stmts: rightStmts, } = this.assignmentRightOpToValueAndStmts(rightOpNode, leftValue); + if (leftValue instanceof Local) { + if (variableDefFlag) { + leftValue.setConstFlag(isConst); + leftValue.setType(declarationType); + } + if (leftValue.getType() instanceof UnknownType && + !(rightValue.getType() instanceof UnknownType) && + !(rightValue.getType() instanceof UndefinedType)) { + leftValue.setType(rightValue.getType()); + } + } + const assignStmt = new ArkAssignStmt(leftValue, rightValue); + assignStmt.setOperandOriginalPositions([...leftPositions, ...rightPositions]); + if (ts__namespace.isArrayBindingPattern(leftOpNode) || + ts__namespace.isArrayLiteralExpression(leftOpNode) || + ts__namespace.isObjectBindingPattern(leftOpNode) || + ts__namespace.isObjectLiteralExpression(leftOpNode)) { + rightStmts.forEach(stmt => stmts.push(stmt)); + stmts.push(assignStmt); + leftStmts.forEach(stmt => stmts.push(stmt)); + } + else { + rightStmts.forEach(stmt => stmts.push(stmt)); + leftStmts.forEach(stmt => stmts.push(stmt)); + stmts.push(assignStmt); + } + } + else { + stmts = leftStmts; + } + return { + value: leftValue, + valueOriginalPositions: leftPositions, + stmts: stmts, + }; + } + assignmentRightOpToValueAndStmts(rightOpNode, leftValue) { + let rightValue; + let rightPositions; + let tempRightStmts = []; + const rightStmts = []; + if (rightOpNode) { + ({ value: rightValue, valueOriginalPositions: rightPositions, stmts: tempRightStmts } = this.tsNodeToValueAndStmts(rightOpNode)); + tempRightStmts.forEach(stmt => rightStmts.push(stmt)); + } + else { + rightValue = ValueUtil.getUndefinedConst(); + rightPositions = [FullPosition.DEFAULT]; + } + if (IRUtils.moreThanOneAddress(leftValue) && IRUtils.moreThanOneAddress(rightValue)) { + ({ + value: rightValue, + valueOriginalPositions: rightPositions, + stmts: tempRightStmts, + } = this.arkIRTransformer.generateAssignStmtForValue(rightValue, rightPositions)); + tempRightStmts.forEach(stmt => rightStmts.push(stmt)); + } + return { + value: rightValue, + valueOriginalPositions: rightPositions, + stmts: rightStmts, + }; + } + // In assignment patterns, the left operand will be an array literal expression + arrayDestructuringToValueAndStmts(arrayDestructuring, isConst = false) { + const stmts = []; + const arrayTempLocal = this.generateTempLocal(); + const leftOriginalPosition = FullPosition.buildFromNode(arrayDestructuring, this.sourceFile); + const elements = arrayDestructuring.elements; + const isArrayBindingPattern = ts__namespace.isArrayBindingPattern(arrayDestructuring); + let index = 0; + for (const element of elements) { + const arrayRef = new ArkArrayRef(arrayTempLocal, ValueUtil.getOrCreateNumberConst(index)); + const arrayRefPositions = [leftOriginalPosition, leftOriginalPosition, FullPosition.DEFAULT]; + const itemName = element.getText(this.sourceFile); + const targetLocal = isArrayBindingPattern ? this.addNewLocal(itemName) : this.getOrCreateLocal(itemName); + const targetLocalPosition = FullPosition.buildFromNode(element, this.sourceFile); + isArrayBindingPattern && targetLocal.setConstFlag(isConst); + const assignStmt = new ArkAssignStmt(targetLocal, arrayRef); + assignStmt.setOperandOriginalPositions([targetLocalPosition, ...arrayRefPositions]); + stmts.push(assignStmt); + index++; + } + return { + value: arrayTempLocal, + valueOriginalPositions: [leftOriginalPosition], + stmts: stmts, + }; + } + // In assignment patterns, the left operand will be an object literal expression + objectDestructuringToValueAndStmts(objectDestructuring, isConst = false) { + const stmts = []; + const objectTempLocal = this.generateTempLocal(); + const leftOriginalPosition = FullPosition.buildFromNode(objectDestructuring, this.sourceFile); + const isObjectBindingPattern = ts__namespace.isObjectBindingPattern(objectDestructuring); + const elements = isObjectBindingPattern ? objectDestructuring.elements : objectDestructuring.properties; + for (const element of elements) { + let fieldName = ''; + let targetName = ''; + if (ts__namespace.isBindingElement(element)) { + fieldName = element.propertyName ? element.propertyName.getText(this.sourceFile) : element.name.getText(this.sourceFile); + targetName = element.name.getText(this.sourceFile); + } + else if (ts__namespace.isPropertyAssignment(element)) { + fieldName = element.name.getText(this.sourceFile); + targetName = element.initializer.getText(this.sourceFile); + } + else if (ts__namespace.isShorthandPropertyAssignment(element)) { + fieldName = element.name.getText(this.sourceFile); + targetName = fieldName; + } + else { + continue; + } + const fieldSignature = ArkSignatureBuilder.buildFieldSignatureFromFieldName(fieldName); + const fieldRef = new ArkInstanceFieldRef(objectTempLocal, fieldSignature); + const fieldRefPositions = [leftOriginalPosition, leftOriginalPosition]; + const targetLocal = isObjectBindingPattern ? this.addNewLocal(targetName) : this.getOrCreateLocal(targetName); + isObjectBindingPattern && targetLocal.setConstFlag(isConst); + const targetLocalPosition = FullPosition.buildFromNode(element, this.sourceFile); + const assignStmt = new ArkAssignStmt(targetLocal, fieldRef); + assignStmt.setOperandOriginalPositions([targetLocalPosition, ...fieldRefPositions]); + stmts.push(assignStmt); + } + return { + value: objectTempLocal, + valueOriginalPositions: [leftOriginalPosition], + stmts: stmts, + }; + } + binaryExpressionToValueAndStmts(binaryExpression) { + const operatorToken = binaryExpression.operatorToken; + if (operatorToken.kind === ts__namespace.SyntaxKind.FirstAssignment) { + const leftOpNode = binaryExpression.left; + const rightOpNode = binaryExpression.right; + const declarationType = UnknownType.getInstance(); + return this.assignmentToValueAndStmts(leftOpNode, rightOpNode, false, false, declarationType, true); + } + else if (ArkValueTransformer.isCompoundAssignmentOperator(operatorToken.kind)) { + return this.compoundAssignmentToValueAndStmts(binaryExpression); + } + const stmts = []; + const binaryExpressionPosition = FullPosition.buildFromNode(binaryExpression, this.sourceFile); + const { value: opValue1, valueOriginalPositions: opPositions1, stmts: opStmts1 } = this.tsNodeToSingleAddressValueAndStmts(binaryExpression.left); + opStmts1.forEach(stmt => stmts.push(stmt)); + if (operatorToken.kind === ts__namespace.SyntaxKind.InstanceOfKeyword) { + const instanceOfExpr = new ArkInstanceOfExpr(opValue1, new UnclearReferenceType(binaryExpression.right.getText(this.sourceFile))); + const instanceOfExprPositions = [binaryExpressionPosition, ...opPositions1]; + const { value: instanceofRes, valueOriginalPositions: instanceofPos, stmts: instanceofStmt, } = this.arkIRTransformer.generateAssignStmtForValue(instanceOfExpr, instanceOfExprPositions); + instanceofStmt.forEach(stmt => stmts.push(stmt)); + return { + value: instanceofRes, + valueOriginalPositions: instanceofPos, + stmts: stmts, + }; + } + const { value: opValue2, valueOriginalPositions: opPositions2, stmts: opStmts2 } = this.tsNodeToSingleAddressValueAndStmts(binaryExpression.right); + opStmts2.forEach(stmt => stmts.push(stmt)); + let exprValue; + let exprValuePositions = [binaryExpressionPosition]; + if (operatorToken.kind === ts__namespace.SyntaxKind.CommaToken) { + exprValue = opValue2; + } + else { + const operator = ArkIRTransformer.tokenToBinaryOperator(operatorToken.kind); + if (operator) { + if (this.isRelationalOperator(operator)) { + exprValue = new ArkConditionExpr(opValue1, opValue2, operator); + } + else { + exprValue = new ArkNormalBinopExpr(opValue1, opValue2, operator); + } + exprValuePositions.push(...opPositions1, ...opPositions2); + } + else { + exprValue = ValueUtil.getUndefinedConst(); + exprValuePositions.push(binaryExpressionPosition); + } + } + return { + value: exprValue, + valueOriginalPositions: exprValuePositions, + stmts: stmts, + }; + } + compoundAssignmentToValueAndStmts(binaryExpression) { + const stmts = []; + let { value: leftValue, valueOriginalPositions: leftPositions, stmts: leftStmts } = this.tsNodeToValueAndStmts(binaryExpression.left); + leftStmts.forEach(stmt => stmts.push(stmt)); + let { value: rightValue, valueOriginalPositions: rightPositions, stmts: rightStmts } = this.tsNodeToValueAndStmts(binaryExpression.right); + rightStmts.forEach(stmt => stmts.push(stmt)); + if (IRUtils.moreThanOneAddress(leftValue) && IRUtils.moreThanOneAddress(rightValue)) { + const { value: newRightValue, valueOriginalPositions: newRightPositions, stmts: rightStmts, } = this.arkIRTransformer.generateAssignStmtForValue(rightValue, rightPositions); + rightValue = newRightValue; + rightPositions = newRightPositions; + rightStmts.forEach(stmt => stmts.push(stmt)); + } + let leftOpValue; + let leftOpPositions; + const operator = this.compoundAssignmentTokenToBinaryOperator(binaryExpression.operatorToken.kind); + if (operator) { + const exprValue = new ArkNormalBinopExpr(leftValue, rightValue, operator); + const exprValuePosition = FullPosition.buildFromNode(binaryExpression, this.sourceFile); + const assignStmt = new ArkAssignStmt(leftValue, exprValue); + assignStmt.setOperandOriginalPositions([...leftPositions, exprValuePosition, ...leftPositions, ...rightPositions]); + stmts.push(assignStmt); + leftOpValue = leftValue; + leftOpPositions = leftPositions; + } + else { + leftOpValue = ValueUtil.getUndefinedConst(); + leftOpPositions = [leftPositions[0]]; + } + return { + value: leftOpValue, + valueOriginalPositions: leftOpPositions, + stmts: stmts, + }; + } + compoundAssignmentTokenToBinaryOperator(token) { + switch (token) { + case ts__namespace.SyntaxKind.QuestionQuestionEqualsToken: + return exports.NormalBinaryOperator.NullishCoalescing; + case ts__namespace.SyntaxKind.AsteriskAsteriskEqualsToken: + return exports.NormalBinaryOperator.Exponentiation; + case ts__namespace.SyntaxKind.SlashEqualsToken: + return exports.NormalBinaryOperator.Division; + case ts__namespace.SyntaxKind.PlusEqualsToken: + return exports.NormalBinaryOperator.Addition; + case ts__namespace.SyntaxKind.MinusEqualsToken: + return exports.NormalBinaryOperator.Subtraction; + case ts__namespace.SyntaxKind.AsteriskEqualsToken: + return exports.NormalBinaryOperator.Multiplication; + case ts__namespace.SyntaxKind.PercentEqualsToken: + return exports.NormalBinaryOperator.Remainder; + case ts__namespace.SyntaxKind.LessThanLessThanEqualsToken: + return exports.NormalBinaryOperator.LeftShift; + case ts__namespace.SyntaxKind.GreaterThanGreaterThanEqualsToken: + return exports.NormalBinaryOperator.RightShift; + case ts__namespace.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: + return exports.NormalBinaryOperator.UnsignedRightShift; + case ts__namespace.SyntaxKind.AmpersandEqualsToken: + return exports.NormalBinaryOperator.BitwiseAnd; + case ts__namespace.SyntaxKind.BarEqualsToken: + return exports.NormalBinaryOperator.BitwiseOr; + case ts__namespace.SyntaxKind.CaretEqualsToken: + return exports.NormalBinaryOperator.BitwiseXor; + case ts__namespace.SyntaxKind.AmpersandAmpersandEqualsToken: + return exports.NormalBinaryOperator.LogicalAnd; + case ts__namespace.SyntaxKind.BarBarEqualsToken: + return exports.NormalBinaryOperator.LogicalOr; + } + return null; + } + conditionToValueAndStmts(condition) { + const stmts = []; + let { value: conditionValue, valueOriginalPositions: conditionPositions, stmts: conditionStmts } = this.tsNodeToValueAndStmts(condition); + conditionStmts.forEach(stmt => stmts.push(stmt)); + let conditionExpr; + if (conditionValue instanceof AbstractBinopExpr && this.isRelationalOperator(conditionValue.getOperator())) { + const operator = conditionValue.getOperator(); + conditionExpr = new ArkConditionExpr(conditionValue.getOp1(), conditionValue.getOp2(), operator); + } + else { + if (IRUtils.moreThanOneAddress(conditionValue)) { + ({ + value: conditionValue, + valueOriginalPositions: conditionPositions, + stmts: conditionStmts, + } = this.arkIRTransformer.generateAssignStmtForValue(conditionValue, conditionPositions)); + conditionStmts.forEach(stmt => stmts.push(stmt)); + } + conditionExpr = new ArkConditionExpr(conditionValue, ValueUtil.getOrCreateNumberConst(0), exports.RelationalBinaryOperator.InEquality); + conditionPositions = [conditionPositions[0], ...conditionPositions, FullPosition.DEFAULT]; + } + return { + value: conditionExpr, + valueOriginalPositions: conditionPositions, + stmts: stmts, + }; + } + literalNodeToValueAndStmts(literalNode) { + const syntaxKind = literalNode.kind; + let constant = null; + switch (syntaxKind) { + case ts__namespace.SyntaxKind.NumericLiteral: + constant = ValueUtil.getOrCreateNumberConst(parseFloat(literalNode.text)); + break; + case ts__namespace.SyntaxKind.BigIntLiteral: + constant = ValueUtil.createBigIntConst(BigInt(literalNode.text.slice(0, -1))); + break; + case ts__namespace.SyntaxKind.StringLiteral: + constant = ValueUtil.createStringConst(literalNode.text); + break; + case ts__namespace.SyntaxKind.RegularExpressionLiteral: + constant = new Constant(literalNode.text, Builtin.REGEXP_CLASS_TYPE); + break; + case ts__namespace.SyntaxKind.NoSubstitutionTemplateLiteral: + constant = ValueUtil.createStringConst(literalNode.text); + break; + case ts__namespace.SyntaxKind.NullKeyword: + constant = ValueUtil.getNullConstant(); + break; + case ts__namespace.SyntaxKind.UndefinedKeyword: + constant = ValueUtil.getUndefinedConst(); + break; + case ts__namespace.SyntaxKind.TrueKeyword: + constant = ValueUtil.getBooleanConstant(true); + break; + case ts__namespace.SyntaxKind.FalseKeyword: + constant = ValueUtil.getBooleanConstant(false); + break; + default: + logger$s.warn(`ast node's syntaxKind is ${ts__namespace.SyntaxKind[literalNode.kind]}, not literalNode`); + } + if (constant === null) { + return null; + } + return { + value: constant, + valueOriginalPositions: [FullPosition.buildFromNode(literalNode, this.sourceFile)], + stmts: [], + }; + } + getOrCreateLocal(localName, localType = UnknownType.getInstance()) { + let local = this.locals.get(localName); + if (local !== undefined) { + return local; + } + local = this.addNewLocal(localName, localType); + this.addNewGlobal(localName); + return local; + } + generateTempLocal(localType = UnknownType.getInstance()) { + const tempLocalName = TEMP_LOCAL_PREFIX + this.tempLocalNo; + this.tempLocalNo++; + const tempLocal = new Local(tempLocalName, localType); + this.locals.set(tempLocalName, tempLocal); + return tempLocal; + } + isRelationalOperator(operator) { + return (operator === exports.RelationalBinaryOperator.LessThan || + operator === exports.RelationalBinaryOperator.LessThanOrEqual || + operator === exports.RelationalBinaryOperator.GreaterThan || + operator === exports.RelationalBinaryOperator.GreaterThanOrEqual || + operator === exports.RelationalBinaryOperator.Equality || + operator === exports.RelationalBinaryOperator.InEquality || + operator === exports.RelationalBinaryOperator.StrictEquality || + operator === exports.RelationalBinaryOperator.StrictInequality); + } + isLiteralNode(node) { + if (ts__namespace.isStringLiteral(node) || + ts__namespace.isNumericLiteral(node) || + ts__namespace.isBigIntLiteral(node) || + ts__namespace.isRegularExpressionLiteral(node) || + ts__namespace.isNoSubstitutionTemplateLiteral(node) || + node.kind === ts__namespace.SyntaxKind.NullKeyword || + node.kind === ts__namespace.SyntaxKind.TrueKeyword || + node.kind === ts__namespace.SyntaxKind.FalseKeyword || + node.kind === ts__namespace.SyntaxKind.UndefinedKeyword) { + return true; + } + return false; + } + resolveTypeNode(type) { + const kind = type.kind; + switch (kind) { + case ts__namespace.SyntaxKind.BooleanKeyword: + return BooleanType.getInstance(); + case ts__namespace.SyntaxKind.NumberKeyword: + return NumberType.getInstance(); + case ts__namespace.SyntaxKind.StringKeyword: + return StringType.getInstance(); + case ts__namespace.SyntaxKind.UndefinedKeyword: + return UndefinedType.getInstance(); + case ts__namespace.SyntaxKind.AnyKeyword: + return AnyType.getInstance(); + case ts__namespace.SyntaxKind.VoidKeyword: + return VoidType.getInstance(); + case ts__namespace.SyntaxKind.NeverKeyword: + return NeverType.getInstance(); + case ts__namespace.SyntaxKind.BigIntKeyword: + return BigIntType.getInstance(); + case ts__namespace.SyntaxKind.TypeReference: + return this.resolveTypeReferenceNode(type); + case ts__namespace.SyntaxKind.ArrayType: + return new ArrayType(this.resolveTypeNode(type.elementType), 1); + case ts__namespace.SyntaxKind.UnionType: { + const mayTypes = []; + type.types.forEach(t => mayTypes.push(this.resolveTypeNode(t))); + return new UnionType(mayTypes); + } + case ts__namespace.SyntaxKind.IntersectionType: { + const intersectionTypes = []; + type.types.forEach(t => intersectionTypes.push(this.resolveTypeNode(t))); + return new IntersectionType(intersectionTypes); + } + case ts__namespace.SyntaxKind.TupleType: { + const types = []; + type.elements.forEach(element => { + types.push(this.resolveTypeNode(element)); + }); + return new TupleType(types); + } + case ts__namespace.SyntaxKind.NamedTupleMember: + return this.resolveTypeNode(type.type); + case ts__namespace.SyntaxKind.LiteralType: + return ArkValueTransformer.resolveLiteralTypeNode(type, this.sourceFile); + case ts__namespace.SyntaxKind.TemplateLiteralType: + return this.resolveTemplateLiteralTypeNode(type); + case ts__namespace.SyntaxKind.TypeLiteral: + return this.resolveTypeLiteralNode(type); + case ts__namespace.SyntaxKind.FunctionType: + return this.resolveFunctionTypeNode(type); + case ts__namespace.SyntaxKind.ImportType: + return UnknownType.getInstance(); + case ts__namespace.SyntaxKind.TypeQuery: + return this.resolveTypeQueryNode(type); + case ts__namespace.SyntaxKind.ParenthesizedType: + return this.resolveTypeNode(type.type); + case ts__namespace.SyntaxKind.TypeOperator: + return this.resolveTypeOperatorNode(type); + default: + return UnknownType.getInstance(); + } + } + resolveTypeQueryNode(typeQueryNode) { + const genericTypes = []; + if (typeQueryNode.typeArguments) { + for (const typeArgument of typeQueryNode.typeArguments) { + genericTypes.push(this.resolveTypeNode(typeArgument)); + } + } + const exprNameNode = typeQueryNode.exprName; + let opValue; + if (ts__namespace.isQualifiedName(exprNameNode)) { + if (exprNameNode.left.getText(this.sourceFile) === THIS_NAME) { + const fieldName = exprNameNode.right.getText(this.sourceFile); + const fieldSignature = this.declaringMethod.getDeclaringArkClass().getFieldWithName(fieldName)?.getSignature() ?? + ArkSignatureBuilder.buildFieldSignatureFromFieldName(fieldName); + const baseLocal = this.locals.get(THIS_NAME) ?? new Local(THIS_NAME, new ClassType(this.declaringMethod.getDeclaringArkClass().getSignature(), genericTypes)); + opValue = new ArkInstanceFieldRef(baseLocal, fieldSignature); + } + else { + const exprName = exprNameNode.getText(this.sourceFile); + opValue = new Local(exprName, UnknownType.getInstance()); + } + } + else { + const exprName = exprNameNode.escapedText.toString(); + opValue = this.locals.get(exprName) ?? this.globals?.get(exprName) ?? new Local(exprName, UnknownType.getInstance()); + } + return new TypeQueryExpr(opValue, genericTypes); + } + resolveTypeOperatorNode(typeOperatorNode) { + let type = this.resolveTypeNode(typeOperatorNode.type); + switch (typeOperatorNode.operator) { + case ts__namespace.SyntaxKind.ReadonlyKeyword: { + if (type instanceof ArrayType || type instanceof TupleType) { + type.setReadonlyFlag(true); + } + return type; + } + case ts__namespace.SyntaxKind.KeyOfKeyword: { + return new KeyofTypeExpr(type); + } + case ts__namespace.SyntaxKind.UniqueKeyword: { + return UnknownType.getInstance(); + } + default: + return UnknownType.getInstance(); + } + } + static resolveLiteralTypeNode(literalTypeNode, sourceFile) { + const literal = literalTypeNode.literal; + const kind = literal.kind; + switch (kind) { + case ts__namespace.SyntaxKind.NullKeyword: + return NullType.getInstance(); + case ts__namespace.SyntaxKind.TrueKeyword: + return LiteralType.TRUE; + case ts__namespace.SyntaxKind.FalseKeyword: + return LiteralType.FALSE; + case ts__namespace.SyntaxKind.NumericLiteral: + return new LiteralType(parseFloat(literal.text)); + case ts__namespace.SyntaxKind.PrefixUnaryExpression: + return new LiteralType(parseFloat(literal.getText(sourceFile))); + } + return new LiteralType(literal.getText(sourceFile)); + } + resolveTemplateLiteralTypeNode(templateLiteralTypeNode) { + let stringLiterals = ['']; + const headString = templateLiteralTypeNode.head.rawText || ''; + let newStringLiterals = []; + for (const stringLiteral of stringLiterals) { + newStringLiterals.push(stringLiteral + headString); + } + stringLiterals = newStringLiterals; + newStringLiterals = []; + for (const templateSpan of templateLiteralTypeNode.templateSpans) { + const templateType = this.resolveTypeNode(templateSpan.type); + const unfoldTemplateTypes = []; + if (templateType instanceof UnionType) { + unfoldTemplateTypes.push(...templateType.getTypes()); + } + else { + unfoldTemplateTypes.push(templateType); + } + const unfoldTemplateTypeStrs = []; + for (const unfoldTemplateType of unfoldTemplateTypes) { + unfoldTemplateTypeStrs.push(unfoldTemplateType instanceof AliasType ? unfoldTemplateType.getOriginalType().toString() : unfoldTemplateType.toString()); + } + const templateSpanString = templateSpan.literal.rawText || ''; + for (const stringLiteral of stringLiterals) { + for (const unfoldTemplateTypeStr of unfoldTemplateTypeStrs) { + newStringLiterals.push(stringLiteral + unfoldTemplateTypeStr + templateSpanString); + } + } + stringLiterals = newStringLiterals; + newStringLiterals = []; + } + const templateTypes = []; + for (const stringLiteral of stringLiterals) { + templateTypes.push(new LiteralType(stringLiteral)); + } + if (templateTypes.length > 0) { + return new UnionType(templateTypes); + } + return templateTypes[0]; + } + resolveTypeReferenceNode(typeReferenceNode) { + const typeReferenceFullName = typeReferenceNode.typeName.getText(this.sourceFile); + if (typeReferenceFullName === Builtin.OBJECT) { + return Builtin.OBJECT_CLASS_TYPE; + } + const aliasTypeAndStmt = this.aliasTypeMap.get(typeReferenceFullName); + const genericTypes = []; + if (typeReferenceNode.typeArguments) { + for (const typeArgument of typeReferenceNode.typeArguments) { + genericTypes.push(this.resolveTypeNode(typeArgument)); + } + } + if (!aliasTypeAndStmt) { + const typeName = typeReferenceNode.typeName.getText(this.sourceFile); + const local = this.locals.get(typeName); + if (local !== undefined) { + return local.getType(); + } + return new UnclearReferenceType(typeName, genericTypes); + } + else { + if (genericTypes.length > 0) { + const oldAlias = aliasTypeAndStmt[0]; + let alias = new AliasType(oldAlias.getName(), TypeInference.replaceTypeWithReal(oldAlias.getOriginalType(), genericTypes), oldAlias.getSignature(), oldAlias.getGenericTypes()); + alias.setRealGenericTypes(genericTypes); + return alias; + } + return aliasTypeAndStmt[0]; + } + } + resolveTypeLiteralNode(typeLiteralNode) { + const anonymousClass = new ArkClass(); + const declaringClass = this.declaringMethod.getDeclaringArkClass(); + const declaringNamespace = declaringClass.getDeclaringArkNamespace(); + if (declaringNamespace) { + buildNormalArkClassFromArkNamespace(typeLiteralNode, declaringNamespace, anonymousClass, this.sourceFile); + } + else { + buildNormalArkClassFromArkFile(typeLiteralNode, declaringClass.getDeclaringArkFile(), anonymousClass, this.sourceFile); + } + return new ClassType(anonymousClass.getSignature()); + } + resolveFunctionTypeNode(functionTypeNode) { + const anonymousMethod = new ArkMethod(); + const declaringClass = this.declaringMethod.getDeclaringArkClass(); + buildArkMethodFromArkClass(functionTypeNode, declaringClass, anonymousMethod, this.sourceFile); + return new FunctionType(anonymousMethod.getSignature()); + } + static isCompoundAssignmentOperator(operator) { + const compoundAssignmentOperators = [ + ts__namespace.SyntaxKind.PlusEqualsToken, + ts__namespace.SyntaxKind.MinusEqualsToken, + ts__namespace.SyntaxKind.AsteriskAsteriskEqualsToken, + ts__namespace.SyntaxKind.AsteriskEqualsToken, + ts__namespace.SyntaxKind.SlashEqualsToken, + ts__namespace.SyntaxKind.PercentEqualsToken, + ts__namespace.SyntaxKind.AmpersandEqualsToken, + ts__namespace.SyntaxKind.BarEqualsToken, + ts__namespace.SyntaxKind.CaretEqualsToken, + ts__namespace.SyntaxKind.LessThanLessThanEqualsToken, + ts__namespace.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken, + ts__namespace.SyntaxKind.GreaterThanGreaterThanEqualsToken, + ts__namespace.SyntaxKind.BarBarEqualsToken, + ts__namespace.SyntaxKind.AmpersandAmpersandEqualsToken, + ts__namespace.SyntaxKind.QuestionQuestionEqualsToken, + ]; + return compoundAssignmentOperators.includes(operator); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @category core/model + */ +class ImportInfo extends ArkBaseModel { + importClauseName = ''; + importType = ''; + importFrom; + nameBeforeAs; + declaringArkFile; + originTsPosition; + tsSourceCode; + lazyExportInfo; + constructor() { + super(); + } + /** + * Returns the program language of the file where this import info defined. + */ + getLanguage() { + return this.getDeclaringArkFile().getLanguage(); + } + build(importClauseName, importType, importFrom, originTsPosition, modifiers, nameBeforeAs) { + this.setImportClauseName(importClauseName); + this.setImportType(importType); + this.setImportFrom(importFrom); + this.setOriginTsPosition(originTsPosition); + this.addModifier(modifiers); + this.setNameBeforeAs(nameBeforeAs); + } + getOriginName() { + return this.nameBeforeAs ?? this.importClauseName; + } + /** + * Returns the export information, i.e., the actual reference generated at the time of call. + * The export information includes: clause's name, clause's type, modifiers, location + * where it is exported from, etc. If the export information could not be found, **null** will be returned. + * @returns The export information. If there is no export information, the return will be a **null**. + */ + getLazyExportInfo() { + if (this.lazyExportInfo === undefined && this.declaringArkFile.getScene().getStage() >= 2) { + this.lazyExportInfo = findExportInfo(this); + } + return this.lazyExportInfo || null; + } + setDeclaringArkFile(declaringArkFile) { + this.declaringArkFile = declaringArkFile; + } + getDeclaringArkFile() { + return this.declaringArkFile; + } + getImportClauseName() { + return this.importClauseName; + } + setImportClauseName(importClauseName) { + this.importClauseName = importClauseName; + } + getImportType() { + return this.importType; + } + setImportType(importType) { + this.importType = importType; + } + setImportFrom(importFrom) { + this.importFrom = importFrom; + } + getNameBeforeAs() { + return this.nameBeforeAs; + } + setNameBeforeAs(nameBeforeAs) { + this.nameBeforeAs = nameBeforeAs; + } + setOriginTsPosition(originTsPosition) { + this.originTsPosition = originTsPosition; + } + getOriginTsPosition() { + return this.originTsPosition ?? LineColPosition.DEFAULT; + } + setTsSourceCode(tsSourceCode) { + this.tsSourceCode = tsSourceCode; + } + getTsSourceCode() { + return this.tsSourceCode ?? ''; + } + getFrom() { + return this.importFrom; + } + isDefault() { + if (this.nameBeforeAs === 'default') { + return true; + } + return this.importType === 'Identifier'; + } + validate() { + return this.validateFields(['declaringArkFile']); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class DummyStmt extends Stmt { + constructor(text) { + super(); + this.text = text; + } + toString() { + return this.text; + } +} +class ArkIRTransformer { + static DUMMY_LOOP_INITIALIZER_STMT = 'LoopInitializer'; + static DUMMY_CONDITIONAL_OPERATOR = 'ConditionalOperator'; + static DUMMY_CONDITIONAL_OPERATOR_IF_TRUE_STMT = ArkIRTransformer.DUMMY_CONDITIONAL_OPERATOR + 'IfTrue'; + static DUMMY_CONDITIONAL_OPERATOR_IF_FALSE_STMT = ArkIRTransformer.DUMMY_CONDITIONAL_OPERATOR + 'IfFalse'; + static DUMMY_CONDITIONAL_OPERATOR_END_STMT = ArkIRTransformer.DUMMY_CONDITIONAL_OPERATOR + 'End'; + sourceFile; + declaringMethod; + inBuilderMethod = false; + builderMethodContextFlag = false; + stmtsHaveOriginalText = new Set(); + arkValueTransformer; + constructor(sourceFile, declaringMethod) { + this.sourceFile = sourceFile; + this.declaringMethod = declaringMethod; + this.inBuilderMethod = ModelUtils.isArkUIBuilderMethod(declaringMethod); + this.arkValueTransformer = new ArkValueTransformer(this, sourceFile, this.declaringMethod); + } + getLocals() { + return this.arkValueTransformer.getLocals(); + } + getGlobals() { + return this.arkValueTransformer.getGlobals(); + } + getThisLocal() { + return this.arkValueTransformer.getThisLocal(); + } + getAliasTypeMap() { + return this.arkValueTransformer.getAliasTypeMap(); + } + prebuildStmts() { + const stmts = []; + let index = 0; + for (const methodParameter of this.declaringMethod.getParameters()) { + const parameterRef = new ArkParameterRef(index, methodParameter.getType()); + stmts.push(new ArkAssignStmt(this.arkValueTransformer.addNewLocal(methodParameter.getName(), parameterRef.getType()), parameterRef)); + index++; + } + const thisRef = new ArkThisRef(this.arkValueTransformer.getThisLocal().getType()); + stmts.push(new ArkAssignStmt(this.arkValueTransformer.getThisLocal(), thisRef)); + return stmts; + } + tsNodeToStmts(node) { + let stmts = []; + if (ts__namespace.isExpressionStatement(node)) { + stmts = this.expressionStatementToStmts(node); + } + else if (ts__namespace.isTypeAliasDeclaration(node)) { + stmts = this.typeAliasDeclarationToStmts(node); + } + else if (ts__namespace.isBlock(node)) { + stmts = this.blockToStmts(node); + } + else if (ts__namespace.isForStatement(node)) { + stmts = this.forStatementToStmts(node); + } + else if (ts__namespace.isForInStatement(node) || ts__namespace.isForOfStatement(node)) { + stmts = this.rangeForStatementToStmts(node); + } + else if (ts__namespace.isWhileStatement(node)) { + stmts = this.whileStatementToStmts(node); + } + else if (ts__namespace.isDoStatement(node)) { + stmts = this.doStatementToStmts(node); + } + else if (ts__namespace.isVariableStatement(node)) { + stmts = this.variableStatementToStmts(node); + } + else if (ts__namespace.isVariableDeclarationList(node)) { + stmts = this.variableDeclarationListToStmts(node); + } + else if (ts__namespace.isIfStatement(node)) { + stmts = this.ifStatementToStmts(node); + } + else if (ts__namespace.isBreakStatement(node) || ts__namespace.isContinueStatement(node)) { + stmts = this.gotoStatementToStmts(node); + } + else if (ts__namespace.isThrowStatement(node)) { + stmts = this.throwStatementToStmts(node); + } + else if (ts__namespace.isCatchClause(node)) { + stmts = this.catchClauseToStmts(node); + } + else if (ts__namespace.isReturnStatement(node)) { + stmts = this.returnStatementToStmts(node); + } + else if (ts__namespace.isFunctionDeclaration(node)) { + stmts = this.functionDeclarationToStmts(node); + } + else if (ts__namespace.isExportAssignment(node)) { + stmts = this.expressionInExportToStmts(node.expression); + } + else if (ts__namespace.isClassDeclaration(node)) { + stmts = this.classDeclarationToStmts(node); + } + this.mapStmtsToTsStmt(stmts, node); + if (stmts.length > 0) { + IRUtils.setComments(stmts[0], node, this.sourceFile, this.declaringMethod.getDeclaringArkFile().getScene().getOptions()); + } + return stmts; + } + tsNodeToValueAndStmts(node) { + return this.arkValueTransformer.tsNodeToValueAndStmts(node); + } + functionDeclarationToStmts(functionDeclarationNode) { + const declaringClass = this.declaringMethod.getDeclaringArkClass(); + const arkMethod = new ArkMethod(); + if (this.builderMethodContextFlag) { + ModelUtils.implicitArkUIBuilderMethods.add(arkMethod); + } + buildArkMethodFromArkClass(functionDeclarationNode, declaringClass, arkMethod, this.sourceFile, this.declaringMethod); + return []; + } + classDeclarationToStmts(node) { + const cls = new ArkClass(); + const declaringArkNamespace = this.declaringMethod.getDeclaringArkClass().getDeclaringArkNamespace(); + if (declaringArkNamespace) { + cls.setDeclaringArkNamespace(declaringArkNamespace); + } + cls.setDeclaringArkFile(this.declaringMethod.getDeclaringArkFile()); + buildNormalArkClassFromArkMethod(node, cls, this.sourceFile, this.declaringMethod); + return []; + } + returnStatementToStmts(returnStatement) { + const stmts = []; + if (returnStatement.expression) { + let { value: exprValue, valueOriginalPositions: exprPositions, stmts: exprStmts } = this.tsNodeToValueAndStmts(returnStatement.expression); + exprStmts.forEach(stmt => stmts.push(stmt)); + if (IRUtils.moreThanOneAddress(exprValue)) { + ({ value: exprValue, valueOriginalPositions: exprPositions, stmts: exprStmts } = this.generateAssignStmtForValue(exprValue, exprPositions)); + exprStmts.forEach(stmt => stmts.push(stmt)); + } + const returnStmt = new ArkReturnStmt(exprValue); + returnStmt.setOperandOriginalPositions(exprPositions); + stmts.push(returnStmt); + if (this.declaringMethod.getSubSignature().getReturnType() instanceof UnknownType) { + this.declaringMethod.getSubSignature().setReturnType(exprValue.getType()); + } + return stmts; + } + stmts.push(new ArkReturnVoidStmt()); + if (this.declaringMethod.getSubSignature().getReturnType() instanceof UnknownType) { + if (this.declaringMethod.containsModifier(ModifierType.ASYNC)) { + const promise = this.declaringMethod.getDeclaringArkFile().getScene().getSdkGlobal(PROMISE); + if (promise instanceof ArkClass) { + this.declaringMethod.getSubSignature().setReturnType(new ClassType(promise.getSignature())); + } + else { + this.declaringMethod.getSubSignature().setReturnType(new UnclearReferenceType(PROMISE, [VoidType.getInstance()])); + } + } + else { + this.declaringMethod.getSubSignature().setReturnType(VoidType.getInstance()); + } + } + return stmts; + } + blockToStmts(block) { + const stmts = []; + for (const statement of block.statements) { + this.tsNodeToStmts(statement).forEach(stmt => stmts.push(stmt)); + } + return stmts; + } + expressionStatementToStmts(expressionStatement) { + const exprNode = expressionStatement.expression; + const { value: exprValue, valueOriginalPositions: exprPositions, stmts: stmts } = this.tsNodeToValueAndStmts(exprNode); + if (exprValue instanceof AbstractInvokeExpr) { + this.addInvokeStmts(exprValue, exprPositions, stmts); + } + else if (this.shouldGenerateExtraAssignStmt(exprNode)) { + const { stmts: exprStmts } = this.generateAssignStmtForValue(exprValue, exprPositions); + exprStmts.forEach(stmt => stmts.push(stmt)); + } + return stmts; + } + addInvokeStmts(invokeExpr, exprPositions, stmts) { + const invokeStmt = new ArkInvokeStmt(invokeExpr); + invokeStmt.setOperandOriginalPositions(exprPositions); + stmts.push(invokeStmt); + let hasRepeat = false; + for (const stmt of stmts) { + if (stmt instanceof ArkAssignStmt && stmt.getRightOp() instanceof ArkStaticInvokeExpr) { + const rightOp = stmt.getRightOp(); + if (rightOp.getMethodSignature().getMethodSubSignature().getMethodName() === COMPONENT_REPEAT) { + const createMethodSignature = ArkSignatureBuilder.buildMethodSignatureFromClassNameAndMethodName(COMPONENT_REPEAT, COMPONENT_CREATE_FUNCTION); + const createInvokeExpr = new ArkStaticInvokeExpr(createMethodSignature, rightOp.getArgs()); + stmt.setRightOp(createInvokeExpr); + hasRepeat = true; + } + } + } + if (hasRepeat) { + const popMethodSignature = ArkSignatureBuilder.buildMethodSignatureFromClassNameAndMethodName(COMPONENT_REPEAT, COMPONENT_POP_FUNCTION); + const popInvokeExpr = new ArkStaticInvokeExpr(popMethodSignature, []); + const popInvokeStmt = new ArkInvokeStmt(popInvokeExpr); + stmts.push(popInvokeStmt); + } + } + shouldGenerateExtraAssignStmt(expression) { + if (ts__namespace.isParenthesizedExpression(expression)) { + return this.shouldGenerateExtraAssignStmt(expression.expression); + } + if ((ts__namespace.isBinaryExpression(expression) && + (expression.operatorToken.kind === ts__namespace.SyntaxKind.FirstAssignment || + ArkValueTransformer.isCompoundAssignmentOperator(expression.operatorToken.kind))) || + ts__namespace.isEtsComponentExpression(expression) || + ts__namespace.isVoidExpression(expression) || + ts__namespace.isNewExpression(expression) || + ts__namespace.isCallExpression(expression) || + (ts__namespace.isPrefixUnaryExpression(expression) && + (expression.operator === ts__namespace.SyntaxKind.PlusPlusToken || expression.operator === ts__namespace.SyntaxKind.MinusMinusToken)) || + (ts__namespace.isPostfixUnaryExpression(expression) && + (expression.operator === ts__namespace.SyntaxKind.PlusPlusToken || expression.operator === ts__namespace.SyntaxKind.MinusMinusToken))) { + return false; + } + return true; + } + typeAliasDeclarationToStmts(typeAliasDeclaration) { + const aliasName = typeAliasDeclaration.name.text; + const rightOp = typeAliasDeclaration.type; + let rightType = this.arkValueTransformer.resolveTypeNode(rightOp); + if (rightType instanceof AbstractTypeExpr) { + rightType = rightType.getType(); + } + const aliasType = new AliasType(aliasName, rightType, new AliasTypeSignature(aliasName, this.declaringMethod.getSignature())); + if (typeAliasDeclaration.typeParameters) { + const genericTypes = buildTypeParameters(typeAliasDeclaration.typeParameters, this.sourceFile, this.declaringMethod); + aliasType.setGenericTypes(genericTypes); + aliasType.setOriginalType(buildGenericType(rightType, aliasType)); + rightType = aliasType.getOriginalType(); + } + let expr = this.generateAliasTypeExpr(rightOp, aliasType); + if ((ts__namespace.isTypeQueryNode(rightOp) || ts__namespace.isTypeReferenceNode(rightOp)) && rightOp.typeArguments) { + let realGenericTypes = []; + rightOp.typeArguments.forEach(typeArgument => { + realGenericTypes.push(this.arkValueTransformer.resolveTypeNode(typeArgument)); + }); + expr.setRealGenericTypes(realGenericTypes); + } + const modifiers = typeAliasDeclaration.modifiers ? buildModifiers(typeAliasDeclaration) : 0; + aliasType.setModifiers(modifiers); + const aliasTypeDefineStmt = new ArkAliasTypeDefineStmt(aliasType, expr); + const leftPosition = FullPosition.buildFromNode(typeAliasDeclaration.name, this.sourceFile); + const rightPosition = FullPosition.buildFromNode(rightOp, this.sourceFile); + const operandOriginalPositions = [leftPosition, rightPosition]; + aliasTypeDefineStmt.setOperandOriginalPositions(operandOriginalPositions); + this.getAliasTypeMap().set(aliasName, [aliasType, aliasTypeDefineStmt]); + return [aliasTypeDefineStmt]; + } + generateAliasTypeExpr(rightOp, aliasType) { + let rightType = aliasType.getOriginalType(); + let expr; + if (ts__namespace.isImportTypeNode(rightOp)) { + expr = this.resolveImportTypeNode(rightOp); + } + else if (ts__namespace.isTypeQueryNode(rightOp)) { + const localName = rightOp.exprName.getText(this.sourceFile); + const originalLocal = Array.from(this.arkValueTransformer.getLocals()).find(local => local.getName() === localName); + if (originalLocal === undefined || rightType instanceof UnclearReferenceType) { + expr = new AliasTypeExpr(new Local(localName, rightType), true); + } + else { + expr = new AliasTypeExpr(originalLocal, true); + } + } + else if (ts__namespace.isTypeReferenceNode(rightOp)) { + // For type A = B stmt and B is also an alias type with the same scope of A, + // rightType here is AliasType with real generic type number. + // The originalObject in expr should be the object without real generic type, so try to find it in this scope. + if (rightType instanceof AliasType) { + const existAliasType = this.getAliasTypeMap().get(rightType.getName()); + if (existAliasType) { + expr = new AliasTypeExpr(existAliasType[0], false); + } + else { + expr = new AliasTypeExpr(rightType, false); + } + } + else { + expr = new AliasTypeExpr(rightType, false); + } + } + else { + expr = new AliasTypeExpr(rightType, false); + // 对于type A = {x:1, y:2}语句,当前阶段即可精确获取ClassType类型,需找到对应的ArkClass作为originalObject + // 对于其他情况此处为UnclearReferenceTye并由类型推导进行查找和处理 + if (rightType instanceof ClassType) { + const classObject = ModelUtils.getClassWithName(rightType.getClassSignature().getClassName(), this.declaringMethod.getDeclaringArkClass()); + if (classObject) { + expr.setOriginalObject(classObject); + } + } + } + return expr; + } + resolveImportTypeNode(importTypeNode) { + const importType = 'typeAliasDefine'; + let importFrom = ''; + let importClauseName = ''; + if (ts__namespace.isLiteralTypeNode(importTypeNode.argument)) { + if (ts__namespace.isStringLiteral(importTypeNode.argument.literal)) { + importFrom = importTypeNode.argument.literal.text; + } + } + const importQualifier = importTypeNode.qualifier; + if (importQualifier !== undefined) { + importClauseName = importQualifier.getText(this.sourceFile); + } + let importInfo = new ImportInfo(); + importInfo.build(importClauseName, importType, importFrom, LineColPosition.buildFromNode(importTypeNode, this.sourceFile), 0); + importInfo.setDeclaringArkFile(this.declaringMethod.getDeclaringArkFile()); + return new AliasTypeExpr(importInfo, importTypeNode.isTypeOf); + } + switchStatementToValueAndStmts(switchStatement) { + const valueAndStmtsOfSwitchAndCases = []; + const exprStmts = []; + let { value: exprValue, valueOriginalPositions: exprPositions, stmts: exprTempStmts } = this.tsNodeToValueAndStmts(switchStatement.expression); + exprTempStmts.forEach(stmt => exprStmts.push(stmt)); + if (IRUtils.moreThanOneAddress(exprValue)) { + ({ value: exprValue, valueOriginalPositions: exprPositions, stmts: exprTempStmts } = this.generateAssignStmtForValue(exprValue, exprPositions)); + exprTempStmts.forEach(stmt => exprStmts.push(stmt)); + } + valueAndStmtsOfSwitchAndCases.push({ + value: exprValue, + valueOriginalPositions: exprPositions, + stmts: exprStmts, + }); + for (const clause of switchStatement.caseBlock.clauses) { + if (ts__namespace.isCaseClause(clause)) { + const clauseStmts = []; + let { value: clauseValue, valueOriginalPositions: clausePositions, stmts: clauseTempStmts } = this.tsNodeToValueAndStmts(clause.expression); + clauseTempStmts.forEach(stmt => clauseStmts.push(stmt)); + if (IRUtils.moreThanOneAddress(clauseValue)) { + ({ + value: clauseValue, + valueOriginalPositions: clausePositions, + stmts: clauseTempStmts, + } = this.generateAssignStmtForValue(clauseValue, clausePositions)); + clauseTempStmts.forEach(stmt => clauseStmts.push(stmt)); + } + valueAndStmtsOfSwitchAndCases.push({ + value: clauseValue, + valueOriginalPositions: clausePositions, + stmts: clauseStmts, + }); + } + } + return valueAndStmtsOfSwitchAndCases; + } + forStatementToStmts(forStatement) { + const stmts = []; + if (forStatement.initializer) { + this.tsNodeToValueAndStmts(forStatement.initializer).stmts.forEach(stmt => stmts.push(stmt)); + } + const dummyInitializerStmt = new DummyStmt(ArkIRTransformer.DUMMY_LOOP_INITIALIZER_STMT); + stmts.push(dummyInitializerStmt); + if (forStatement.condition) { + const { value: conditionValue, stmts: conditionStmts } = this.arkValueTransformer.conditionToValueAndStmts(forStatement.condition); + conditionStmts.forEach(stmt => stmts.push(stmt)); + stmts.push(new ArkIfStmt(conditionValue)); + } + else { + // The omitted condition always evaluates to true. + const trueConstant = ValueUtil.getBooleanConstant(true); + const conditionExpr = new ArkConditionExpr(trueConstant, trueConstant, exports.RelationalBinaryOperator.Equality); + stmts.push(new ArkIfStmt(conditionExpr)); + } + if (forStatement.incrementor) { + this.tsNodeToValueAndStmts(forStatement.incrementor).stmts.forEach(stmt => stmts.push(stmt)); + } + return stmts; + } + rangeForStatementToStmts(forOfStatement) { + const stmts = []; + let { value: iterableValue, valueOriginalPositions: iterablePositions, stmts: iterableStmts } = this.tsNodeToValueAndStmts(forOfStatement.expression); + iterableStmts.forEach(stmt => stmts.push(stmt)); + if (!(iterableValue instanceof Local)) { + ({ + value: iterableValue, + valueOriginalPositions: iterablePositions, + stmts: iterableStmts, + } = this.generateAssignStmtForValue(iterableValue, iterablePositions)); + iterableStmts.forEach(stmt => stmts.push(stmt)); + } + const iteratorMethodSubSignature = new MethodSubSignature(Builtin.ITERATOR_FUNCTION, [], Builtin.ITERATOR_CLASS_TYPE); + const iteratorMethodSignature = new MethodSignature(ClassSignature.DEFAULT, iteratorMethodSubSignature); + const iteratorInvokeExpr = new ArkInstanceInvokeExpr(iterableValue, iteratorMethodSignature, []); + const iteratorInvokeExprPositions = [iterablePositions[0], ...iterablePositions]; + const { value: iterator, valueOriginalPositions: iteratorPositions, stmts: iteratorStmts, } = this.generateAssignStmtForValue(iteratorInvokeExpr, iteratorInvokeExprPositions); + iteratorStmts.forEach(stmt => stmts.push(stmt)); + iterator.setType(Builtin.ITERATOR_CLASS_TYPE); + const nextMethodSubSignature = new MethodSubSignature(Builtin.ITERATOR_NEXT, [], Builtin.ITERATOR_RESULT_CLASS_TYPE); + const nextMethodSignature = new MethodSignature(ClassSignature.DEFAULT, nextMethodSubSignature); + const iteratorNextInvokeExpr = new ArkInstanceInvokeExpr(iterator, nextMethodSignature, []); + const iteratorNextInvokeExprPositions = [iteratorPositions[0], ...iteratorPositions]; + const { value: iteratorResult, valueOriginalPositions: iteratorResultPositions, stmts: iteratorResultStmts, } = this.generateAssignStmtForValue(iteratorNextInvokeExpr, iteratorNextInvokeExprPositions); + iteratorResultStmts.forEach(stmt => stmts.push(stmt)); + iteratorResult.setType(Builtin.ITERATOR_RESULT_CLASS_TYPE); + const doneFieldSignature = new FieldSignature(Builtin.ITERATOR_RESULT_DONE, Builtin.ITERATOR_RESULT_CLASS_SIGNATURE, BooleanType.getInstance(), false); + const doneFieldRef = new ArkInstanceFieldRef(iteratorResult, doneFieldSignature); + const doneFieldRefPositions = [iteratorResultPositions[0], ...iteratorResultPositions]; + const { value: doneFlag, valueOriginalPositions: doneFlagPositions, stmts: doneFlagStmts, } = this.generateAssignStmtForValue(doneFieldRef, doneFieldRefPositions); + doneFlagStmts.forEach(stmt => stmts.push(stmt)); + doneFlag.setType(BooleanType.getInstance()); + const conditionExpr = new ArkConditionExpr(doneFlag, ValueUtil.getBooleanConstant(true), exports.RelationalBinaryOperator.Equality); + const conditionExprPositions = [doneFlagPositions[0], ...doneFlagPositions, FullPosition.DEFAULT]; + const ifStmt = new ArkIfStmt(conditionExpr); + ifStmt.setOperandOriginalPositions(conditionExprPositions); + stmts.push(ifStmt); + const valueFieldSignature = new FieldSignature(Builtin.ITERATOR_RESULT_VALUE, Builtin.ITERATOR_RESULT_CLASS_SIGNATURE, UnknownType.getInstance(), false); + const valueFieldRef = new ArkInstanceFieldRef(iteratorResult, valueFieldSignature); + const valueFieldRefPositions = [iteratorResultPositions[0], ...iteratorResultPositions]; + const { value: yieldValue, valueOriginalPositions: yieldValuePositions, stmts: yieldValueStmts, } = this.generateAssignStmtForValue(valueFieldRef, valueFieldRefPositions); + yieldValueStmts.forEach(stmt => stmts.push(stmt)); + const castExpr = new ArkCastExpr(yieldValue, UnknownType.getInstance()); + const castExprPositions = [yieldValuePositions[0], ...yieldValuePositions]; + const initializerNode = forOfStatement.initializer; + if (ts__namespace.isVariableDeclarationList(initializerNode)) { + const isConst = (initializerNode.flags & ts__namespace.NodeFlags.Const) !== 0; + const { value: initValue, valueOriginalPositions: initOriPos, stmts: initStmts, } = this.arkValueTransformer.variableDeclarationToValueAndStmts(initializerNode.declarations[0], isConst, false); + const assignStmt = new ArkAssignStmt(initValue, castExpr); + assignStmt.setOperandOriginalPositions([...initOriPos, ...castExprPositions]); + stmts.push(assignStmt); + initStmts.forEach(stmt => stmts.push(stmt)); + } + else { + // initializer maybe an expression + const { value: initValue, valueOriginalPositions: initOriPos, stmts: initStmts } = this.tsNodeToValueAndStmts(initializerNode); + const assignStmt = new ArkAssignStmt(initValue, castExpr); + assignStmt.setOperandOriginalPositions([...initOriPos, ...castExprPositions]); + initStmts.forEach(stmt => stmts.push(stmt)); + stmts.push(assignStmt); + } + return stmts; + } + whileStatementToStmts(whileStatement) { + const stmts = []; + const dummyInitializerStmt = new DummyStmt(ArkIRTransformer.DUMMY_LOOP_INITIALIZER_STMT); + stmts.push(dummyInitializerStmt); + const { value: conditionExpr, stmts: conditionStmts } = this.arkValueTransformer.conditionToValueAndStmts(whileStatement.expression); + conditionStmts.forEach(stmt => stmts.push(stmt)); + stmts.push(new ArkIfStmt(conditionExpr)); + return stmts; + } + doStatementToStmts(doStatement) { + const stmts = []; + const { value: conditionExpr, stmts: conditionStmts } = this.arkValueTransformer.conditionToValueAndStmts(doStatement.expression); + conditionStmts.forEach(stmt => stmts.push(stmt)); + stmts.push(new ArkIfStmt(conditionExpr)); + return stmts; + } + variableStatementToStmts(variableStatement) { + return this.variableDeclarationListToStmts(variableStatement.declarationList); + } + variableDeclarationListToStmts(variableDeclarationList) { + return this.arkValueTransformer.variableDeclarationListToValueAndStmts(variableDeclarationList).stmts; + } + ifStatementToStmts(ifStatement) { + const stmts = []; + if (this.inBuilderMethod) { + const { value: conditionExpr, valueOriginalPositions: conditionExprPositions, stmts: conditionStmts, } = this.arkValueTransformer.conditionToValueAndStmts(ifStatement.expression); + conditionStmts.forEach(stmt => stmts.push(stmt)); + const createMethodSignature = ArkSignatureBuilder.buildMethodSignatureFromClassNameAndMethodName(COMPONENT_IF, COMPONENT_CREATE_FUNCTION); + const { value: conditionLocal, valueOriginalPositions: conditionLocalPositions, stmts: assignConditionStmts, } = this.generateAssignStmtForValue(conditionExpr, conditionExprPositions); + assignConditionStmts.forEach(stmt => stmts.push(stmt)); + const createInvokeExpr = new ArkStaticInvokeExpr(createMethodSignature, [conditionLocal]); + const createInvokeExprPositions = [conditionLocalPositions[0], ...conditionLocalPositions]; + const { stmts: createStmts } = this.generateAssignStmtForValue(createInvokeExpr, createInvokeExprPositions); + createStmts.forEach(stmt => stmts.push(stmt)); + const branchMethodSignature = ArkSignatureBuilder.buildMethodSignatureFromClassNameAndMethodName(COMPONENT_IF, COMPONENT_BRANCH_FUNCTION); + const branchInvokeExpr = new ArkStaticInvokeExpr(branchMethodSignature, [ValueUtil.getOrCreateNumberConst(0)]); + const branchInvokeExprPositions = [conditionLocalPositions[0], FullPosition.DEFAULT]; + const branchInvokeStmt = new ArkInvokeStmt(branchInvokeExpr); + branchInvokeStmt.setOperandOriginalPositions(branchInvokeExprPositions); + stmts.push(branchInvokeStmt); + this.tsNodeToStmts(ifStatement.thenStatement).forEach(stmt => stmts.push(stmt)); + if (ifStatement.elseStatement) { + const branchElseMethodSignature = ArkSignatureBuilder.buildMethodSignatureFromClassNameAndMethodName(COMPONENT_IF, COMPONENT_BRANCH_FUNCTION); + const branchElseInvokeExpr = new ArkStaticInvokeExpr(branchElseMethodSignature, [ValueUtil.getOrCreateNumberConst(1)]); + const branchElseInvokeExprPositions = [FullPosition.buildFromNode(ifStatement.elseStatement, this.sourceFile), FullPosition.DEFAULT]; + const branchElseInvokeStmt = new ArkInvokeStmt(branchElseInvokeExpr); + branchElseInvokeStmt.setOperandOriginalPositions(branchElseInvokeExprPositions); + stmts.push(branchElseInvokeStmt); + this.tsNodeToStmts(ifStatement.elseStatement).forEach(stmt => stmts.push(stmt)); + } + const popMethodSignature = ArkSignatureBuilder.buildMethodSignatureFromClassNameAndMethodName(COMPONENT_IF, COMPONENT_POP_FUNCTION); + const popInvokeExpr = new ArkStaticInvokeExpr(popMethodSignature, []); + const popInvokeStmt = new ArkInvokeStmt(popInvokeExpr); + stmts.push(popInvokeStmt); + } + else { + const { value: conditionExpr, valueOriginalPositions: conditionExprPositions, stmts: conditionStmts, } = this.arkValueTransformer.conditionToValueAndStmts(ifStatement.expression); + conditionStmts.forEach(stmt => stmts.push(stmt)); + const ifStmt = new ArkIfStmt(conditionExpr); + ifStmt.setOperandOriginalPositions(conditionExprPositions); + stmts.push(ifStmt); + } + return stmts; + } + gotoStatementToStmts(gotoStatement) { + return []; + } + throwStatementToStmts(throwStatement) { + const stmts = []; + const { value: throwValue, valueOriginalPositions: throwValuePositions, stmts: throwStmts } = this.tsNodeToValueAndStmts(throwStatement.expression); + throwStmts.forEach(stmt => stmts.push(stmt)); + const throwStmt = new ArkThrowStmt(throwValue); + throwStmt.setOperandOriginalPositions(throwValuePositions); + stmts.push(throwStmt); + return stmts; + } + catchClauseToStmts(catchClause) { + const stmts = []; + if (catchClause.variableDeclaration) { + const { value: catchValue, valueOriginalPositions: catchOriPos, stmts: catchStmts, } = this.arkValueTransformer.variableDeclarationToValueAndStmts(catchClause.variableDeclaration, false, false); + const caughtExceptionRef = new ArkCaughtExceptionRef(UnknownType.getInstance()); + const assignStmt = new ArkAssignStmt(catchValue, caughtExceptionRef); + assignStmt.setOperandOriginalPositions(catchOriPos); + stmts.push(assignStmt); + catchStmts.forEach(stmt => stmts.push(stmt)); + } + return stmts; + } + expressionInExportToStmts(expression) { + if (ts__namespace.isNewExpression(expression) || ts__namespace.isObjectLiteralExpression(expression)) { + return this.newClassInExportToStmts(expression); + } + return []; + } + newClassInExportToStmts(expression) { + let stmts = []; + let { value: rightValue, valueOriginalPositions: rightPositions, stmts: rightStmts } = this.tsNodeToValueAndStmts(expression); + rightStmts.forEach(stmt => stmts.push(stmt)); + let leftValue = this.arkValueTransformer.addNewLocal(DEFAULT); + let leftPositions = rightPositions; + const assignStmt = new ArkAssignStmt(leftValue, rightValue); + assignStmt.setOperandOriginalPositions([...leftPositions, ...rightPositions]); + stmts.push(assignStmt); + return stmts; + } + mapStmtsToTsStmt(stmts, node) { + for (const stmt of stmts) { + if (!this.stmtsHaveOriginalText.has(stmt)) { + this.stmtsHaveOriginalText.add(stmt); + stmt.setOriginPositionInfo(LineColPosition.buildFromNode(node, this.sourceFile)); + stmt.setOriginalText(node.getText(this.sourceFile)); + } + } + } + static tokenToUnaryOperator(token) { + switch (token) { + case ts__namespace.SyntaxKind.MinusToken: + return exports.UnaryOperator.Neg; + case ts__namespace.SyntaxKind.TildeToken: + return exports.UnaryOperator.BitwiseNot; + case ts__namespace.SyntaxKind.ExclamationToken: + return exports.UnaryOperator.LogicalNot; + } + return null; + } + static tokenToBinaryOperator(token) { + switch (token) { + case ts__namespace.SyntaxKind.QuestionQuestionToken: + return exports.NormalBinaryOperator.NullishCoalescing; + case ts__namespace.SyntaxKind.AsteriskAsteriskToken: + return exports.NormalBinaryOperator.Exponentiation; + case ts__namespace.SyntaxKind.SlashToken: + return exports.NormalBinaryOperator.Division; + case ts__namespace.SyntaxKind.PlusToken: + return exports.NormalBinaryOperator.Addition; + case ts__namespace.SyntaxKind.MinusToken: + return exports.NormalBinaryOperator.Subtraction; + case ts__namespace.SyntaxKind.AsteriskToken: + return exports.NormalBinaryOperator.Multiplication; + case ts__namespace.SyntaxKind.PercentToken: + return exports.NormalBinaryOperator.Remainder; + case ts__namespace.SyntaxKind.LessThanLessThanToken: + return exports.NormalBinaryOperator.LeftShift; + case ts__namespace.SyntaxKind.GreaterThanGreaterThanToken: + return exports.NormalBinaryOperator.RightShift; + case ts__namespace.SyntaxKind.GreaterThanGreaterThanGreaterThanToken: + return exports.NormalBinaryOperator.UnsignedRightShift; + case ts__namespace.SyntaxKind.AmpersandToken: + return exports.NormalBinaryOperator.BitwiseAnd; + case ts__namespace.SyntaxKind.BarToken: + return exports.NormalBinaryOperator.BitwiseOr; + case ts__namespace.SyntaxKind.CaretToken: + return exports.NormalBinaryOperator.BitwiseXor; + case ts__namespace.SyntaxKind.AmpersandAmpersandToken: + return exports.NormalBinaryOperator.LogicalAnd; + case ts__namespace.SyntaxKind.BarBarToken: + return exports.NormalBinaryOperator.LogicalOr; + case ts__namespace.SyntaxKind.LessThanToken: + return exports.RelationalBinaryOperator.LessThan; + case ts__namespace.SyntaxKind.LessThanEqualsToken: + return exports.RelationalBinaryOperator.LessThanOrEqual; + case ts__namespace.SyntaxKind.GreaterThanToken: + return exports.RelationalBinaryOperator.GreaterThan; + case ts__namespace.SyntaxKind.GreaterThanEqualsToken: + return exports.RelationalBinaryOperator.GreaterThanOrEqual; + case ts__namespace.SyntaxKind.EqualsEqualsToken: + return exports.RelationalBinaryOperator.Equality; + case ts__namespace.SyntaxKind.ExclamationEqualsToken: + return exports.RelationalBinaryOperator.InEquality; + case ts__namespace.SyntaxKind.EqualsEqualsEqualsToken: + return exports.RelationalBinaryOperator.StrictEquality; + case ts__namespace.SyntaxKind.ExclamationEqualsEqualsToken: + return exports.RelationalBinaryOperator.StrictInequality; + } + return null; + } + generateAssignStmtForValue(value, valueOriginalPositions) { + const leftOp = this.arkValueTransformer.generateTempLocal(value.getType()); + const leftOpPosition = valueOriginalPositions[0]; + const assignStmt = new ArkAssignStmt(leftOp, value); + assignStmt.setOperandOriginalPositions([leftOpPosition, ...valueOriginalPositions]); + return { + value: leftOp, + valueOriginalPositions: [leftOpPosition], + stmts: [assignStmt], + }; + } + generateIfStmtForValues(leftValue, leftOpOriginalPositions, rightValue, rightOpOriginalPositions) { + const stmts = []; + if (IRUtils.moreThanOneAddress(leftValue)) { + const { value: tempLeftValue, valueOriginalPositions: tempLeftPositions, stmts: leftStmts, } = this.generateAssignStmtForValue(leftValue, leftOpOriginalPositions); + leftStmts.forEach(stmt => stmts.push(stmt)); + leftValue = tempLeftValue; + leftOpOriginalPositions = tempLeftPositions; + } + if (IRUtils.moreThanOneAddress(rightValue)) { + const { value: tempRightValue, valueOriginalPositions: tempRightPositions, stmts: rightStmts, } = this.generateAssignStmtForValue(rightValue, rightOpOriginalPositions); + rightStmts.forEach(stmt => stmts.push(stmt)); + rightValue = tempRightValue; + rightOpOriginalPositions = tempRightPositions; + } + const conditionExpr = new ArkConditionExpr(leftValue, rightValue, exports.RelationalBinaryOperator.Equality); + const conditionPositions = [...leftOpOriginalPositions, ...rightOpOriginalPositions]; + const ifStmt = new ArkIfStmt(conditionExpr); + ifStmt.setOperandOriginalPositions([...conditionPositions]); + stmts.push(ifStmt); + return stmts; + } + setBuilderMethodContextFlag(builderMethodContextFlag) { + this.builderMethodContextFlag = builderMethodContextFlag; + } +} + +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Builder for loop in CFG + */ +class LoopBuilder { + rebuildBlocksInLoop(blockBuilderToCfgBlock, blocksContainLoopCondition, basicBlockSet, blockBuilders) { + for (const blockBuilder of blocksContainLoopCondition) { + if (!blockBuilderToCfgBlock.get(blockBuilder)) { + continue; + } + const block = blockBuilderToCfgBlock.get(blockBuilder); + const blockId = block.getId(); + const stmts = block.getStmts(); + const stmtsCnt = stmts.length; + const { ifStmtIdx, iteratorNextStmtIdx, dummyInitializerStmtIdx } = this.findIteratorIdx(stmts); + if (iteratorNextStmtIdx !== -1 || dummyInitializerStmtIdx !== -1) { + const lastStmtIdxBeforeCondition = iteratorNextStmtIdx !== -1 ? iteratorNextStmtIdx : dummyInitializerStmtIdx; + const stmtsInsertBeforeCondition = stmts.slice(0, lastStmtIdxBeforeCondition); + // If the loop body is empty, the loop conditional block should contain its own + const emptyLoopBody = blockBuilder.nexts.length === 1; + if (emptyLoopBody) { + blockBuilder.nexts.splice(0, 0, blockBuilder); + blockBuilder.lasts.push(blockBuilder); + block.getSuccessors().splice(0, 0, block); + block.addPredecessorBlock(block); + } + let prevBlockBuilderContainsLoop = this.doesPrevBlockBuilderContainLoop(blockBuilder, blockId, blocksContainLoopCondition); + if (prevBlockBuilderContainsLoop) { + // should create an extra block when previous block contains loop condition + this.insertBeforeConditionBlockBuilder(blockBuilderToCfgBlock, blockBuilder, stmtsInsertBeforeCondition, false, basicBlockSet, blockBuilders); + } + else { + const blockBuilderBeforeCondition = blockBuilder.lasts[0]; + const blockBeforeCondition = blockBuilderToCfgBlock.get(blockBuilderBeforeCondition); + stmtsInsertBeforeCondition.forEach(stmt => blockBeforeCondition?.getStmts().push(stmt)); + } + if (dummyInitializerStmtIdx !== -1 && ifStmtIdx !== stmtsCnt - 1) { + // put incrementor statements into block which reenters condition + this.adjustIncrementorStmts(stmts, ifStmtIdx, blockBuilder, blockId, blockBuilderToCfgBlock, blocksContainLoopCondition, basicBlockSet, emptyLoopBody, blockBuilders); + } + else if (iteratorNextStmtIdx !== -1) { + // put statements which get value of iterator into block after condition + const blockBuilderAfterCondition = blockBuilder.nexts[0]; + const blockAfterCondition = blockBuilderToCfgBlock.get(blockBuilderAfterCondition); + const stmtsAfterCondition = stmts.slice(ifStmtIdx + 1); + blockAfterCondition?.getStmts().splice(0, 0, ...stmtsAfterCondition); + } + // remove statements which should not in condition + const firstStmtIdxInCondition = iteratorNextStmtIdx !== -1 ? iteratorNextStmtIdx : dummyInitializerStmtIdx + 1; + stmts.splice(0, firstStmtIdxInCondition); + stmts.splice(ifStmtIdx - firstStmtIdxInCondition + 1); + } + } + } + doesPrevBlockBuilderContainLoop(currBlockBuilder, currBlockId, blocksContainLoopCondition) { + let prevBlockBuilderContainsLoop = false; + for (const prevBlockBuilder of currBlockBuilder.lasts) { + if (prevBlockBuilder.id < currBlockId && blocksContainLoopCondition.has(prevBlockBuilder)) { + prevBlockBuilderContainsLoop = true; + break; + } + } + return prevBlockBuilderContainsLoop; + } + insertBeforeConditionBlockBuilder(blockBuilderToCfgBlock, conditionBlockBuilder, stmtsInsertBeforeCondition, collectReenter, basicBlockSet, blockBuilders) { + if (stmtsInsertBeforeCondition.length === 0) { + return; + } + const blockId = conditionBlockBuilder.id; + const block = this.getBlockFromMap(blockBuilderToCfgBlock, conditionBlockBuilder); + const { blockBuildersBeforeCondition, blocksBeforeCondition, blockBuildersReenterCondition, blocksReenterCondition } = this.collectBlocksBeforeAndReenter(blockBuilderToCfgBlock, conditionBlockBuilder, blockId); + const { collectedBlockBuilders, collectedBlocks } = this.getCollectedBlocks(collectReenter, blockBuildersBeforeCondition, blocksBeforeCondition, blockBuildersReenterCondition, blocksReenterCondition); + const { blockBuilderInsertBeforeCondition, blockInsertBeforeCondition } = this.createAndLinkBlocks(collectedBlockBuilders, collectedBlocks, conditionBlockBuilder, stmtsInsertBeforeCondition, block); + this.updatePredecessors(collectedBlockBuilders, blockBuilderToCfgBlock, conditionBlockBuilder, blockBuilderInsertBeforeCondition, blockInsertBeforeCondition); + const { newPrevBlockBuildersBeforeCondition, newPrevBlocksBeforeCondition } = this.getNewPrevBlocks(collectReenter, blockBuildersBeforeCondition, blocksBeforeCondition, blockBuilderInsertBeforeCondition, blockInsertBeforeCondition, blockBuildersReenterCondition, blocksReenterCondition); + this.updateConditionBlockBuilder(conditionBlockBuilder, newPrevBlockBuildersBeforeCondition, block, newPrevBlocksBeforeCondition); + this.finalizeInsertion(blockBuilderInsertBeforeCondition, blockInsertBeforeCondition, basicBlockSet, blockBuilderToCfgBlock, blockBuilders); + } + getBlockFromMap(blockBuilderToCfgBlock, conditionBlockBuilder) { + return blockBuilderToCfgBlock.get(conditionBlockBuilder); + } + collectBlocksBeforeAndReenter(blockBuilderToCfgBlock, conditionBlockBuilder, blockId) { + const blockBuildersBeforeCondition = []; + const blocksBeforeCondition = []; + const blockBuildersReenterCondition = []; + const blocksReenterCondition = []; + for (const prevBlockBuilder of conditionBlockBuilder.lasts) { + const prevBlock = blockBuilderToCfgBlock.get(prevBlockBuilder); + if (prevBlock.getId() < blockId) { + blockBuildersBeforeCondition.push(prevBlockBuilder); + blocksBeforeCondition.push(prevBlock); + } + else { + blockBuildersReenterCondition.push(prevBlockBuilder); + blocksReenterCondition.push(prevBlock); + } + } + return { + blockBuildersBeforeCondition, + blocksBeforeCondition, + blockBuildersReenterCondition, + blocksReenterCondition, + }; + } + getCollectedBlocks(collectReenter, blockBuildersBeforeCondition, blocksBeforeCondition, blockBuildersReenterCondition, blocksReenterCondition) { + let collectedBlockBuilders = []; + let collectedBlocks = []; + if (collectReenter) { + collectedBlockBuilders = blockBuildersReenterCondition; + collectedBlocks = blocksReenterCondition; + } + else { + collectedBlockBuilders = blockBuildersBeforeCondition; + collectedBlocks = blocksBeforeCondition; + } + return { collectedBlockBuilders, collectedBlocks }; + } + createAndLinkBlocks(collectedBlockBuilders, collectedBlocks, conditionBlockBuilder, stmtsInsertBeforeCondition, block) { + const blockBuilderInsertBeforeCondition = new BlockBuilder(-1, []); + blockBuilderInsertBeforeCondition.lasts.push(...collectedBlockBuilders); + blockBuilderInsertBeforeCondition.nexts.push(conditionBlockBuilder); + const blockInsertBeforeCondition = new BasicBlock(); + stmtsInsertBeforeCondition.forEach(stmt => blockInsertBeforeCondition.getStmts().push(stmt)); + blockInsertBeforeCondition.getPredecessors().push(...collectedBlocks); + blockInsertBeforeCondition.addSuccessorBlock(block); + return { blockBuilderInsertBeforeCondition, blockInsertBeforeCondition }; + } + updatePredecessors(collectedBlockBuilders, blockBuilderToCfgBlock, conditionBlockBuilder, blockBuilderInsertBeforeCondition, blockInsertBeforeCondition) { + for (const prevBlockBuilder of collectedBlockBuilders) { + const prevBlock = blockBuilderToCfgBlock.get(prevBlockBuilder); + for (let j = 0; j < prevBlockBuilder.nexts.length; j++) { + if (prevBlockBuilder.nexts[j] === conditionBlockBuilder) { + prevBlockBuilder.nexts[j] = blockBuilderInsertBeforeCondition; + prevBlock.setSuccessorBlock(j, blockInsertBeforeCondition); + break; + } + } + } + } + getNewPrevBlocks(collectReenter, blockBuildersBeforeCondition, blocksBeforeCondition, blockBuilderInsertBeforeCondition, blockInsertBeforeCondition, blockBuildersReenterCondition, blocksReenterCondition) { + let newPrevBlockBuildersBeforeCondition = []; + let newPrevBlocksBeforeCondition = []; + if (collectReenter) { + newPrevBlockBuildersBeforeCondition = [...blockBuildersBeforeCondition, blockBuilderInsertBeforeCondition]; + newPrevBlocksBeforeCondition = [...blocksBeforeCondition, blockInsertBeforeCondition]; + } + else { + newPrevBlockBuildersBeforeCondition = [blockBuilderInsertBeforeCondition, ...blockBuildersReenterCondition]; + newPrevBlocksBeforeCondition = [blockInsertBeforeCondition, ...blocksReenterCondition]; + } + return { + newPrevBlockBuildersBeforeCondition, + newPrevBlocksBeforeCondition, + }; + } + updateConditionBlockBuilder(conditionBlockBuilder, newPrevBlockBuildersBeforeCondition, block, newPrevBlocksBeforeCondition) { + conditionBlockBuilder.lasts = newPrevBlockBuildersBeforeCondition; + const predecessorsCnt = block.getPredecessors().length; + block.getPredecessors().splice(0, predecessorsCnt, ...newPrevBlocksBeforeCondition); + } + finalizeInsertion(blockBuilderInsertBeforeCondition, blockInsertBeforeCondition, basicBlockSet, blockBuilderToCfgBlock, blockBuilders) { + blockBuilders.push(blockBuilderInsertBeforeCondition); + basicBlockSet.add(blockInsertBeforeCondition); + blockBuilderToCfgBlock.set(blockBuilderInsertBeforeCondition, blockInsertBeforeCondition); + } + findIteratorIdx(stmts) { + let ifStmtIdx = -1; + let iteratorNextStmtIdx = -1; + let dummyInitializerStmtIdx = -1; + const stmtsCnt = stmts.length; + for (let i = 0; i < stmtsCnt; i++) { + const stmt = stmts[i]; + if (stmt instanceof ArkAssignStmt && stmt.getRightOp() instanceof AbstractInvokeExpr) { + const invokeExpr = stmt.getRightOp(); + if (invokeExpr.getMethodSignature().getMethodSubSignature().getMethodName() === Builtin.ITERATOR_NEXT) { + iteratorNextStmtIdx = i; + continue; + } + } + if (stmt.toString() === ArkIRTransformer.DUMMY_LOOP_INITIALIZER_STMT) { + dummyInitializerStmtIdx = i; + continue; + } + if (stmt instanceof ArkIfStmt) { + ifStmtIdx = i; + break; + } + } + return { + ifStmtIdx: ifStmtIdx, + iteratorNextStmtIdx: iteratorNextStmtIdx, + dummyInitializerStmtIdx: dummyInitializerStmtIdx, + }; + } + adjustIncrementorStmts(stmts, ifStmtIdx, currBlockBuilder, currBlockId, blockBuilderToCfgBlock, blocksContainLoopCondition, basicBlockSet, emptyLoopBody, blockBuilders) { + const stmtsReenterCondition = stmts.slice(ifStmtIdx + 1); + if (emptyLoopBody) { + const incrementorBlockBuilder = new BlockBuilder(-1, []); + incrementorBlockBuilder.lasts.push(currBlockBuilder); + currBlockBuilder.nexts[0] = incrementorBlockBuilder; + incrementorBlockBuilder.nexts.push(currBlockBuilder); + currBlockBuilder.lasts[1] = incrementorBlockBuilder; + const incrementorBlock = new BasicBlock(); + blockBuilderToCfgBlock.set(incrementorBlockBuilder, incrementorBlock); + stmtsReenterCondition.forEach(stmt => incrementorBlock.getStmts().push(stmt)); + const currBlock = blockBuilderToCfgBlock.get(currBlockBuilder); + incrementorBlock.getPredecessors().push(currBlock); + currBlock.setPredecessorBlock(1, incrementorBlock); + incrementorBlock.addSuccessorBlock(currBlock); + currBlock.setSuccessorBlock(0, incrementorBlock); + basicBlockSet.add(incrementorBlock); + return; + } + const blockBuildersReenterCondition = []; + for (const prevBlockBuilder of currBlockBuilder.lasts) { + const prevBlock = blockBuilderToCfgBlock.get(prevBlockBuilder); + if (prevBlock.getId() > currBlockId) { + blockBuildersReenterCondition.push(prevBlockBuilder); + } + } + if (blockBuildersReenterCondition.length > 1 || blocksContainLoopCondition.has(blockBuildersReenterCondition[0])) { + // put incrementor statements into an extra block + this.insertBeforeConditionBlockBuilder(blockBuilderToCfgBlock, currBlockBuilder, stmtsReenterCondition, true, basicBlockSet, blockBuilders); + } + else { + // put incrementor statements into prev reenter block + const blockReenterCondition = blockBuilderToCfgBlock.get(blockBuildersReenterCondition[0]); + stmtsReenterCondition.forEach(stmt => blockReenterCondition?.getStmts().push(stmt)); + } + } +} + +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$r = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'SwitchBuilder'); +/** + * Builder for switch statement in CFG + */ +class SwitchBuilder { + buildSwitch(blockBuilderToCfgBlock, blockBuildersContainSwitch, valueAndStmtsOfSwitchAndCasesAll, arkIRTransformer, basicBlockSet) { + for (let i = 0; i < blockBuildersContainSwitch.length; i++) { + const blockBuilderContainSwitch = blockBuildersContainSwitch[i]; + if (!blockBuilderToCfgBlock.has(blockBuilderContainSwitch)) { + logger$r.error(`can't find basicBlock corresponding to the blockBuilder.`); + continue; + } + const blockContainSwitch = blockBuilderToCfgBlock.get(blockBuilderContainSwitch); + const valueAndStmtsOfSwitch = valueAndStmtsOfSwitchAndCasesAll[i][0]; + const stmtsOfSwitch = valueAndStmtsOfSwitch.stmts; + stmtsOfSwitch.forEach((stmt) => { + blockContainSwitch.addStmt(stmt); + }); + const stmtsCnt = blockBuilderContainSwitch.stmts.length; + const switchStmtBuilder = blockBuilderContainSwitch.stmts[stmtsCnt - 1]; + const cases = switchStmtBuilder.cases; + let nonEmptyCaseCnt = 0; + for (const currCase of cases) { + if (currCase.stmt.block) { + // there are stmts after this case + nonEmptyCaseCnt++; + } + } + if (nonEmptyCaseCnt === 0) { + continue; + } + const caseCnt = cases.length; + const caseIfBlocks = this.generateIfBlocksForCases(valueAndStmtsOfSwitchAndCasesAll[i], caseCnt, blockContainSwitch, basicBlockSet, arkIRTransformer); + this.linkIfBlockAndCaseBlock(blockContainSwitch, caseIfBlocks, switchStmtBuilder, blockBuilderToCfgBlock); + } + } + generateIfBlocksForCases(valueAndStmtsOfSwitchAndCases, caseCnt, blockContainSwitch, basicBlockSet, arkIRTransformer) { + const valueAndStmtsOfSwitch = valueAndStmtsOfSwitchAndCases[0]; + const valueOfSwitch = valueAndStmtsOfSwitch.value; + const caseIfBlocks = []; + for (let j = 0; j < caseCnt; j++) { + let caseIfBlock; + if (j === 0) { + caseIfBlock = blockContainSwitch; + } + else { + caseIfBlock = new BasicBlock(); + basicBlockSet.add(caseIfBlock); + } + caseIfBlocks.push(caseIfBlock); + const caseValueAndStmts = valueAndStmtsOfSwitchAndCases[j + 1]; + const caseValue = caseValueAndStmts.value; + const caseStmts = caseValueAndStmts.stmts; + caseStmts.forEach((stmt) => { + caseIfBlock.addStmt(stmt); + }); + const caseIfStmts = arkIRTransformer.generateIfStmtForValues(valueOfSwitch, valueAndStmtsOfSwitch.valueOriginalPositions, caseValue, caseValueAndStmts.valueOriginalPositions); + caseIfStmts.forEach((stmt) => { + caseIfBlock.addStmt(stmt); + }); + } + return caseIfBlocks; + } + linkIfBlockAndCaseBlock(blockContainSwitch, caseIfBlocks, switchStmtBuilder, blockBuilderToCfgBlock) { + const successorsOfBlockContainSwitch = Array.from(blockContainSwitch.getSuccessors()); + const expectedSuccessorsOfCaseIfBlock = []; + const defaultStmtBuilder = switchStmtBuilder.default; + if (defaultStmtBuilder && defaultStmtBuilder.block) { + expectedSuccessorsOfCaseIfBlock.push(...successorsOfBlockContainSwitch.splice(-1, 1)); + } + else { + const afterSwitchStmtBuilder = switchStmtBuilder.afterSwitch; + const afterSwitchBlockBuilder = afterSwitchStmtBuilder?.block; + if (!afterSwitchBlockBuilder || !blockBuilderToCfgBlock.has(afterSwitchBlockBuilder)) { + logger$r.error(`can't find basicBlock corresponding to the blockBuilder.`); + return false; + } + expectedSuccessorsOfCaseIfBlock.push(blockBuilderToCfgBlock.get(afterSwitchBlockBuilder)); + } + const caseCnt = switchStmtBuilder.cases.length; + for (let i = caseCnt - 1; i >= 0; i--) { + const currCase = switchStmtBuilder.cases[i]; + if (currCase.stmt.block) { + expectedSuccessorsOfCaseIfBlock.push(...successorsOfBlockContainSwitch.splice(-1, 1)); + } + else { + // if there are no stmts after this case, reuse the successor of the next case + expectedSuccessorsOfCaseIfBlock.push(...expectedSuccessorsOfCaseIfBlock.slice(-1)); + } + } + expectedSuccessorsOfCaseIfBlock.reverse(); + blockContainSwitch.getSuccessors().forEach(successor => { + successor.getPredecessors().splice(0, 1); + }); + blockContainSwitch.getSuccessors().splice(0); + for (let j = 0; j < caseCnt; j++) { + const caseIfBlock = caseIfBlocks[j]; + caseIfBlock.addSuccessorBlock(expectedSuccessorsOfCaseIfBlock[j]); + expectedSuccessorsOfCaseIfBlock[j].addPredecessorBlock(caseIfBlock); + if (j === caseCnt - 1) { + // the false branch of last case should be default or block after switch statement + caseIfBlock.addSuccessorBlock(expectedSuccessorsOfCaseIfBlock[j + 1]); + expectedSuccessorsOfCaseIfBlock[j + 1].addPredecessorBlock(caseIfBlock); + } + else { + caseIfBlock.addSuccessorBlock(caseIfBlocks[j + 1]); + caseIfBlocks[j + 1].addPredecessorBlock(caseIfBlock); + } + } + return true; + } +} + +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Builder for condition in CFG + */ +class ConditionBuilder { + rebuildBlocksContainConditionalOperator(basicBlockSet, isArkUIBuilder) { + if (isArkUIBuilder) { + this.deleteDummyConditionalOperatorStmt(basicBlockSet); + return; + } + const currBasicBlocks = Array.from(basicBlockSet); + for (const currBasicBlock of currBasicBlocks) { + const stmtsInCurrBasicBlock = Array.from(currBasicBlock.getStmts()); + const stmtsCnt = stmtsInCurrBasicBlock.length; + let conditionalOperatorEndPos = -1; + for (let i = stmtsCnt - 1; i >= 0; i--) { + const stmt = stmtsInCurrBasicBlock[i]; + if (stmt instanceof DummyStmt && stmt.toString()?.startsWith(ArkIRTransformer.DUMMY_CONDITIONAL_OPERATOR_END_STMT)) { + conditionalOperatorEndPos = i; + break; + } + } + if (conditionalOperatorEndPos === -1) { + continue; + } + let { generatedTopBlock: generatedTopBlock, generatedBottomBlocks: generatedBottomBlocks } = this.generateBlocksContainConditionalOperatorGroup(stmtsInCurrBasicBlock.slice(0, conditionalOperatorEndPos + 1), basicBlockSet); + if (conditionalOperatorEndPos !== stmtsCnt - 1) { + // need create a new basic block for rest statements + const { generatedTopBlock: extraBlock } = this.generateBlockWithoutConditionalOperator(stmtsInCurrBasicBlock.slice(conditionalOperatorEndPos + 1)); + generatedBottomBlocks.forEach(generatedBottomBlock => { + generatedBottomBlock.addSuccessorBlock(extraBlock); + extraBlock.addPredecessorBlock(generatedBottomBlock); + }); + basicBlockSet.add(extraBlock); + generatedBottomBlocks = this.removeUnnecessaryBlocksInConditionalOperator(extraBlock, basicBlockSet); + } + this.relinkPrevAndSuccOfBlockContainConditionalOperator(currBasicBlock, generatedTopBlock, generatedBottomBlocks); + basicBlockSet.delete(currBasicBlock); + } + } + relinkPrevAndSuccOfBlockContainConditionalOperator(currBasicBlock, generatedTopBlock, generatedBottomBlocks) { + const predecessorsOfCurrBasicBlock = Array.from(currBasicBlock.getPredecessors()); + predecessorsOfCurrBasicBlock.forEach(predecessor => { + predecessor.removeSuccessorBlock(currBasicBlock); + currBasicBlock.removePredecessorBlock(predecessor); + generatedTopBlock.addPredecessorBlock(predecessor); + predecessor.addSuccessorBlock(generatedTopBlock); + }); + const successorsOfCurrBasicBlock = Array.from(currBasicBlock.getSuccessors()); + successorsOfCurrBasicBlock.forEach(successor => { + successor.removePredecessorBlock(currBasicBlock); + currBasicBlock.removeSuccessorBlock(successor); + generatedBottomBlocks.forEach(generatedBottomBlock => { + generatedBottomBlock.addSuccessorBlock(successor); + successor.addPredecessorBlock(generatedBottomBlock); + }); + }); + } + generateBlocksContainConditionalOperatorGroup(sourceStmts, basicBlockSet) { + const { firstEndPos: firstEndPos } = this.findFirstConditionalOperator(sourceStmts); + if (firstEndPos === -1) { + return this.generateBlockWithoutConditionalOperator(sourceStmts); + } + const { generatedTopBlock: firstGeneratedTopBlock, generatedBottomBlocks: firstGeneratedBottomBlocks, generatedAllBlocks: firstGeneratedAllBlocks, } = this.generateBlocksContainSingleConditionalOperator(sourceStmts.slice(0, firstEndPos + 1)); + const generatedTopBlock = firstGeneratedTopBlock; + let generatedBottomBlocks = firstGeneratedBottomBlocks; + firstGeneratedAllBlocks.forEach(block => basicBlockSet.add(block)); + const stmtsCnt = sourceStmts.length; + if (firstEndPos !== stmtsCnt - 1) { + // need handle other conditional operators + const { generatedTopBlock: restGeneratedTopBlock, generatedBottomBlocks: restGeneratedBottomBlocks } = this.generateBlocksContainConditionalOperatorGroup(sourceStmts.slice(firstEndPos + 1, stmtsCnt), basicBlockSet); + firstGeneratedBottomBlocks.forEach(firstGeneratedBottomBlock => { + firstGeneratedBottomBlock.addSuccessorBlock(restGeneratedTopBlock); + restGeneratedTopBlock.addPredecessorBlock(firstGeneratedBottomBlock); + }); + restGeneratedBottomBlocks.forEach(block => basicBlockSet.add(block)); + this.removeUnnecessaryBlocksInConditionalOperator(restGeneratedTopBlock, basicBlockSet); + generatedBottomBlocks = restGeneratedBottomBlocks; + } + return { generatedTopBlock, generatedBottomBlocks }; + } + generateBlocksContainSingleConditionalOperator(sourceStmts) { + const { firstIfTruePos: ifTruePos, firstIfFalsePos: ifFalsePos, firstEndPos: endPos } = this.findFirstConditionalOperator(sourceStmts); + if (endPos === -1) { + return this.generateBlockWithoutConditionalOperator(sourceStmts); + } + const { generatedTopBlock: generatedTopBlock, generatedAllBlocks: generatedAllBlocks } = this.generateBlockWithoutConditionalOperator(sourceStmts.slice(0, ifTruePos)); + let generatedBottomBlocks = []; + const { generatedTopBlock: generatedTopBlockOfTrueBranch, generatedBottomBlocks: generatedBottomBlocksOfTrueBranch, generatedAllBlocks: generatedAllBlocksOfTrueBranch, } = this.generateBlocksContainSingleConditionalOperator(sourceStmts.slice(ifTruePos + 1, ifFalsePos)); + generatedBottomBlocks.push(...generatedBottomBlocksOfTrueBranch); + generatedAllBlocks.push(...generatedAllBlocksOfTrueBranch); + const { generatedTopBlock: generatedTopBlockOfFalseBranch, generatedBottomBlocks: generatedBottomBlocksOfFalseBranch, generatedAllBlocks: generatedAllBlocksOfFalseBranch, } = this.generateBlocksContainSingleConditionalOperator(sourceStmts.slice(ifFalsePos + 1, endPos)); + generatedBottomBlocks.push(...generatedBottomBlocksOfFalseBranch); + generatedAllBlocks.push(...generatedAllBlocksOfFalseBranch); + generatedTopBlock.addSuccessorBlock(generatedTopBlockOfTrueBranch); + generatedTopBlockOfTrueBranch.addPredecessorBlock(generatedTopBlock); + generatedTopBlock.addSuccessorBlock(generatedTopBlockOfFalseBranch); + generatedTopBlockOfFalseBranch.addPredecessorBlock(generatedTopBlock); + const stmtsCnt = sourceStmts.length; + if (endPos !== stmtsCnt - 1) { + // need create a new basic block for rest statements + const { generatedTopBlock: extraBlock } = this.generateBlockWithoutConditionalOperator(sourceStmts.slice(endPos + 1)); + generatedBottomBlocks.forEach(generatedBottomBlock => { + generatedBottomBlock.addSuccessorBlock(extraBlock); + extraBlock.addPredecessorBlock(generatedBottomBlock); + }); + generatedBottomBlocks = [extraBlock]; + generatedAllBlocks.push(extraBlock); + } + return { generatedTopBlock, generatedBottomBlocks, generatedAllBlocks }; + } + generateBlockWithoutConditionalOperator(sourceStmts) { + const generatedBlock = new BasicBlock(); + sourceStmts.forEach(stmt => generatedBlock.addStmt(stmt)); + return { + generatedTopBlock: generatedBlock, + generatedBottomBlocks: [generatedBlock], + generatedAllBlocks: [generatedBlock], + }; + } + deleteDummyConditionalOperatorStmt(basicBlockSet) { + for (const basicBlock of basicBlockSet) { + const stmts = Array.from(basicBlock.getStmts()); + for (const stmt of stmts) { + if (stmt instanceof DummyStmt && stmt.toString()?.startsWith(ArkIRTransformer.DUMMY_CONDITIONAL_OPERATOR)) { + basicBlock.remove(stmt); + } + } + } + } + findFirstConditionalOperator(stmts) { + let firstIfTruePos = -1; + let firstIfFalsePos = -1; + let firstEndPos = -1; + let firstConditionalOperatorNo = ''; + for (let i = 0; i < stmts.length; i++) { + const stmt = stmts[i]; + if (stmt instanceof DummyStmt) { + if (stmt.toString().startsWith(ArkIRTransformer.DUMMY_CONDITIONAL_OPERATOR_IF_TRUE_STMT) && firstIfTruePos === -1) { + firstIfTruePos = i; + firstConditionalOperatorNo = stmt.toString().replace(ArkIRTransformer.DUMMY_CONDITIONAL_OPERATOR_IF_TRUE_STMT, ''); + } + else if (stmt.toString() === ArkIRTransformer.DUMMY_CONDITIONAL_OPERATOR_IF_FALSE_STMT + firstConditionalOperatorNo) { + firstIfFalsePos = i; + } + else if (stmt.toString() === ArkIRTransformer.DUMMY_CONDITIONAL_OPERATOR_END_STMT + firstConditionalOperatorNo) { + firstEndPos = i; + } + } + } + return { firstIfTruePos, firstIfFalsePos, firstEndPos }; + } + removeUnnecessaryBlocksInConditionalOperator(bottomBlock, allBlocks) { + const firstStmtInBottom = bottomBlock.getStmts()[0]; + if (!(firstStmtInBottom instanceof ArkAssignStmt)) { + return [bottomBlock]; + } + const targetValue = firstStmtInBottom.getLeftOp(); + const tempResultValue = firstStmtInBottom.getRightOp(); + if (!(targetValue instanceof Local && IRUtils.isTempLocal(tempResultValue))) { + return [bottomBlock]; + } + const oldPredecessors = Array.from(bottomBlock.getPredecessors()); + const newPredecessors = []; + for (const predecessor of oldPredecessors) { + predecessor.removeSuccessorBlock(bottomBlock); + newPredecessors.push(...this.replaceTempRecursively(predecessor, targetValue, tempResultValue, allBlocks)); + } + bottomBlock.remove(firstStmtInBottom); + if (bottomBlock.getStmts().length === 0) { + // must be a new block without successors + allBlocks.delete(bottomBlock); + return newPredecessors; + } + oldPredecessors.forEach(oldPredecessor => { + bottomBlock.removePredecessorBlock(oldPredecessor); + }); + newPredecessors.forEach(newPredecessor => { + bottomBlock.addPredecessorBlock(newPredecessor); + newPredecessor.addSuccessorBlock(bottomBlock); + }); + return [bottomBlock]; + } + replaceTempRecursively(currBottomBlock, targetLocal, tempResultLocal, allBlocks) { + const stmts = currBottomBlock.getStmts(); + const stmtsCnt = stmts.length; + let tempResultReassignStmt = null; + for (let i = stmtsCnt - 1; i >= 0; i--) { + const stmt = stmts[i]; + if (stmt instanceof ArkAssignStmt && stmt.getLeftOp() === tempResultLocal) { + if (IRUtils.isTempLocal(stmt.getRightOp())) { + tempResultReassignStmt = stmt; + } + else { + stmt.setLeftOp(targetLocal); + } + } + } + let newBottomBlocks = []; + if (tempResultReassignStmt) { + const oldPredecessors = currBottomBlock.getPredecessors(); + const newPredecessors = []; + const prevTempResultLocal = tempResultReassignStmt.getRightOp(); + for (const predecessor of oldPredecessors) { + predecessor.removeSuccessorBlock(currBottomBlock); + newPredecessors.push(...this.replaceTempRecursively(predecessor, targetLocal, prevTempResultLocal, allBlocks)); + } + currBottomBlock.remove(tempResultReassignStmt); + if (currBottomBlock.getStmts().length === 0) { + // remove this block + newBottomBlocks = newPredecessors; + allBlocks.delete(currBottomBlock); + } + else { + currBottomBlock.getPredecessors().splice(0, oldPredecessors.length, ...newPredecessors); + newPredecessors.forEach(newPredecessor => { + newPredecessor.addSuccessorBlock(currBottomBlock); + }); + newBottomBlocks = [currBottomBlock]; + } + } + else { + newBottomBlocks = [currBottomBlock]; + } + return newBottomBlocks; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class Trap { + tryBlocks; + catchBlocks; + constructor(tryBlocks, catchBlocks) { + this.tryBlocks = tryBlocks; + this.catchBlocks = catchBlocks; + } + getTryBlocks() { + return this.tryBlocks; + } + getCatchBlocks() { + return this.catchBlocks; + } +} + +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$q = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'TrapBuilder'); +/** + * Builder for traps from try...catch + */ +class TrapBuilder { + buildTraps(blockBuilderToCfgBlock, blockBuildersBeforeTry, arkIRTransformer, basicBlockSet) { + const traps = []; + for (const blockBuilderBeforeTry of blockBuildersBeforeTry) { + if (blockBuilderBeforeTry.nexts.length === 0) { + logger$q.error(`can't find try block.`); + continue; + } + const blockBuilderContainTry = blockBuilderBeforeTry.nexts[0]; + const stmtsCnt = blockBuilderBeforeTry.stmts.length; + const tryStmtBuilder = blockBuilderBeforeTry.stmts[stmtsCnt - 1]; + const finallyBlockBuilder = tryStmtBuilder.finallyStatement?.block; + if (!finallyBlockBuilder) { + logger$q.error(`can't find finally block or dummy finally block.`); + continue; + } + const { bfsBlocks: tryBfsBlocks, tailBlocks: tryTailBlocks } = this.getAllBlocksBFS(blockBuilderToCfgBlock, blockBuilderContainTry, finallyBlockBuilder); + let catchBfsBlocks = []; + let catchTailBlocks = []; + const catchBlockBuilder = tryStmtBuilder.catchStatement?.block; + if (catchBlockBuilder) { + ({ bfsBlocks: catchBfsBlocks, tailBlocks: catchTailBlocks } = this.getAllBlocksBFS(blockBuilderToCfgBlock, catchBlockBuilder)); + } + const finallyStmts = finallyBlockBuilder.stmts; + const blockBuilderAfterFinally = tryStmtBuilder.afterFinal?.block; + if (!blockBuilderAfterFinally) { + logger$q.error(`can't find block after try...catch.`); + continue; + } + if (finallyStmts.length === 1 && finallyStmts[0].code === 'dummyFinally') { + // no finally block + const trapsIfNoFinally = this.buildTrapsIfNoFinally(tryBfsBlocks, tryTailBlocks, catchBfsBlocks, catchTailBlocks, finallyBlockBuilder, blockBuilderAfterFinally, basicBlockSet, blockBuilderToCfgBlock); + if (trapsIfNoFinally) { + traps.push(...trapsIfNoFinally); + } + } + else { + const trapsIfFinallyExist = this.buildTrapsIfFinallyExist(tryBfsBlocks, tryTailBlocks, catchBfsBlocks, catchTailBlocks, finallyBlockBuilder, blockBuilderAfterFinally, basicBlockSet, arkIRTransformer, blockBuilderToCfgBlock); + traps.push(...trapsIfFinallyExist); + } + } + return traps; + } + buildTrapsIfNoFinally(tryBfsBlocks, tryTailBlocks, catchBfsBlocks, catchTailBlocks, finallyBlockBuilder, blockBuilderAfterFinally, basicBlockSet, blockBuilderToCfgBlock) { + if (catchBfsBlocks.length === 0) { + logger$q.error(`catch block expected.`); + return null; + } + if (!blockBuilderToCfgBlock.has(blockBuilderAfterFinally)) { + logger$q.error(`can't find basicBlock corresponding to the blockBuilder.`); + return null; + } + let blockAfterFinally = blockBuilderToCfgBlock.get(blockBuilderAfterFinally); + if (!blockBuilderToCfgBlock.has(finallyBlockBuilder)) { + logger$q.error(`can't find basicBlock corresponding to the blockBuilder.`); + return null; + } + const finallyBlock = blockBuilderToCfgBlock.get(finallyBlockBuilder); + let dummyFinallyIdxInPredecessors = -1; + for (let i = 0; i < blockAfterFinally.getPredecessors().length; i++) { + if (blockAfterFinally.getPredecessors()[i] === finallyBlock) { + dummyFinallyIdxInPredecessors = i; + break; + } + } + if (dummyFinallyIdxInPredecessors === -1) { + return null; + } + blockAfterFinally.getPredecessors().splice(dummyFinallyIdxInPredecessors, 1); + for (const tryTailBlock of tryTailBlocks) { + tryTailBlock.setSuccessorBlock(0, blockAfterFinally); + blockAfterFinally.addPredecessorBlock(tryTailBlock); + } + basicBlockSet.delete(finallyBlock); + for (const catchTailBlock of catchTailBlocks) { + catchTailBlock.addSuccessorBlock(blockAfterFinally); + blockAfterFinally.addPredecessorBlock(catchTailBlock); + } + for (const tryTailBlock of tryTailBlocks) { + tryTailBlock.addExceptionalSuccessorBlock(catchBfsBlocks[0]); + } + return [new Trap(tryBfsBlocks, catchBfsBlocks)]; + } + buildTrapsIfFinallyExist(tryBfsBlocks, tryTailBlocks, catchBfsBlocks, catchTailBlocks, finallyBlockBuilder, blockBuilderAfterFinally, basicBlockSet, arkIRTransformer, blockBuilderToCfgBlock) { + const { bfsBlocks: finallyBfsBlocks, tailBlocks: finallyTailBlocks } = this.getAllBlocksBFS(blockBuilderToCfgBlock, finallyBlockBuilder, blockBuilderAfterFinally); + const copyFinallyBfsBlocks = this.copyFinallyBlocks(finallyBfsBlocks, finallyTailBlocks, basicBlockSet, arkIRTransformer, blockBuilderToCfgBlock); + const traps = []; + if (catchBfsBlocks.length !== 0) { + for (const catchTailBlock of catchTailBlocks) { + catchTailBlock.addSuccessorBlock(finallyBfsBlocks[0]); + finallyBfsBlocks[0].addPredecessorBlock(catchTailBlock); + } + // try -> catch trap + for (const tryTailBlock of tryTailBlocks) { + tryTailBlock.addExceptionalSuccessorBlock(catchBfsBlocks[0]); + } + traps.push(new Trap(tryBfsBlocks, catchBfsBlocks)); + // catch -> finally trap + for (const catchTailBlock of catchTailBlocks) { + catchTailBlock.addExceptionalSuccessorBlock(copyFinallyBfsBlocks[0]); + } + traps.push(new Trap(catchBfsBlocks, copyFinallyBfsBlocks)); + } + else { + // try -> finally trap + for (const tryTailBlock of tryTailBlocks) { + tryTailBlock.addExceptionalSuccessorBlock(copyFinallyBfsBlocks[0]); + } + traps.push(new Trap(tryBfsBlocks, copyFinallyBfsBlocks)); + } + return traps; + } + getAllBlocksBFS(blockBuilderToCfgBlock, startBlockBuilder, endBlockBuilder) { + const bfsBlocks = []; + const tailBlocks = []; + const queue = []; + const visitedBlockBuilders = new Set(); + queue.push(startBlockBuilder); + while (queue.length !== 0) { + const currBlockBuilder = queue.splice(0, 1)[0]; + if (visitedBlockBuilders.has(currBlockBuilder)) { + continue; + } + visitedBlockBuilders.add(currBlockBuilder); + if (!blockBuilderToCfgBlock.has(currBlockBuilder)) { + logger$q.error(`can't find basicBlock corresponding to the blockBuilder.`); + continue; + } + const currBlock = blockBuilderToCfgBlock.get(currBlockBuilder); + bfsBlocks.push(currBlock); + const childList = currBlockBuilder.nexts; + if (childList.length !== 0) { + for (const child of childList) { + // A tail block's successor may be within the traversal range + if (child === endBlockBuilder) { + tailBlocks.push(currBlock); + } + else { + queue.push(child); + } + } + } + else { + tailBlocks.push(currBlock); + } + } + return { bfsBlocks, tailBlocks }; + } + copyFinallyBlocks(finallyBfsBlocks, finallyTailBlocks, basicBlockSet, arkIRTransformer, blockBuilderToCfgBlock) { + const copyFinallyBfsBlocks = this.copyBlocks(finallyBfsBlocks); + const caughtExceptionRef = new ArkCaughtExceptionRef(UnknownType.getInstance()); + const { value: exceptionValue, stmts: exceptionAssignStmts } = arkIRTransformer.generateAssignStmtForValue(caughtExceptionRef, [FullPosition.DEFAULT]); + copyFinallyBfsBlocks[0].addHead(exceptionAssignStmts); + const finallyPredecessorsCnt = copyFinallyBfsBlocks[0].getPredecessors().length; + copyFinallyBfsBlocks[0].getPredecessors().splice(0, finallyPredecessorsCnt); + const throwStmt = new ArkThrowStmt(exceptionValue); + let copyFinallyTailBlocks = copyFinallyBfsBlocks.splice(copyFinallyBfsBlocks.length - finallyTailBlocks.length, finallyTailBlocks.length); + if (copyFinallyTailBlocks.length > 1) { + const newCopyFinallyTailBlock = new BasicBlock(); + copyFinallyTailBlocks.forEach((copyFinallyTailBlock) => { + copyFinallyTailBlock.addSuccessorBlock(newCopyFinallyTailBlock); + newCopyFinallyTailBlock.addPredecessorBlock(copyFinallyTailBlock); + }); + copyFinallyBfsBlocks.push(...copyFinallyTailBlocks); + copyFinallyTailBlocks = [newCopyFinallyTailBlock]; + } + copyFinallyTailBlocks[0].addStmt(throwStmt); + copyFinallyBfsBlocks.push(...copyFinallyTailBlocks); + copyFinallyBfsBlocks.forEach((copyFinallyBfsBlock) => { + basicBlockSet.add(copyFinallyBfsBlock); + }); + return copyFinallyBfsBlocks; + } + copyBlocks(sourceBlocks) { + const sourceToTarget = new Map(); + const targetBlocks = []; + for (const sourceBlock of sourceBlocks) { + const targetBlock = new BasicBlock(); + for (const stmt of sourceBlock.getStmts()) { + targetBlock.addStmt(this.copyStmt(stmt)); + } + sourceToTarget.set(sourceBlock, targetBlock); + targetBlocks.push(targetBlock); + } + for (const sourceBlock of sourceBlocks) { + const targetBlock = sourceToTarget.get(sourceBlock); + for (const predecessor of sourceBlock.getPredecessors()) { + const targetPredecessor = sourceToTarget.get(predecessor); + // Only include blocks within the copy range, so that predecessor and successor relationships to + // external blocks can be trimmed + if (targetPredecessor) { + targetBlock.addPredecessorBlock(targetPredecessor); + } + } + for (const successor of sourceBlock.getSuccessors()) { + const targetSuccessor = sourceToTarget.get(successor); + if (targetSuccessor) { + targetBlock.addSuccessorBlock(targetSuccessor); + } + } + } + return targetBlocks; + } + copyStmt(sourceStmt) { + if (sourceStmt instanceof ArkAssignStmt) { + return new ArkAssignStmt(sourceStmt.getLeftOp(), sourceStmt.getRightOp()); + } + else if (sourceStmt instanceof ArkInvokeStmt) { + return new ArkInvokeStmt(sourceStmt.getInvokeExpr()); + } + else if (sourceStmt instanceof ArkIfStmt) { + return new ArkIfStmt(sourceStmt.getConditionExpr()); + } + else if (sourceStmt instanceof ArkReturnStmt) { + return new ArkReturnStmt(sourceStmt.getOp()); + } + else if (sourceStmt instanceof ArkReturnVoidStmt) { + return new ArkReturnVoidStmt(); + } + else if (sourceStmt instanceof ArkThrowStmt) { + return new ArkThrowStmt(sourceStmt.getOp()); + } + return null; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class StatementBuilder { + type; + //节点对应源代码 + code; + next; + lasts; + walked; + index; + // TODO:以下两个属性需要获取 + line; //行号//ast节点存了一个start值为这段代码的起始地址,可以从start开始往回查原文有几个换行符确定行号 + column; // 列 + astNode; //ast节点对象 + scopeID; + addressCode3 = []; + block; + ifExitPass; + passTmies = 0; + numOfIdentifier = 0; + isDoWhile = false; + constructor(type, code, astNode, scopeID) { + this.type = type; + this.code = code; + this.next = null; + this.lasts = new Set(); + this.walked = false; + this.index = 0; + this.line = -1; + this.column = -1; + this.astNode = astNode; + this.scopeID = scopeID; + this.block = null; + this.ifExitPass = false; + } +} +class ConditionStatementBuilder extends StatementBuilder { + nextT; + nextF; + loopBlock; + condition; + doStatement = null; + constructor(type, code, astNode, scopeID) { + super(type, code, astNode, scopeID); + this.nextT = null; + this.nextF = null; + this.loopBlock = null; + this.condition = ''; + } +} +class SwitchStatementBuilder extends StatementBuilder { + nexts; + cases = []; + default = null; + afterSwitch = null; + constructor(type, code, astNode, scopeID) { + super(type, code, astNode, scopeID); + this.nexts = []; + } +} +class TryStatementBuilder extends StatementBuilder { + tryFirst = null; + tryExit = null; + catchStatement = null; + catchError = ''; + finallyStatement = null; + afterFinal = null; + constructor(type, code, astNode, scopeID) { + super(type, code, astNode, scopeID); + } +} +class Case { + value; + stmt; + valueNode; + constructor(value, stmt) { + this.value = value; + this.stmt = stmt; + } +} +let Scope$1 = class Scope { + id; + constructor(id) { + this.id = id; + } +}; +class BlockBuilder { + id; + stmts; + nexts = []; + lasts = []; + walked = false; + constructor(id, stmts) { + this.id = id; + this.stmts = stmts; + } +} +class TextError extends Error { + constructor(message) { + // 调用父类的构造函数,并传入错误消息 + super(message); + // 设置错误类型的名称 + this.name = 'TextError'; + } +} +class CfgBuilder { + name; + astRoot; + entry; + exit; + loopStack; + switchExitStack; + functions; + breakin; + statementArray; + dotEdges; + scopes; + tempVariableNum; + current3ACstm; + blocks; + currentDeclarationKeyword; + variables; + declaringClass; + importFromPath; + catches; + exits = []; + emptyBody = false; + arrowFunctionWithoutBlock = false; + sourceFile; + declaringMethod; + constructor(ast, name, declaringMethod, sourceFile) { + this.name = name; + this.astRoot = ast; + this.declaringMethod = declaringMethod; + this.declaringClass = declaringMethod.getDeclaringArkClass(); + this.entry = new StatementBuilder('entry', '', ast, 0); + this.loopStack = []; + this.switchExitStack = []; + this.functions = []; + this.breakin = ''; + this.statementArray = []; + this.dotEdges = []; + this.exit = new StatementBuilder('exit', 'return;', null, 0); + this.scopes = []; + this.tempVariableNum = 0; + this.current3ACstm = this.entry; + this.blocks = []; + this.currentDeclarationKeyword = ''; + this.variables = []; + this.importFromPath = []; + this.catches = []; + this.sourceFile = sourceFile; + this.arrowFunctionWithoutBlock = true; + } + getDeclaringMethod() { + return this.declaringMethod; + } + judgeLastType(s, lastStatement) { + if (lastStatement.type === 'ifStatement') { + let lastIf = lastStatement; + if (lastIf.nextT == null) { + lastIf.nextT = s; + s.lasts.add(lastIf); + } + else { + lastIf.nextF = s; + s.lasts.add(lastIf); + } + } + else if (lastStatement.type === 'loopStatement') { + let lastLoop = lastStatement; + lastLoop.nextT = s; + s.lasts.add(lastLoop); + } + else if (lastStatement.type === 'catchOrNot') { + let lastLoop = lastStatement; + lastLoop.nextT = s; + s.lasts.add(lastLoop); + } + else { + lastStatement.next = s; + s.lasts.add(lastStatement); + } + } + ASTNodeBreakStatement(c, lastStatement) { + let p = c; + while (p && p !== this.astRoot) { + if (ts__namespace.isWhileStatement(p) || ts__namespace.isDoStatement(p) || ts__namespace.isForStatement(p) || ts__namespace.isForInStatement(p) || ts__namespace.isForOfStatement(p)) { + const lastLoopNextF = this.loopStack[this.loopStack.length - 1].nextF; + this.judgeLastType(lastLoopNextF, lastStatement); + lastLoopNextF.lasts.add(lastStatement); + return; + } + if (ts__namespace.isCaseClause(p) || ts__namespace.isDefaultClause(p)) { + const lastSwitchExit = this.switchExitStack[this.switchExitStack.length - 1]; + this.judgeLastType(lastSwitchExit, lastStatement); + lastSwitchExit.lasts.add(lastStatement); + return; + } + p = p.parent; + } + } + ASTNodeIfStatement(c, lastStatement, scopeID) { + let ifstm = new ConditionStatementBuilder('ifStatement', '', c, scopeID); + this.judgeLastType(ifstm, lastStatement); + let ifexit = new StatementBuilder('ifExit', '', c, scopeID); + this.exits.push(ifexit); + ifstm.condition = c.expression.getText(this.sourceFile); + ifstm.code = 'if (' + ifstm.condition + ')'; + if (ts__namespace.isBlock(c.thenStatement)) { + this.walkAST(ifstm, ifexit, [...c.thenStatement.statements]); + } + else { + this.walkAST(ifstm, ifexit, [c.thenStatement]); + } + if (c.elseStatement) { + if (ts__namespace.isBlock(c.elseStatement)) { + this.walkAST(ifstm, ifexit, [...c.elseStatement.statements]); + } + else { + this.walkAST(ifstm, ifexit, [c.elseStatement]); + } + } + if (!ifstm.nextT) { + ifstm.nextT = ifexit; + ifexit.lasts.add(ifstm); + } + if (!ifstm.nextF) { + ifstm.nextF = ifexit; + ifexit.lasts.add(ifstm); + } + return ifexit; + } + ASTNodeWhileStatement(c, lastStatement, scopeID) { + this.breakin = 'loop'; + let loopstm = new ConditionStatementBuilder('loopStatement', '', c, scopeID); + this.loopStack.push(loopstm); + this.judgeLastType(loopstm, lastStatement); + let loopExit = new StatementBuilder('loopExit', '', c, scopeID); + this.exits.push(loopExit); + loopstm.nextF = loopExit; + loopExit.lasts.add(loopstm); + loopstm.condition = c.expression.getText(this.sourceFile); + loopstm.code = 'while (' + loopstm.condition + ')'; + if (ts__namespace.isBlock(c.statement)) { + this.walkAST(loopstm, loopstm, [...c.statement.statements]); + } + else { + this.walkAST(loopstm, loopstm, [c.statement]); + } + if (!loopstm.nextF) { + loopstm.nextF = loopExit; + loopExit.lasts.add(loopstm); + } + if (!loopstm.nextT) { + loopstm.nextT = loopExit; + loopExit.lasts.add(loopstm); + } + this.loopStack.pop(); + return loopExit; + } + ASTNodeForStatement(c, lastStatement, scopeID) { + this.breakin = 'loop'; + let loopstm = new ConditionStatementBuilder('loopStatement', '', c, scopeID); + this.loopStack.push(loopstm); + this.judgeLastType(loopstm, lastStatement); + let loopExit = new StatementBuilder('loopExit', '', c, scopeID); + this.exits.push(loopExit); + loopstm.nextF = loopExit; + loopExit.lasts.add(loopstm); + loopstm.code = 'for ('; + if (ts__namespace.isForStatement(c)) { + loopstm.code += + c.initializer?.getText(this.sourceFile) + '; ' + c.condition?.getText(this.sourceFile) + '; ' + c.incrementor?.getText(this.sourceFile); + } + else if (ts__namespace.isForOfStatement(c)) { + loopstm.code += c.initializer?.getText(this.sourceFile) + ' of ' + c.expression.getText(this.sourceFile); + } + else { + loopstm.code += c.initializer?.getText(this.sourceFile) + ' in ' + c.expression.getText(this.sourceFile); + } + loopstm.code += ')'; + if (ts__namespace.isBlock(c.statement)) { + this.walkAST(loopstm, loopstm, [...c.statement.statements]); + } + else { + this.walkAST(loopstm, loopstm, [c.statement]); + } + if (!loopstm.nextF) { + loopstm.nextF = loopExit; + loopExit.lasts.add(loopstm); + } + if (!loopstm.nextT) { + loopstm.nextT = loopExit; + loopExit.lasts.add(loopstm); + } + this.loopStack.pop(); + return loopExit; + } + ASTNodeDoStatement(c, lastStatement, scopeID) { + this.breakin = 'loop'; + let loopstm = new ConditionStatementBuilder('loopStatement', '', c, scopeID); + this.loopStack.push(loopstm); + let loopExit = new StatementBuilder('loopExit', '', c, scopeID); + this.exits.push(loopExit); + loopstm.nextF = loopExit; + loopExit.lasts.add(loopstm); + loopstm.condition = c.expression.getText(this.sourceFile); + loopstm.code = 'while (' + loopstm.condition + ')'; + loopstm.isDoWhile = true; + if (ts__namespace.isBlock(c.statement)) { + this.walkAST(lastStatement, loopstm, [...c.statement.statements]); + } + else { + this.walkAST(lastStatement, loopstm, [c.statement]); + } + let lastType = lastStatement.type; + if (lastType === 'ifStatement' || lastType === 'loopStatement') { + let lastCondition = lastStatement; + loopstm.nextT = lastCondition.nextT; + lastCondition.nextT?.lasts.add(loopstm); + } + else { + loopstm.nextT = lastStatement.next; + lastStatement.next?.lasts.add(loopstm); + } + if (loopstm.nextT && loopstm.nextT !== loopstm) { + loopstm.nextT.isDoWhile = true; + loopstm.doStatement = loopstm.nextT; + } + this.loopStack.pop(); + return loopExit; + } + ASTNodeSwitchStatement(c, lastStatement, scopeID) { + this.breakin = 'switch'; + let switchstm = new SwitchStatementBuilder('switchStatement', '', c, scopeID); + this.judgeLastType(switchstm, lastStatement); + let switchExit = new StatementBuilder('switchExit', '', null, scopeID); + this.exits.push(switchExit); + this.switchExitStack.push(switchExit); + switchExit.lasts.add(switchstm); + switchstm.code = 'switch (' + c.expression + ')'; + let lastCaseExit = null; + for (let i = 0; i < c.caseBlock.clauses.length; i++) { + const clause = c.caseBlock.clauses[i]; + let casestm; + if (ts__namespace.isCaseClause(clause)) { + casestm = new StatementBuilder('statement', 'case ' + clause.expression.getText(this.sourceFile) + ':', clause, scopeID); + } + else { + casestm = new StatementBuilder('statement', 'default:', clause, scopeID); + } + switchstm.nexts.push(casestm); + casestm.lasts.add(switchstm); + let caseExit = new StatementBuilder('caseExit', '', null, scopeID); + this.exits.push(caseExit); + this.walkAST(casestm, caseExit, [...clause.statements]); + if (ts__namespace.isCaseClause(clause)) { + const cas = new Case(casestm.code, casestm.next); + switchstm.cases.push(cas); + } + else { + switchstm.default = casestm.next; + } + switchstm.nexts[switchstm.nexts.length - 1] = casestm.next; + for (const stmt of [...casestm.lasts]) { + casestm.next.lasts.add(stmt); + } + casestm.next.lasts.delete(casestm); + if (lastCaseExit) { + lastCaseExit.next = casestm.next; + casestm.next?.lasts.add(lastCaseExit); + } + lastCaseExit = caseExit; + if (i === c.caseBlock.clauses.length - 1) { + caseExit.next = switchExit; + switchExit.lasts.add(caseExit); + } + } + this.switchExitStack.pop(); + return switchExit; + } + ASTNodeTryStatement(c, lastStatement, scopeID) { + let trystm = new TryStatementBuilder('tryStatement', 'try', c, scopeID); + this.judgeLastType(trystm, lastStatement); + let tryExit = new StatementBuilder('tryExit', '', c, scopeID); + this.exits.push(tryExit); + trystm.tryExit = tryExit; + this.walkAST(trystm, tryExit, [...c.tryBlock.statements]); + trystm.tryFirst = trystm.next; + trystm.next?.lasts.add(trystm); + if (c.catchClause) { + let text = 'catch'; + if (c.catchClause.variableDeclaration) { + text += '(' + c.catchClause.variableDeclaration.getText(this.sourceFile) + ')'; + } + let catchOrNot = new ConditionStatementBuilder('catchOrNot', text, c, scopeID); + let catchExit = new StatementBuilder('catch exit', '', c, scopeID); + catchOrNot.nextF = catchExit; + catchExit.lasts.add(catchOrNot); + this.walkAST(catchOrNot, catchExit, [...c.catchClause.block.statements]); + if (!catchOrNot.nextT) { + catchOrNot.nextT = catchExit; + catchExit.lasts.add(catchOrNot); + } + const catchStatement = new StatementBuilder('statement', catchOrNot.code, c.catchClause, catchOrNot.nextT.scopeID); + catchStatement.next = catchOrNot.nextT; + trystm.catchStatement = catchStatement; + catchStatement.lasts.add(trystm); + if (c.catchClause.variableDeclaration) { + trystm.catchError = c.catchClause.variableDeclaration.getText(this.sourceFile); + } + else { + trystm.catchError = 'Error'; + } + } + let final = new StatementBuilder('statement', 'finally', c, scopeID); + let finalExit = new StatementBuilder('finallyExit', '', c, scopeID); + this.exits.push(finalExit); + if (c.finallyBlock && c.finallyBlock.statements.length > 0) { + this.walkAST(final, finalExit, [...c.finallyBlock.statements]); + } + else { + let dummyFinally = new StatementBuilder('statement', 'dummyFinally', c, new Scope$1(this.scopes.length).id); + final.next = dummyFinally; + dummyFinally.lasts.add(final); + dummyFinally.next = finalExit; + finalExit.lasts.add(dummyFinally); + } + trystm.finallyStatement = final.next; + tryExit.next = final.next; + final.next?.lasts.add(tryExit); + trystm.next = finalExit; + finalExit.lasts.add(trystm); + return finalExit; + } + walkAST(lastStatement, nextStatement, nodes) { + let scope = new Scope$1(this.scopes.length); + this.scopes.push(scope); + for (let i = 0; i < nodes.length; i++) { + let c = nodes[i]; + if (ts__namespace.isVariableStatement(c) || ts__namespace.isExpressionStatement(c) || ts__namespace.isThrowStatement(c) || ts__namespace.isTypeAliasDeclaration(c)) { + let s = new StatementBuilder('statement', c.getText(this.sourceFile), c, scope.id); + this.judgeLastType(s, lastStatement); + lastStatement = s; + } + else if (!this.declaringMethod.isDefaultArkMethod() && ts__namespace.isFunctionDeclaration(c)) { + let s = new StatementBuilder('functionDeclarationStatement', c.getText(this.sourceFile), c, scope.id); + this.judgeLastType(s, lastStatement); + lastStatement = s; + } + else if (!this.declaringMethod.isDefaultArkMethod() && ts__namespace.isClassDeclaration(c)) { + let s = new StatementBuilder('classDeclarationStatement', c.getText(this.sourceFile), c, scope.id); + this.judgeLastType(s, lastStatement); + lastStatement = s; + } + else if (ts__namespace.isReturnStatement(c)) { + let s = new StatementBuilder('returnStatement', c.getText(this.sourceFile), c, scope.id); + this.judgeLastType(s, lastStatement); + lastStatement = s; + break; + } + else if (ts__namespace.isBreakStatement(c)) { + this.ASTNodeBreakStatement(c, lastStatement); + return; + } + else if (ts__namespace.isContinueStatement(c)) { + const lastLoop = this.loopStack[this.loopStack.length - 1]; + this.judgeLastType(lastLoop, lastStatement); + lastLoop.lasts.add(lastStatement); + return; + } + else if (ts__namespace.isIfStatement(c)) { + lastStatement = this.ASTNodeIfStatement(c, lastStatement, scope.id); + } + else if (ts__namespace.isWhileStatement(c)) { + lastStatement = this.ASTNodeWhileStatement(c, lastStatement, scope.id); + } + if (ts__namespace.isForStatement(c) || ts__namespace.isForInStatement(c) || ts__namespace.isForOfStatement(c)) { + lastStatement = this.ASTNodeForStatement(c, lastStatement, scope.id); + } + else if (ts__namespace.isDoStatement(c)) { + lastStatement = this.ASTNodeDoStatement(c, lastStatement, scope.id); + } + else if (ts__namespace.isSwitchStatement(c)) { + lastStatement = this.ASTNodeSwitchStatement(c, lastStatement, scope.id); + } + else if (ts__namespace.isBlock(c)) { + let blockExit = new StatementBuilder('blockExit', '', c, scope.id); + this.exits.push(blockExit); + this.walkAST(lastStatement, blockExit, c.getChildren(this.sourceFile)[1].getChildren(this.sourceFile)); + lastStatement = blockExit; + } + else if (ts__namespace.isTryStatement(c)) { + lastStatement = this.ASTNodeTryStatement(c, lastStatement, scope.id); + } + else if (ts__namespace.isExportAssignment(c)) { + if (ts__namespace.isNewExpression(c.expression) || ts__namespace.isObjectLiteralExpression(c.expression)) { + let s = new StatementBuilder('statement', c.getText(this.sourceFile), c, scope.id); + this.judgeLastType(s, lastStatement); + lastStatement = s; + } + } + } + if (lastStatement.type !== 'breakStatement' && lastStatement.type !== 'continueStatement' && lastStatement.type !== 'returnStatement') { + lastStatement.next = nextStatement; + nextStatement.lasts.add(lastStatement); + } + } + addReturnInEmptyMethod() { + if (this.entry.next === this.exit) { + const ret = new StatementBuilder('returnStatement', 'return;', null, this.entry.scopeID); + this.entry.next = ret; + ret.lasts.add(this.entry); + ret.next = this.exit; + this.exit.lasts = new Set([ret]); + } + } + deleteExitAfterCondition(last, exit) { + if (last.nextT === exit) { + last.nextT = exit.next; + const lasts = exit.next.lasts; + lasts.delete(exit); + lasts.add(last); + } + else if (last.nextF === exit) { + last.nextF = exit.next; + const lasts = exit.next.lasts; + lasts.delete(exit); + lasts.add(last); + } + } + deleteExitAfterSwitch(last, exit) { + if (exit.type === 'switchExit') { + last.afterSwitch = exit.next; + } + exit.next.lasts.delete(exit); + last.nexts = last.nexts.filter(item => item !== exit); + if (last.nexts.length === 0) { + last.next = exit.next; + exit.next?.lasts.add(last); + } + } + deleteExit() { + for (const exit of this.exits) { + const lasts = [...exit.lasts]; + for (const last of lasts) { + if (last instanceof ConditionStatementBuilder) { + this.deleteExitAfterCondition(last, exit); + } + else if (last instanceof SwitchStatementBuilder) { + this.deleteExitAfterSwitch(last, exit); + } + else if (last instanceof TryStatementBuilder && exit.type === 'finallyExit') { + last.afterFinal = exit.next; + last.next = last.tryFirst; + exit.lasts.delete(last); + } + else { + last.next = exit.next; + const lasts = exit.next.lasts; + lasts.delete(exit); + lasts.add(last); + } + } + } + // 部分语句例如return后面的exit语句的next无法在上面清除 + for (const exit of this.exits) { + if (exit.next && exit.next.lasts.has(exit)) { + exit.next.lasts.delete(exit); + } + } + } + addStmt2BlockStmtQueueInSpecialCase(stmt, stmtQueue) { + if (stmt.next) { + if (((stmt.type === 'continueStatement' || stmt.next.type === 'loopStatement') && stmt.next.block) || stmt.next.type.includes('exit')) { + return null; + } + stmt.next.passTmies++; + if (stmt.next.passTmies === stmt.next.lasts.size || stmt.next.type === 'loopStatement' || stmt.next.isDoWhile) { + if (stmt.next.scopeID !== stmt.scopeID && + !(stmt.next instanceof ConditionStatementBuilder && stmt.next.doStatement) && + !(ts__namespace.isCaseClause(stmt.astNode) || ts__namespace.isDefaultClause(stmt.astNode))) { + stmtQueue.push(stmt.next); + return null; + } + return stmt.next; + } + } + return null; + } + addStmt2BlockStmtQueue(stmt, stmtQueue) { + if (stmt instanceof ConditionStatementBuilder) { + stmtQueue.push(stmt.nextF); + stmtQueue.push(stmt.nextT); + } + else if (stmt instanceof SwitchStatementBuilder) { + if (stmt.nexts.length === 0) { + stmtQueue.push(stmt.afterSwitch); + } + for (let i = stmt.nexts.length - 1; i >= 0; i--) { + stmtQueue.push(stmt.nexts[i]); + } + } + else if (stmt instanceof TryStatementBuilder) { + if (stmt.finallyStatement) { + stmtQueue.push(stmt.finallyStatement); + } + if (stmt.catchStatement) { + stmtQueue.push(stmt.catchStatement); + } + if (stmt.tryFirst) { + stmtQueue.push(stmt.tryFirst); + } + } + else if (stmt.next) { + return this.addStmt2BlockStmtQueueInSpecialCase(stmt, stmtQueue); + } + return null; + } + buildBlocks() { + const stmtQueue = [this.entry]; + const handledStmts = new Set(); + while (stmtQueue.length > 0) { + let stmt = stmtQueue.pop(); + if (stmt.type.includes('exit')) { + continue; + } + if (handledStmts.has(stmt)) { + continue; + } + const block = new BlockBuilder(this.blocks.length, []); + this.blocks.push(block); + while (stmt && !handledStmts.has(stmt)) { + if (stmt.type === 'loopStatement' && block.stmts.length > 0 && !stmt.isDoWhile) { + stmtQueue.push(stmt); + break; + } + if (stmt.type.includes('Exit')) { + break; + } + block.stmts.push(stmt); + stmt.block = block; + handledStmts.add(stmt); + const addRet = this.addStmt2BlockStmtQueue(stmt, stmtQueue); + if (addRet instanceof StatementBuilder) { + stmt = addRet; + } + else { + break; + } + } + } + } + buildConditionNextBlocks(originStatement, block, isLastStatement) { + let nextT = originStatement.nextT?.block; + if (nextT && (isLastStatement || nextT !== block) && !originStatement.nextT?.type.includes(' exit')) { + block.nexts.push(nextT); + nextT.lasts.push(block); + } + let nextF = originStatement.nextF?.block; + if (nextF && (isLastStatement || nextF !== block) && !originStatement.nextF?.type.includes(' exit')) { + block.nexts.push(nextF); + nextF.lasts.push(block); + } + } + buildSwitchNextBlocks(originStatement, block, isLastStatement) { + if (originStatement.nexts.length === 0) { + const nextBlock = originStatement.afterSwitch.block; + if (nextBlock && (isLastStatement || nextBlock !== block)) { + block.nexts.push(nextBlock); + nextBlock.lasts.push(block); + } + } + for (const next of originStatement.nexts) { + const nextBlock = next.block; + if (nextBlock && (isLastStatement || nextBlock !== block)) { + block.nexts.push(nextBlock); + nextBlock.lasts.push(block); + } + } + } + buildNormalNextBlocks(originStatement, block, isLastStatement) { + let next = originStatement.next?.block; + if (next && (isLastStatement || next !== block) && !originStatement.next?.type.includes(' exit')) { + block.nexts.push(next); + next.lasts.push(block); + } + } + buildBlocksNextLast() { + for (let block of this.blocks) { + for (let originStatement of block.stmts) { + let isLastStatement = block.stmts.indexOf(originStatement) === block.stmts.length - 1; + if (originStatement instanceof ConditionStatementBuilder) { + this.buildConditionNextBlocks(originStatement, block, isLastStatement); + } + else if (originStatement instanceof SwitchStatementBuilder) { + this.buildSwitchNextBlocks(originStatement, block, isLastStatement); + } + else { + this.buildNormalNextBlocks(originStatement, block, isLastStatement); + } + } + } + } + addReturnBlock(returnStatement, notReturnStmts) { + let returnBlock = new BlockBuilder(this.blocks.length, [returnStatement]); + returnStatement.block = returnBlock; + this.blocks.push(returnBlock); + for (const notReturnStmt of notReturnStmts) { + if (notReturnStmt instanceof ConditionStatementBuilder) { + if (this.exit === notReturnStmt.nextT) { + notReturnStmt.nextT = returnStatement; + notReturnStmt.block?.nexts.splice(0, 0, returnBlock); + } + else if (this.exit === notReturnStmt.nextF) { + notReturnStmt.nextF = returnStatement; + notReturnStmt.block?.nexts.push(returnBlock); + } + } + else { + notReturnStmt.next = returnStatement; + notReturnStmt.block?.nexts.push(returnBlock); + } + returnStatement.lasts.add(notReturnStmt); + returnStatement.next = this.exit; + const lasts = [...this.exit.lasts]; + lasts[lasts.indexOf(notReturnStmt)] = returnStatement; + this.exit.lasts = new Set(lasts); + returnBlock.lasts.push(notReturnStmt.block); + } + this.exit.block = returnBlock; + } + addReturnStmt() { + let notReturnStmts = []; + for (let stmt of [...this.exit.lasts]) { + if (stmt.type !== 'returnStatement') { + notReturnStmts.push(stmt); + } + } + if (notReturnStmts.length < 1) { + return; + } + const returnStatement = new StatementBuilder('returnStatement', 'return;', null, this.exit.scopeID); + let TryOrSwitchExit = false; + if (notReturnStmts.length === 1 && notReturnStmts[0].block) { + let p = notReturnStmts[0].astNode; + while (p && p !== this.astRoot) { + if (ts__namespace.isTryStatement(p) || ts__namespace.isSwitchStatement(p)) { + TryOrSwitchExit = true; + break; + } + p = p.parent; + } + } + if (notReturnStmts.length === 1 && !(notReturnStmts[0] instanceof ConditionStatementBuilder) && !TryOrSwitchExit) { + const notReturnStmt = notReturnStmts[0]; + notReturnStmt.next = returnStatement; + returnStatement.lasts = new Set([notReturnStmt]); + returnStatement.next = this.exit; + const lasts = [...this.exit.lasts]; + lasts[lasts.indexOf(notReturnStmt)] = returnStatement; + this.exit.lasts = new Set(lasts); + notReturnStmt.block?.stmts.push(returnStatement); + returnStatement.block = notReturnStmt.block; + } + else { + this.addReturnBlock(returnStatement, notReturnStmts); + } + } + resetWalked() { + for (let stmt of this.statementArray) { + stmt.walked = false; + } + } + addStmtBuilderPosition() { + for (const stmt of this.statementArray) { + if (stmt.astNode) { + const { line, character } = ts__namespace.getLineAndCharacterOfPosition(this.sourceFile, stmt.astNode.getStart(this.sourceFile)); + stmt.line = line + 1; + stmt.column = character + 1; + } + } + } + CfgBuilder2Array(stmt) { + if (stmt.walked) { + return; + } + stmt.walked = true; + stmt.index = this.statementArray.length; + if (!stmt.type.includes(' exit')) { + this.statementArray.push(stmt); + } + if (stmt.type === 'ifStatement' || stmt.type === 'loopStatement' || stmt.type === 'catchOrNot') { + let cstm = stmt; + if (cstm.nextT == null || cstm.nextF == null) { + this.errorTest(cstm); + return; + } + this.CfgBuilder2Array(cstm.nextF); + this.CfgBuilder2Array(cstm.nextT); + } + else if (stmt.type === 'switchStatement') { + let sstm = stmt; + for (let ss of sstm.nexts) { + this.CfgBuilder2Array(ss); + } + } + else if (stmt.type === 'tryStatement') { + let trystm = stmt; + if (trystm.tryFirst) { + this.CfgBuilder2Array(trystm.tryFirst); + } + if (trystm.catchStatement) { + this.CfgBuilder2Array(trystm.catchStatement); + } + if (trystm.finallyStatement) { + this.CfgBuilder2Array(trystm.finallyStatement); + } + if (trystm.next) { + this.CfgBuilder2Array(trystm.next); + } + } + else { + if (stmt.next != null) { + this.CfgBuilder2Array(stmt.next); + } + } + } + getDotEdges(stmt) { + if (this.statementArray.length === 0) { + this.CfgBuilder2Array(this.entry); + } + if (stmt.walked) { + return; + } + stmt.walked = true; + if (stmt.type === 'ifStatement' || stmt.type === 'loopStatement' || stmt.type === 'catchOrNot') { + let cstm = stmt; + if (cstm.nextT == null || cstm.nextF == null) { + this.errorTest(cstm); + return; + } + let edge = [cstm.index, cstm.nextF.index]; + this.dotEdges.push(edge); + edge = [cstm.index, cstm.nextT.index]; + this.dotEdges.push(edge); + this.getDotEdges(cstm.nextF); + this.getDotEdges(cstm.nextT); + } + else if (stmt.type === 'switchStatement') { + let sstm = stmt; + for (let ss of sstm.nexts) { + let edge = [sstm.index, ss.index]; + this.dotEdges.push(edge); + this.getDotEdges(ss); + } + } + else { + if (stmt.next != null) { + let edge = [stmt.index, stmt.next.index]; + this.dotEdges.push(edge); + this.getDotEdges(stmt.next); + } + } + } + errorTest(stmt) { + let mes = 'ifnext error '; + if (this.declaringClass?.getDeclaringArkFile()) { + mes += this.declaringClass?.getDeclaringArkFile().getName() + '.' + this.declaringClass.getName() + '.' + this.name; + } + mes += '\n' + stmt.code; + throw new TextError(mes); + } + buildStatementBuilder4ArrowFunction(stmt) { + let s = new StatementBuilder('statement', stmt.getText(this.sourceFile), stmt, 0); + this.entry.next = s; + s.lasts = new Set([this.entry]); + s.next = this.exit; + this.exit.lasts = new Set([s]); + } + buildCfgBuilder() { + let stmts = []; + if (ts__namespace.isSourceFile(this.astRoot)) { + stmts = [...this.astRoot.statements]; + } + else if (ts__namespace.isFunctionDeclaration(this.astRoot) || + ts__namespace.isMethodDeclaration(this.astRoot) || + ts__namespace.isConstructorDeclaration(this.astRoot) || + ts__namespace.isGetAccessorDeclaration(this.astRoot) || + ts__namespace.isSetAccessorDeclaration(this.astRoot) || + ts__namespace.isFunctionExpression(this.astRoot) || + ts__namespace.isClassStaticBlockDeclaration(this.astRoot)) { + if (this.astRoot.body) { + stmts = [...this.astRoot.body.statements]; + } + else { + this.emptyBody = true; + } + } + else if (ts__namespace.isArrowFunction(this.astRoot)) { + if (ts__namespace.isBlock(this.astRoot.body)) { + stmts = [...this.astRoot.body.statements]; + } + } + else if (ts__namespace.isMethodSignature(this.astRoot) || + ts__namespace.isConstructSignatureDeclaration(this.astRoot) || + ts__namespace.isCallSignatureDeclaration(this.astRoot) || + ts__namespace.isFunctionTypeNode(this.astRoot)) { + this.emptyBody = true; + } + else if (ts__namespace.isModuleDeclaration(this.astRoot) && ts__namespace.isModuleBlock(this.astRoot.body)) { + stmts = [...this.astRoot.body.statements]; + } + if (!ModelUtils.isArkUIBuilderMethod(this.declaringMethod)) { + this.walkAST(this.entry, this.exit, stmts); + } + else { + this.handleBuilder(stmts); + } + if (ts__namespace.isArrowFunction(this.astRoot) && !ts__namespace.isBlock(this.astRoot.body)) { + this.buildStatementBuilder4ArrowFunction(this.astRoot.body); + } + this.addReturnInEmptyMethod(); + this.deleteExit(); + this.CfgBuilder2Array(this.entry); + this.addStmtBuilderPosition(); + this.buildBlocks(); + this.blocks = this.blocks.filter(b => b.stmts.length !== 0); + this.buildBlocksNextLast(); + this.addReturnStmt(); + } + handleBuilder(stmts) { + let lastStmt = this.entry; + for (const stmt of stmts) { + const stmtBuilder = new StatementBuilder('statement', stmt.getText(this.sourceFile), stmt, 0); + lastStmt.next = stmtBuilder; + stmtBuilder.lasts.add(lastStmt); + lastStmt = stmtBuilder; + } + lastStmt.next = this.exit; + this.exit.lasts.add(lastStmt); + } + isBodyEmpty() { + return this.emptyBody; + } + buildCfg() { + if (ts__namespace.isArrowFunction(this.astRoot) && !ts__namespace.isBlock(this.astRoot.body)) { + return this.buildCfgForSimpleArrowFunction(); + } + return this.buildNormalCfg(); + } + buildCfgForSimpleArrowFunction() { + const stmts = []; + const arkIRTransformer = new ArkIRTransformer(this.sourceFile, this.declaringMethod); + arkIRTransformer.prebuildStmts().forEach(stmt => stmts.push(stmt)); + const expressionBodyNode = this.astRoot.body; + const expressionBodyStmts = []; + let { value: expressionBodyValue, valueOriginalPositions: expressionBodyPositions, stmts: tempStmts, } = arkIRTransformer.tsNodeToValueAndStmts(expressionBodyNode); + tempStmts.forEach(stmt => expressionBodyStmts.push(stmt)); + if (IRUtils.moreThanOneAddress(expressionBodyValue)) { + ({ + value: expressionBodyValue, + valueOriginalPositions: expressionBodyPositions, + stmts: tempStmts, + } = arkIRTransformer.generateAssignStmtForValue(expressionBodyValue, expressionBodyPositions)); + tempStmts.forEach(stmt => expressionBodyStmts.push(stmt)); + } + const returnStmt = new ArkReturnStmt(expressionBodyValue); + returnStmt.setOperandOriginalPositions([expressionBodyPositions[0], ...expressionBodyPositions]); + expressionBodyStmts.push(returnStmt); + arkIRTransformer.mapStmtsToTsStmt(expressionBodyStmts, expressionBodyNode); + expressionBodyStmts.forEach(stmt => stmts.push(stmt)); + const cfg = new Cfg(); + const blockInCfg = new BasicBlock(); + blockInCfg.setId(0); + stmts.forEach(stmt => { + blockInCfg.addStmt(stmt); + stmt.setCfg(cfg); + }); + cfg.addBlock(blockInCfg); + cfg.setStartingStmt(stmts[0]); + return { + cfg: cfg, + locals: arkIRTransformer.getLocals(), + globals: arkIRTransformer.getGlobals(), + aliasTypeMap: arkIRTransformer.getAliasTypeMap(), + traps: [], + }; + } + buildNormalCfg() { + const { blockBuilderToCfgBlock, basicBlockSet, arkIRTransformer } = this.initializeBuild(); + const { blocksContainLoopCondition, blockBuildersBeforeTry, blockBuildersContainSwitch, valueAndStmtsOfSwitchAndCasesAll } = this.processBlocks(blockBuilderToCfgBlock, basicBlockSet, arkIRTransformer); + const currBlockId = this.blocks.length; + this.linkBasicBlocks(blockBuilderToCfgBlock); + this.adjustBlocks(blockBuilderToCfgBlock, blocksContainLoopCondition, basicBlockSet, blockBuildersContainSwitch, valueAndStmtsOfSwitchAndCasesAll, arkIRTransformer); + const trapBuilder = new TrapBuilder(); + const traps = trapBuilder.buildTraps(blockBuilderToCfgBlock, blockBuildersBeforeTry, arkIRTransformer, basicBlockSet); + const cfg = this.createCfg(blockBuilderToCfgBlock, basicBlockSet, currBlockId); + return { + cfg, + locals: arkIRTransformer.getLocals(), + globals: arkIRTransformer.getGlobals(), + aliasTypeMap: arkIRTransformer.getAliasTypeMap(), + traps, + }; + } + initializeBuild() { + const blockBuilderToCfgBlock = new Map(); + const basicBlockSet = new Set(); + const arkIRTransformer = new ArkIRTransformer(this.sourceFile, this.declaringMethod); + return { blockBuilderToCfgBlock, basicBlockSet, arkIRTransformer }; + } + processBlocks(blockBuilderToCfgBlock, basicBlockSet, arkIRTransformer) { + const blocksContainLoopCondition = new Set(); + const blockBuildersBeforeTry = new Set(); + const blockBuildersContainSwitch = []; + const valueAndStmtsOfSwitchAndCasesAll = []; + for (let i = 0; i < this.blocks.length; i++) { + const stmtsInBlock = []; + if (i === 0) { + arkIRTransformer.prebuildStmts().forEach(stmt => stmtsInBlock.push(stmt)); + } + const stmtsCnt = this.blocks[i].stmts.length; + if (this.blocks[i].stmts[stmtsCnt - 1].type === 'tryStatement') { + blockBuildersBeforeTry.add(this.blocks[i]); + } + for (const statementBuilder of this.blocks[i].stmts) { + if (statementBuilder.type === 'loopStatement') { + blocksContainLoopCondition.add(this.blocks[i]); + } + else if (statementBuilder instanceof SwitchStatementBuilder) { + blockBuildersContainSwitch.push(this.blocks[i]); + const valueAndStmtsOfSwitchAndCases = arkIRTransformer.switchStatementToValueAndStmts(statementBuilder.astNode); + valueAndStmtsOfSwitchAndCasesAll.push(valueAndStmtsOfSwitchAndCases); + continue; + } + if (statementBuilder.astNode && statementBuilder.code !== '') { + arkIRTransformer.tsNodeToStmts(statementBuilder.astNode).forEach(s => stmtsInBlock.push(s)); + } + else if (statementBuilder.code.startsWith('return')) { + stmtsInBlock.push(this.generateReturnStmt(arkIRTransformer)); + } + } + const blockInCfg = new BasicBlock(); + blockInCfg.setId(this.blocks[i].id); + for (const stmt of stmtsInBlock) { + blockInCfg.addStmt(stmt); + } + basicBlockSet.add(blockInCfg); + blockBuilderToCfgBlock.set(this.blocks[i], blockInCfg); + } + return { + blocksContainLoopCondition, + blockBuildersBeforeTry, + blockBuildersContainSwitch, + valueAndStmtsOfSwitchAndCasesAll, + }; + } + generateReturnStmt(arkIRTransformer) { + if (this.name === CONSTRUCTOR_NAME) { + this.declaringMethod.getSubSignature().setReturnType(arkIRTransformer.getThisLocal().getType()); + return new ArkReturnStmt(arkIRTransformer.getThisLocal()); + } + if (this.declaringMethod.getSubSignature().getReturnType() instanceof UnknownType && !this.declaringMethod.getAsteriskToken()) { + if (this.declaringMethod.containsModifier(ModifierType.ASYNC)) { + const promise = this.declaringMethod.getDeclaringArkFile().getScene().getSdkGlobal(PROMISE); + if (promise instanceof ArkClass) { + this.declaringMethod.getSubSignature().setReturnType(new ClassType(promise.getSignature())); + } + else { + this.declaringMethod.getSubSignature().setReturnType(new UnclearReferenceType(PROMISE, [VoidType.getInstance()])); + } + } + else { + this.declaringMethod.getSubSignature().setReturnType(VoidType.getInstance()); + } + } + return new ArkReturnVoidStmt(); + } + adjustBlocks(blockBuilderToCfgBlock, blocksContainLoopCondition, basicBlockSet, blockBuildersContainSwitch, valueAndStmtsOfSwitchAndCasesAll, arkIRTransformer) { + const loopBuilder = new LoopBuilder(); + loopBuilder.rebuildBlocksInLoop(blockBuilderToCfgBlock, blocksContainLoopCondition, basicBlockSet, this.blocks); + const switchBuilder = new SwitchBuilder(); + switchBuilder.buildSwitch(blockBuilderToCfgBlock, blockBuildersContainSwitch, valueAndStmtsOfSwitchAndCasesAll, arkIRTransformer, basicBlockSet); + const conditionalBuilder = new ConditionBuilder(); + conditionalBuilder.rebuildBlocksContainConditionalOperator(basicBlockSet, ModelUtils.isArkUIBuilderMethod(this.declaringMethod)); + } + createCfg(blockBuilderToCfgBlock, basicBlockSet, prevBlockId) { + let currBlockId = prevBlockId; + for (const blockBuilder of this.blocks) { + if (blockBuilder.id === -1) { + blockBuilder.id = currBlockId++; + const block = blockBuilderToCfgBlock.get(blockBuilder); + block.setId(blockBuilder.id); + } + } + const cfg = new Cfg(); + const startingBasicBlock = blockBuilderToCfgBlock.get(this.blocks[0]); + cfg.setStartingStmt(startingBasicBlock.getStmts()[0]); + currBlockId = 0; + for (const basicBlock of basicBlockSet) { + basicBlock.setId(currBlockId++); + cfg.addBlock(basicBlock); + } + for (const stmt of cfg.getStmts()) { + stmt.setCfg(cfg); + } + return cfg; + } + linkBasicBlocks(blockBuilderToCfgBlock) { + for (const [blockBuilder, cfgBlock] of blockBuilderToCfgBlock) { + for (const successorBlockBuilder of blockBuilder.nexts) { + if (!blockBuilderToCfgBlock.get(successorBlockBuilder)) { + continue; + } + const successorBlock = blockBuilderToCfgBlock.get(successorBlockBuilder); + cfgBlock.addSuccessorBlock(successorBlock); + } + for (const predecessorBlockBuilder of blockBuilder.lasts) { + if (!blockBuilderToCfgBlock.get(predecessorBlockBuilder)) { + continue; + } + const predecessorBlock = blockBuilderToCfgBlock.get(predecessorBlockBuilder); + cfgBlock.addPredecessorBlock(predecessorBlock); + } + } + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class BodyBuilder { + cfgBuilder; + globals; + constructor(methodSignature, sourceAstNode, declaringMethod, sourceFile) { + this.cfgBuilder = new CfgBuilder(sourceAstNode, methodSignature.getMethodSubSignature().getMethodName(), declaringMethod, sourceFile); + } + build() { + this.cfgBuilder.buildCfgBuilder(); + if (!this.cfgBuilder.isBodyEmpty()) { + const { cfg, locals, globals, aliasTypeMap, traps } = this.cfgBuilder.buildCfg(); + if (globals !== null) { + this.setGlobals(globals); + } + cfg.buildDefUseStmt(locals); + return new ArkBody(locals, cfg, aliasTypeMap, traps.length ? traps : undefined); + } + return null; + } + getCfgBuilder() { + return this.cfgBuilder; + } + getGlobals() { + return this.globals; + } + setGlobals(globals) { + this.globals = globals; + } + /** + * 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. + * childrenChain contains all nested method from the root node of the childrenChain. + * baseLocals are all locals defined in the outer function. + * allNestedLocals are collect all locals defined in all outer functions of this childrenChain. + * 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. + */ + findClosuresUsedInNested(childrenChain, baseLocals, allNestedLocals) { + let closuresRes = []; + const nestedMethod = childrenChain.parent; + let nestedGlobals = nestedMethod.getBodyBuilder()?.getGlobals(); + if (nestedGlobals !== undefined) { + for (let global of nestedGlobals.values()) { + const nestedLocal = allNestedLocals.get(global.getName()); + const closure = baseLocals.get(global.getName()); + if (nestedLocal === undefined && closure !== undefined) { + closuresRes.push(closure); + } + } + } + const children = childrenChain.children; + if (children === null) { + return closuresRes; + } + for (let chain of children) { + const nestedLocals = nestedMethod.getBody()?.getLocals(); + if (nestedLocals !== undefined) { + nestedLocals.forEach((value, key) => { + allNestedLocals.set(key, value); + }); + } + const closures = this.findClosuresUsedInNested(chain, baseLocals, allNestedLocals); + if (closures) { + closuresRes.push(...closures); + } + } + return closuresRes; + } + /** + * 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. + * 2. Create a lexical env local in the parent method, and pass it to root node of the childrenChain through the method signature. + * 3. Update the root node of the childrenChain to add parameterRef assign stmt and closureRef assign stmt. + * 4. Recursively do this for all nested method level by level. + */ + buildLexicalEnv(childrenChain, baseLocals, index) { + let usedClosures = this.findClosuresUsedInNested(childrenChain, baseLocals, new Map()); + const nestedMethod = childrenChain.parent; + const nestedSignature = nestedMethod.getImplementationSignature(); + if (nestedSignature !== null && usedClosures !== null && usedClosures.length > 0) { + let lexicalEnv = new LexicalEnvType(nestedSignature, usedClosures); + const closuresLocal = new Local(`${LEXICAL_ENV_NAME_PREFIX}${index++}`, lexicalEnv); + baseLocals.set(closuresLocal.getName(), closuresLocal); + this.updateNestedMethodWithClosures(nestedMethod, closuresLocal); + } + else if (usedClosures === null || usedClosures.length === 0) { + this.moveCurrentMethodLocalToGlobal(nestedMethod); + } + const nextNestedChains = childrenChain.children; + if (nextNestedChains === null) { + return index; + } + for (let nextChain of nextNestedChains) { + const newBaseLocals = nestedMethod.getBody()?.getLocals(); + if (newBaseLocals === undefined) { + return index; + } + index = this.buildLexicalEnv(nextChain, newBaseLocals, index); + } + return index; + } + /** + * Find out and tag all closures from globals, and remove closures from both globals and locals. + * Precondition: body build has been done. All locals, globals and closures are both set as Local in body, + * while potential globals and closures are also recorded in bodybuilder. + * Constraint: only the outermost function can call this method to recursively handle closures of itself as well as all nested methods. + */ + handleGlobalAndClosure() { + /** + * Step1: Handle the outermost function, take it as Level 0. + * 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. + */ + let outerMethod = this.getCfgBuilder().getDeclaringMethod(); + let outerGlobals = outerMethod.getBodyBuilder()?.getGlobals(); + outerMethod.freeBodyBuilder(); + let outerLocals = outerMethod.getBody()?.getLocals(); + if (outerGlobals !== undefined && outerLocals !== undefined) { + outerGlobals.forEach((value, key) => { + const local = outerLocals.get(key); + if (local !== undefined) { + value.addUsedStmts(local.getUsedStmts()); + outerLocals.delete(key); + } + }); + if (outerGlobals.size > 0) { + outerMethod.getBody()?.setUsedGlobals(outerGlobals); + } + } + let nestedMethodChains = this.generateNestedMethodChains(outerMethod).children; + if (nestedMethodChains === null || outerLocals === undefined) { + return; + } + let closuresIndex = 0; + for (let nestedChain of nestedMethodChains) { + /** + * Step2: Handle each nested function in Level 1 one by one. + * Find out all closures from Level 0 used by these Level 1 functions as well as all their children nested functions. + * This will be done level by level recursively. + */ + closuresIndex = this.buildLexicalEnv(nestedChain, outerLocals, closuresIndex); + /** + * Step3: Delete old globals which are recognized as closures, then the rest globals are the true globals. + * The redundancy locals should be deleted but the used stmts of them should be restored to globals. + * This will be done level by level recursively. + */ + this.reorganizeGlobalAndLocal(nestedChain); + /** + * Step4: Infer UnclearReferenceType to check whether it is the type alias define in its parent function.. + */ + this.inferTypesDefineInOuter(outerMethod, nestedChain); + /** + * Step5: For each nested function, find out whether it is called by its parent function and update the related locals, globals and stmts. + */ + this.updateNestedMethodUsedInOuter(nestedChain); + this.freeBodyBuilder(nestedChain); + } + } + freeBodyBuilder(nestedChain) { + nestedChain.parent.freeBodyBuilder(); + const childrenChains = nestedChain.children; + if (childrenChains === null) { + return; + } + for (const chain of childrenChains) { + this.freeBodyBuilder(chain); + } + } + updateLocalTypesWithTypeAlias(locals, typeAliases) { + for (let local of locals.values()) { + const newType = this.inferUnclearReferenceTypeWithTypeAlias(local.getType(), typeAliases); + if (newType !== null) { + local.setType(newType); + } + } + } + inferUnclearReferenceTypeWithTypeAlias(localType, typeAliases) { + if (localType instanceof ArrayType && localType.getBaseType() instanceof UnclearReferenceType) { + const typeAlias = typeAliases.get(localType.getBaseType().getName()); + if (typeAlias !== undefined) { + localType.setBaseType(typeAlias[0]); + return localType; + } + return null; + } + if (localType instanceof UnionType) { + const optionTypes = localType.getTypes(); + for (let i = 0; i < optionTypes.length; i++) { + const newType = this.inferUnclearReferenceTypeWithTypeAlias(optionTypes[i], typeAliases); + if (newType !== null) { + optionTypes[i] = newType; + } + } + return localType; + } + if (localType instanceof UnclearReferenceType) { + const typeAlias = typeAliases.get(localType.getName()); + if (typeAlias !== undefined) { + return typeAlias[0]; + } + } + return null; + } + generateNestedMethodChains(outerMethod) { + let candidateMethods = []; + outerMethod + .getDeclaringArkClass() + .getMethods() + .forEach(method => { + if (method.getName().startsWith(NAME_PREFIX) && method.getName().endsWith(`${NAME_DELIMITER}${outerMethod.getName()}`)) { + candidateMethods.push(method); + } + }); + const childrenChains = this.getNestedChildrenChains(outerMethod, candidateMethods); + if (childrenChains.length > 0) { + return { parent: outerMethod, children: childrenChains }; + } + return { parent: outerMethod, children: null }; + } + getNestedChildrenChains(parentMethod, candidateMethods) { + let nestedMethodChain = []; + for (let method of candidateMethods) { + const outerMethodSignature = method.getOuterMethod()?.getSignature(); + if (outerMethodSignature !== undefined && methodSignatureCompare(parentMethod.getSignature(), outerMethodSignature)) { + const childrenChains = this.getNestedChildrenChains(method, candidateMethods); + if (childrenChains.length > 0) { + nestedMethodChain.push({ parent: method, children: childrenChains }); + } + else { + nestedMethodChain.push({ parent: method, children: null }); + } + } + } + return nestedMethodChain; + } + moveCurrentMethodLocalToGlobal(method) { + const globals = method.getBodyBuilder()?.getGlobals(); + const locals = method.getBody()?.getLocals(); + if (locals === undefined || globals === undefined) { + return; + } + globals.forEach((value, key) => { + const local = locals.get(key); + if (local !== undefined) { + value.addUsedStmts(local.getUsedStmts()); + locals.delete(key); + } + }); + if (globals.size > 0) { + method.getBody()?.setUsedGlobals(globals); + } + } + reorganizeGlobalAndLocal(nestedChain) { + const nestedMethod = nestedChain.parent; + const params = nestedMethod.getSubSignature().getParameters(); + const globals = nestedMethod.getBodyBuilder()?.getGlobals(); + if (params.length > 0 && params[0].getType() instanceof LexicalEnvType && globals !== undefined) { + const closures = params[0].getType().getClosures(); + for (let closure of closures) { + globals.delete(closure.getName()); + } + } + this.moveCurrentMethodLocalToGlobal(nestedMethod); + const childrenChains = nestedChain.children; + if (childrenChains === null) { + return; + } + for (const chain of childrenChains) { + this.reorganizeGlobalAndLocal(chain); + } + } + // 对嵌套函数中的UnclearReferenceType类型的变量进行类型推导,类型是否为外层函数中定义的类型别名 + inferTypesDefineInOuter(outerMethod, childrenChain) { + const typeAliases = outerMethod.getBody()?.getAliasTypeMap(); + const nestedLocals = childrenChain.parent.getBody()?.getLocals(); + if (typeAliases !== undefined && nestedLocals !== undefined) { + this.updateLocalTypesWithTypeAlias(nestedLocals, typeAliases); + } + const childrenChains = childrenChain.children; + if (childrenChains === null) { + return; + } + for (const chain of childrenChains) { + this.inferTypesDefineInOuter(childrenChain.parent, chain); + } + } + updateNestedMethodUsedInOuter(nestedChain) { + const nestedMethod = nestedChain.parent; + const outerMethod = nestedMethod.getOuterMethod(); + if (outerMethod === undefined) { + return; + } + const outerLocals = outerMethod.getBody()?.getLocals(); + if (outerLocals !== undefined) { + for (let local of outerLocals.values()) { + if (local.getType() instanceof LexicalEnvType && + methodSignatureCompare(local.getType().getNestedMethod(), nestedMethod.getSignature())) { + this.updateOuterMethodWithClosures(outerMethod, nestedMethod, local); + break; + } + } + } + const nestedMethodName = nestedMethod.getName(); + const originalMethodName = this.getOriginalNestedMethodName(nestedMethodName) ?? ''; + const outerGlobals = outerMethod.getBody()?.getUsedGlobals(); + const callGlobal = outerGlobals?.get(nestedMethodName) ?? outerGlobals?.get(originalMethodName); + if (callGlobal !== undefined && callGlobal instanceof GlobalRef && callGlobal.getRef() === null) { + const fieldSignature = new FieldSignature(nestedMethodName, nestedMethod.getDeclaringArkClass().getSignature(), new FunctionType(nestedMethod.getSignature())); + callGlobal.setRef(new ArkStaticFieldRef(fieldSignature)); + } + const childrenChains = nestedChain.children; + if (childrenChains === null) { + return; + } + for (const chain of childrenChains) { + this.updateNestedMethodUsedInOuter(chain); + } + } + updateNestedMethodWithClosures(nestedMethod, closuresLocal) { + if (!(closuresLocal.getType() instanceof LexicalEnvType)) { + return; + } + const declareSignatures = nestedMethod.getDeclareSignatures(); + declareSignatures?.forEach((signature, index) => { + nestedMethod.setDeclareSignatureWithIndex(this.createNewSignatureWithClosures(closuresLocal, signature), index); + }); + const implementSignature = nestedMethod.getImplementationSignature(); + if (implementSignature !== null) { + nestedMethod.setImplementationSignature(this.createNewSignatureWithClosures(closuresLocal, implementSignature)); + } + this.addClosureParamsAssignStmts(closuresLocal, nestedMethod); + } + updateOuterMethodWithClosures(outerMethod, nestedMethod, closuresLocal) { + const nestedMethodName = nestedMethod.getName(); + const nestedMethodLocal = outerMethod.getBody()?.getLocals().get(nestedMethodName); + if (nestedMethodLocal !== undefined) { + this.updateLocalInfoWithClosures(nestedMethodLocal, outerMethod, nestedMethod, closuresLocal); + } + else { + const nestedMethodGlobal = outerMethod.getBody()?.getUsedGlobals()?.get(nestedMethodName); + if (nestedMethodGlobal !== undefined && nestedMethodGlobal instanceof GlobalRef) { + this.updateGlobalInfoWithClosures(nestedMethodGlobal, outerMethod, nestedMethod, closuresLocal); + } + } + const originalMethodName = this.getOriginalNestedMethodName(nestedMethodName); + if (originalMethodName === null) { + return; + } + const originalMethodLocal = outerMethod.getBody()?.getLocals().get(originalMethodName); + if (originalMethodLocal !== undefined) { + this.updateLocalInfoWithClosures(originalMethodLocal, outerMethod, nestedMethod, closuresLocal); + } + else { + const originalMethodGlobal = outerMethod.getBody()?.getUsedGlobals()?.get(originalMethodName); + if (originalMethodGlobal !== undefined && originalMethodGlobal instanceof GlobalRef) { + this.updateGlobalInfoWithClosures(originalMethodGlobal, outerMethod, nestedMethod, closuresLocal); + } + } + } + getOriginalNestedMethodName(nestedMethodName) { + if (nestedMethodName.startsWith(NAME_PREFIX) && nestedMethodName.includes(NAME_DELIMITER)) { + const nameComponents = nestedMethodName.slice(1).split(NAME_DELIMITER); + if (nameComponents.length > 1) { + return nameComponents[0]; + } + } + return null; + } + updateGlobalInfoWithClosures(globalRef, outerMethod, nestedMethod, closuresLocal) { + if (globalRef.getRef() !== null) { + return; + } + const methodSignature = nestedMethod.getImplementationSignature(); + if (methodSignature === null) { + return; + } + const lexicalEnv = closuresLocal.getType(); + if (!(lexicalEnv instanceof LexicalEnvType)) { + return; + } + const fieldSignature = new FieldSignature(methodSignature.getMethodSubSignature().getMethodName(), methodSignature.getDeclaringClassSignature(), new ClosureType(lexicalEnv, methodSignature)); + globalRef.setRef(new ArkStaticFieldRef(fieldSignature)); + this.updateAbstractInvokeExprWithClosures(globalRef, outerMethod.getSignature(), nestedMethod.getSignature(), closuresLocal); + } + updateLocalInfoWithClosures(local, outerMethod, nestedMethod, closuresLocal) { + const localType = local.getType(); + if (!(localType instanceof FunctionType)) { + return; + } + const lexicalEnv = closuresLocal.getType(); + if (!(lexicalEnv instanceof LexicalEnvType)) { + return; + } + // 更新local的类型为ClosureType,methodSignature为内层嵌套函数 + const nestedMethodSignature = nestedMethod.getImplementationSignature(); + if (nestedMethodSignature !== null) { + local.setType(new ClosureType(lexicalEnv, nestedMethodSignature, localType.getRealGenericTypes())); + } + else { + local.setType(new ClosureType(lexicalEnv, localType.getMethodSignature(), localType.getRealGenericTypes())); + } + this.updateAbstractInvokeExprWithClosures(local, outerMethod.getSignature(), nestedMethod.getSignature(), closuresLocal); + } + // 更新所有stmt中调用内层函数处的AbstractInvokeExpr中的函数签名和实参args,加入闭包参数 + // 更新所有stmt中定义的函数指针的usedStmt中的函数签名和实参args,加入闭包参数 + updateAbstractInvokeExprWithClosures(value, outerMethodSignature, nestedMethodSignature, closuresLocal) { + for (const usedStmt of value.getUsedStmts()) { + if (usedStmt instanceof ArkInvokeStmt) { + this.updateSignatureAndArgsInArkInvokeExpr(usedStmt, nestedMethodSignature, closuresLocal); + } + else if (usedStmt instanceof ArkAssignStmt) { + const rightOp = usedStmt.getRightOp(); + if (rightOp instanceof AbstractInvokeExpr) { + this.updateSignatureAndArgsInArkInvokeExpr(usedStmt, nestedMethodSignature, closuresLocal); + } + const leftOp = usedStmt.getLeftOp(); + if (leftOp instanceof Local) { + leftOp.setType(rightOp.getType()); + } + } + else if (usedStmt instanceof ArkReturnStmt) { + outerMethodSignature.getMethodSubSignature().setReturnType(value.getType()); + } + const defValue = usedStmt.getDef(); + if (defValue === null) { + continue; + } + if ((defValue instanceof Local || defValue instanceof GlobalRef) && defValue.getType() instanceof FunctionType) { + this.updateAbstractInvokeExprWithClosures(defValue, outerMethodSignature, nestedMethodSignature, closuresLocal); + } + } + } + createNewSignatureWithClosures(closuresLocal, oldSignature) { + let oldSubSignature = oldSignature.getMethodSubSignature(); + const params = oldSubSignature.getParameters(); + const closuresParam = new MethodParameter(); + closuresParam.setName(closuresLocal.getName()); + closuresParam.setType(closuresLocal.getType()); + params.unshift(closuresParam); + let newSubSignature = new MethodSubSignature(oldSubSignature.getMethodName(), params, oldSubSignature.getReturnType(), oldSubSignature.isStatic()); + return new MethodSignature(oldSignature.getDeclaringClassSignature(), newSubSignature); + } + updateSignatureAndArgsInArkInvokeExpr(stmt, methodSignature, closuresLocal) { + let expr; + if (stmt instanceof ArkInvokeStmt) { + expr = stmt.getInvokeExpr(); + } + else { + const rightOp = stmt.getRightOp(); + if (!(rightOp instanceof AbstractInvokeExpr)) { + return; + } + expr = rightOp; + } + const exprMethodName = expr.getMethodSignature().getMethodSubSignature().getMethodName(); + const nestedMethodName = methodSignature.getMethodSubSignature().getMethodName(); + if (exprMethodName === nestedMethodName) { + expr.setMethodSignature(this.createNewSignatureWithClosures(closuresLocal, methodSignature)); + expr.getArgs().unshift(closuresLocal); + closuresLocal.addUsedStmt(stmt); + return; + } + const originalMethodName = this.getOriginalNestedMethodName(nestedMethodName); + if (originalMethodName !== null) { + if (exprMethodName === originalMethodName || expr instanceof ArkPtrInvokeExpr) { + expr.setMethodSignature(methodSignature); + expr.getArgs().unshift(closuresLocal); + closuresLocal.addUsedStmt(stmt); + } + } + } + addClosureParamsAssignStmts(closuresParam, method) { + const lexicalEnv = closuresParam.getType(); + if (!(lexicalEnv instanceof LexicalEnvType)) { + return; + } + const closures = lexicalEnv.getClosures(); + if (closures.length === 0) { + return; + } + const oldParamRefs = method.getParameterRefs(); + let body = method.getBody(); + if (body === undefined) { + return; + } + let stmts = Array.from(body.getCfg().getBlocks())[0].getStmts(); + let index = 0; + const parameterRef = new ArkParameterRef(index, lexicalEnv); + const closuresLocal = new Local(closuresParam.getName(), lexicalEnv); + body.addLocal(closuresLocal.getName(), closuresLocal); + let assignStmt = new ArkAssignStmt(closuresLocal, parameterRef); + assignStmt.setCfg(body.getCfg()); + stmts.splice(index, 0, assignStmt); + closuresLocal.setDeclaringStmt(assignStmt); + oldParamRefs?.forEach(paramRef => { + index++; + paramRef.setIndex(index); + }); + for (let closure of closures) { + let local = body.getLocals().get(closure.getName()); + if (local === undefined) { + local = new Local(closure.getName(), closure.getType()); + body.addLocal(local.getName(), local); + } + else { + local.setType(closure.getType()); + } + index++; + const closureFieldRef = new ClosureFieldRef(closuresParam, closure.getName(), closure.getType()); + let assignStmt = new ArkAssignStmt(local, closureFieldRef); + assignStmt.setCfg(body.getCfg()); + stmts.splice(index, 0, assignStmt); + local.setDeclaringStmt(assignStmt); + closuresLocal.addUsedStmt(assignStmt); + } + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$p = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'ViewTreeBuilder'); +const COMPONENT_CREATE_FUNCTIONS$1 = new Set([COMPONENT_CREATE_FUNCTION, COMPONENT_BRANCH_FUNCTION]); +function backtraceLocalInitValue$1(value) { + let stmt = value.getDeclaringStmt(); + if (stmt instanceof ArkAssignStmt) { + let rightOp = stmt.getRightOp(); + if (rightOp instanceof Local) { + return backtraceLocalInitValue$1(rightOp); + } + else if (rightOp instanceof ArkInstanceFieldRef && rightOp.getBase().getName().startsWith(TEMP_LOCAL_PREFIX)) { + return backtraceLocalInitValue$1(rightOp.getBase()); + } + else if (rightOp instanceof ArkArrayRef) { + return backtraceLocalInitValue$1(rightOp.getBase()); + } + return rightOp; + } + return value; +} +function parseObjectLiteral$1(objectLiteralCls, scene) { + let map = new Map(); + if (objectLiteralCls?.getCategory() !== ClassCategory.OBJECT) { + return map; + } + objectLiteralCls?.getFields().forEach(field => { + let stmts = field.getInitializer(); + if (stmts.length === 0) { + return; + } + let assignStmt = stmts[stmts.length - 1]; + if (!(assignStmt instanceof ArkAssignStmt)) { + return; + } + let value = assignStmt.getRightOp(); + if (value instanceof Local) { + value = backtraceLocalInitValue$1(value); + } + map.set(field, value); + if (value instanceof ArkNewExpr) { + let subCls = ModelUtils.getArkClassInBuild(scene, value.getClassType()); + let childMap = parseObjectLiteral$1(subCls, scene); + if (childMap) { + map.set(field, childMap); + } + } + }); + return map; +} +let StateValuesUtils$1 = class StateValuesUtils { + declaringArkClass; + constructor(declaringArkClass) { + this.declaringArkClass = declaringArkClass; + } + static getInstance(declaringArkClass) { + return new StateValuesUtils(declaringArkClass); + } + parseStmtUsesStateValues(stmt, uses = new Set(), wholeMethod = false, visitor = new Set()) { + if (visitor.has(stmt)) { + return uses; + } + visitor.add(stmt); + let values = stmt.getUses(); + if (stmt instanceof ArkAssignStmt) { + values.push(stmt.getLeftOp()); + } + for (const v of values) { + this.parseValueUsesStateValues(v, uses, wholeMethod, visitor); + } + return uses; + } + objectLiteralMapUsedStateValues(uses, map) { + for (const [_, value] of map) { + if (value instanceof ArkInstanceFieldRef) { + let srcField = this.declaringArkClass.getFieldWithName(value.getFieldName()); + let decorators = srcField?.getStateDecorators(); + if (srcField && decorators && decorators.length > 0) { + uses.add(srcField); + } + } + else if (value instanceof Map) { + this.objectLiteralMapUsedStateValues(uses, value); + } + else if (value instanceof ArkNormalBinopExpr || value instanceof ArkConditionExpr) { + this.parseValueUsesStateValues(value.getOp1(), uses); + this.parseValueUsesStateValues(value.getOp2(), uses); + } + } + } + parseObjectUsedStateValues(type, uses = new Set()) { + if (!(type instanceof ClassType)) { + return uses; + } + let cls = ModelUtils.getArkClassInBuild(this.declaringArkClass.getDeclaringArkFile().getScene(), type); + let map = parseObjectLiteral$1(cls, this.declaringArkClass.getDeclaringArkFile().getScene()); + this.objectLiteralMapUsedStateValues(uses, map); + return uses; + } + parseMethodUsesStateValues(methodSignature, uses, visitor = new Set()) { + if (visitor.has(methodSignature)) { + return; + } + visitor.add(methodSignature); + let method = this.declaringArkClass.getDeclaringArkFile().getScene().getMethod(methodSignature); + if (!method) { + return; + } + let stmts = method.getCfg()?.getStmts(); + if (!stmts) { + return; + } + for (const stmt of stmts) { + this.parseStmtUsesStateValues(stmt, uses, true, visitor); + } + } + parseValueUsesStateValues(v, uses = new Set(), wholeMethod = false, visitor = new Set()) { + if (v instanceof ArkInstanceFieldRef) { + let field = this.declaringArkClass.getField(v.getFieldSignature()); + let decorators = field?.getStateDecorators(); + if (field && decorators && decorators.length > 0) { + uses.add(field); + } + } + else if (v instanceof ArkInstanceInvokeExpr) { + this.parseMethodUsesStateValues(v.getMethodSignature(), uses, visitor); + } + else if (v instanceof Local) { + if (v.getName() === 'this') { + return uses; + } + let type = v.getType(); + if (type instanceof FunctionType) { + this.parseMethodUsesStateValues(type.getMethodSignature(), uses, visitor); + return uses; + } + this.parseObjectUsedStateValues(type, uses); + let declaringStmt = v.getDeclaringStmt(); + if (!wholeMethod && declaringStmt) { + this.parseStmtUsesStateValues(declaringStmt, uses, wholeMethod, visitor); + } + } + return uses; + } +}; +var ViewTreeNodeType; +(function (ViewTreeNodeType) { + ViewTreeNodeType[ViewTreeNodeType["SystemComponent"] = 0] = "SystemComponent"; + ViewTreeNodeType[ViewTreeNodeType["CustomComponent"] = 1] = "CustomComponent"; + ViewTreeNodeType[ViewTreeNodeType["Builder"] = 2] = "Builder"; + ViewTreeNodeType[ViewTreeNodeType["BuilderParam"] = 3] = "BuilderParam"; +})(ViewTreeNodeType || (ViewTreeNodeType = {})); +class ViewTreeNodeImpl { + name; + stmts; + attributes; + stateValues; + parent; + children; + classSignature; + signature; + stateValuesTransfer; + builderParam; + builder; + type; + constructor(name) { + this.name = name; + this.attributes = new Map(); + this.stmts = this.attributes; + this.stateValues = new Set(); + this.parent = null; + this.children = []; + this.type = ViewTreeNodeType.SystemComponent; + } + /** + * Whether the node type is Builder. + * @returns true: node is Builder, false others. + */ + isBuilder() { + return this.type === ViewTreeNodeType.Builder; + } + /** + * @internal + */ + isBuilderParam() { + return this.type === ViewTreeNodeType.BuilderParam; + } + /** + * Whether the node type is custom component. + * @returns true: node is custom component, false others. + */ + isCustomComponent() { + return this.type === ViewTreeNodeType.CustomComponent; + } + /** + * walk node and node's children + * @param selector Node selector function, return true skipping the follow-up nodes. + * @returns + * - true: There are nodes that meet the selector. + * - false: does not exist. + */ + walk(selector, visitor = new Set()) { + if (visitor.has(this)) { + return false; + } + let ret = selector(this); + visitor.add(this); + for (const child of this.children) { + ret = ret || child.walk(selector, visitor); + if (ret) { + break; + } + } + return ret; + } + static createCustomComponent() { + let instance = new ViewTreeNodeImpl(COMPONENT_CUSTOMVIEW); + instance.type = ViewTreeNodeType.CustomComponent; + return instance; + } + static createBuilderNode() { + let instance = new ViewTreeNodeImpl(BUILDER_DECORATOR); + instance.type = ViewTreeNodeType.Builder; + return instance; + } + static createBuilderParamNode() { + let instance = new ViewTreeNodeImpl(BUILDER_PARAM_DECORATOR); + instance.type = ViewTreeNodeType.BuilderParam; + return instance; + } + changeBuilderParam2BuilderNode(builder) { + this.name = BUILDER_DECORATOR; + this.type = ViewTreeNodeType.Builder; + this.signature = builder.getSignature(); + this.classSignature = this.signature; + const root = builder.getViewTree()?.getRoot(); + if (root) { + for (let child of root.children) { + this.children.push(child); + } + } + else { + logger$p.error(`ViewTree->changeBuilderParam2BuilderNode ${builder.getSignature().toString()} @Builder viewtree fail.`); + } + } + hasBuilderParam() { + return this.walk(item => { + return item.isBuilderParam(); + }); + } + clone(parent, map = new Map()) { + let newNode = new ViewTreeNodeImpl(this.name); + newNode.attributes = this.attributes; + newNode.stmts = newNode.attributes; + newNode.stateValues = this.stateValues; + newNode.parent = parent; + newNode.type = this.type; + newNode.signature = this.signature; + newNode.classSignature = newNode.signature; + newNode.builderParam = this.builderParam; + newNode.builder = this.builder; + map.set(this, newNode); + for (const child of this.children) { + if (map.has(child)) { + newNode.children.push(map.get(child)); + } + else { + newNode.children.push(child.clone(newNode, map)); + } + } + return newNode; + } + addStmt(tree, stmt) { + this.parseAttributes(stmt); + if (this.name !== COMPONENT_FOR_EACH && this.name !== COMPONENT_LAZY_FOR_EACH) { + this.parseStateValues(tree, stmt); + } + } + parseAttributes(stmt) { + let expr; + if (stmt instanceof ArkAssignStmt) { + let op = stmt.getRightOp(); + if (op instanceof ArkInstanceInvokeExpr) { + expr = op; + } + else if (op instanceof ArkStaticInvokeExpr) { + expr = op; + } + } + else if (stmt instanceof ArkInvokeStmt) { + let invoke = stmt.getInvokeExpr(); + if (invoke instanceof ArkInstanceInvokeExpr) { + expr = invoke; + } + else if (invoke instanceof ArkStaticInvokeExpr) { + expr = invoke; + } + } + if (expr) { + let key = expr.getMethodSignature().getMethodSubSignature().getMethodName(); + let relationValues = []; + for (const arg of expr.getArgs()) { + if (arg instanceof Local) { + this.getBindValues(arg, relationValues); + } + else if (arg instanceof Constant) { + relationValues.push(arg); + } + } + this.attributes.set(key, [stmt, relationValues]); + } + } + getBindValues(local, relationValues, visitor = new Set()) { + if (visitor.has(local)) { + return; + } + visitor.add(local); + const stmt = local.getDeclaringStmt(); + if (!stmt) { + let type = local.getType(); + if (type instanceof FunctionType) { + relationValues.push(type.getMethodSignature()); + } + return; + } + for (const v of stmt.getUses()) { + if (v instanceof Constant) { + relationValues.push(v); + } + else if (v instanceof ArkInstanceFieldRef) { + relationValues.push(v); + } + else if (v instanceof Local) { + this.getBindValues(v, relationValues, visitor); + } + } + } + parseStateValues(tree, stmt) { + let stateValues = StateValuesUtils$1.getInstance(tree.getDeclaringArkClass()).parseStmtUsesStateValues(stmt); + stateValues.forEach(field => { + this.stateValues.add(field); + tree.addStateValue(field, this); + }, this); + } +} +class TreeNodeStack { + root = null; + stack; + constructor() { + this.stack = []; + } + /** + * @internal + */ + push(node) { + let parent = this.getParent(); + node.parent = parent; + this.stack.push(node); + if (parent === null || parent === undefined) { + this.root = node; + } + else { + parent.children.push(node); + } + } + /** + * @internal + */ + pop() { + this.stack.pop(); + } + /** + * @internal + */ + top() { + return this.isEmpty() ? null : this.stack[this.stack.length - 1]; + } + /** + * @internal + */ + isEmpty() { + return this.stack.length === 0; + } + /** + * @internal + */ + popAutomicComponent(name) { + if (this.isEmpty()) { + return; + } + let node = this.stack[this.stack.length - 1]; + if (name !== node.name && !this.isContainer(node.name)) { + this.stack.pop(); + } + } + /** + * @internal + */ + popComponentExpect(name) { + for (let i = this.stack.length - 1; i >= 0; i--) { + if (this.stack[i].name !== name) { + this.stack.pop(); + } + else { + break; + } + } + return this; + } + getParent() { + if (this.stack.length === 0) { + return null; + } + let node = this.stack[this.stack.length - 1]; + if (!this.isContainer(node.name)) { + this.stack.pop(); + } + return this.stack[this.stack.length - 1]; + } + isContainer(name) { + return isEtsContainerComponent(name) || SPECIAL_CONTAINER_COMPONENT.has(name) || name === BUILDER_DECORATOR; + } +} +class ViewTreeImpl extends TreeNodeStack { + render; + buildViewStatus; + stateValues; + fieldTypes; + /** + * @internal + */ + constructor(render) { + super(); + this.render = render; + this.stateValues = new Map(); + this.fieldTypes = new Map(); + this.buildViewStatus = false; + } + /** + * ViewTree root node. + * @returns root node + */ + getRoot() { + this.buildViewTree(); + return this.root; + } + /** + * Map of the component controlled by the state variable + * @returns + */ + getStateValues() { + this.buildViewTree(); + return this.stateValues; + } + /** + * @deprecated Use {@link getStateValues} instead. + */ + isClassField(name) { + return this.fieldTypes.has(name); + } + /** + * @deprecated Use {@link getStateValues} instead. + */ + getClassFieldType(name) { + return this.fieldTypes.get(name); + } + /** + * @internal + */ + buildViewTree() { + if (!this.render || this.isInitialized()) { + return; + } + this.buildViewStatus = true; + this.loadClasssFieldTypes(); + if (this.render.hasBuilderDecorator()) { + let node = ViewTreeNodeImpl.createBuilderNode(); + node.signature = this.render.getSignature(); + node.classSignature = node.signature; + this.push(node); + } + if (this.render.getCfg()) { + this.buildViewTreeFromCfg(this.render.getCfg()); + } + } + /** + * @internal + */ + isInitialized() { + return this.root != null || this.buildViewStatus; + } + /** + * @internal + */ + addStateValue(field, node) { + if (!this.stateValues.has(field)) { + this.stateValues.set(field, new Set()); + } + let sets = this.stateValues.get(field); + sets?.add(node); + } + /** + * @internal + */ + isCreateFunc(name) { + return COMPONENT_CREATE_FUNCTIONS$1.has(name); + } + loadClasssFieldTypes() { + for (const field of this.render.getDeclaringArkClass().getFields()) { + let decorators = field.getStateDecorators(); + if (decorators.length > 0) { + if (decorators.length === 1) { + this.fieldTypes.set(field.getName(), decorators[0]); + } + else { + this.fieldTypes.set(field.getName(), decorators[0]); + } + } + else { + this.fieldTypes.set(field.getName(), field.getSignature().getType()); + } + } + } + /** + * @internal + */ + getDeclaringArkClass() { + return this.render.getDeclaringArkClass(); + } + /** + * @internal + */ + findMethod(methodSignature) { + let method = this.render.getDeclaringArkFile().getScene().getMethod(methodSignature); + if (method) { + return method; + } + // class + method = this.getDeclaringArkClass().getMethod(methodSignature); + if (method) { + return method; + } + return this.findMethodWithName(methodSignature.getMethodSubSignature().getMethodName()); + } + /** + * @internal + */ + findMethodWithName(name) { + let method = this.getDeclaringArkClass().getMethodWithName(name); + if (method) { + return method; + } + // namespace + this.getDeclaringArkClass() + .getDeclaringArkNamespace() + ?.getAllMethodsUnderThisNamespace() + .forEach(value => { + if (value.getName() === name) { + method = value; + } + }); + if (method) { + return method; + } + this.getDeclaringArkClass() + .getDeclaringArkFile() + .getAllNamespacesUnderThisFile() + .forEach(namespace => { + namespace.getAllMethodsUnderThisNamespace().forEach(value => { + if (value.getName() === name) { + method = value; + } + }); + }); + return method; + } + /** + * @internal + */ + findClass(classSignature) { + return ModelUtils.getClass(this.render, classSignature); + } + findBuilderMethod(value) { + let method; + if (value instanceof ArkInstanceFieldRef) { + method = this.findMethodWithName(value.getFieldName()); + } + else if (value instanceof ArkStaticInvokeExpr) { + method = this.findMethod(value.getMethodSignature()); + } + else if (value instanceof Local && value.getType() instanceof FunctionType) { + method = this.findMethod(value.getType().getMethodSignature()); + } + else if (value instanceof Local) { + method = this.findMethodWithName(value.getName()); + } + if (method && !method.hasBuilderDecorator()) { + method = this.findMethodInvokeBuilderMethod(method); + } + return method; + } + /** + * @internal + */ + addBuilderNode(method) { + let builderViewTree = method.getViewTree(); + if (!builderViewTree || !builderViewTree.getRoot()) { + logger$p.error(`ViewTree->addBuilderNode ${method.getSignature().toString()} build viewtree fail.`); + // add empty node + let node = ViewTreeNodeImpl.createBuilderNode(); + node.signature = method.getSignature(); + node.classSignature = node.signature; + this.push(node); + this.pop(); + return node; + } + let root = builderViewTree.getRoot(); + this.push(root); + if (method.getDeclaringArkClass() === this.render.getDeclaringArkClass()) { + for (const [field, nodes] of builderViewTree.getStateValues()) { + for (const node of nodes) { + this.addStateValue(field, node); + } + } + } + this.pop(); + return root; + } + /** + * @internal + */ + addCustomComponentNode(cls, arg, builder) { + let node = ViewTreeNodeImpl.createCustomComponent(); + node.signature = cls.getSignature(); + node.classSignature = node.signature; + node.stateValuesTransfer = this.parseObjectLiteralExpr(cls, arg, builder); + if (arg instanceof Local && arg.getType()) { + let stateValues = StateValuesUtils$1.getInstance(this.getDeclaringArkClass()).parseObjectUsedStateValues(arg.getType()); + stateValues.forEach(field => { + node.stateValues.add(field); + this.addStateValue(field, node); + }); + } + this.push(node); + let componentViewTree = cls.getViewTree(); + if (!componentViewTree || !componentViewTree.getRoot()) { + logger$p.error(`ViewTree->addCustomComponentNode ${cls.getSignature().toString()} build viewtree fail.`); + return node; + } + let root = componentViewTree.getRoot(); + if (root.hasBuilderParam()) { + root = this.cloneBuilderParamNode(node, root); + } + node.children.push(root); + return node; + } + cloneBuilderParamNode(node, root) { + root = root.clone(node); + if (node.stateValuesTransfer) { + root.walk(item => { + let child = item; + if (!child.isBuilderParam() || !child.builderParam) { + return false; + } + let method = node.stateValuesTransfer?.get(child.builderParam); + if (method) { + child.changeBuilderParam2BuilderNode(method); + } + return false; + }); + } + return root; + } + /** + * @internal + */ + addBuilderParamNode(field) { + let node = ViewTreeNodeImpl.createBuilderParamNode(); + node.builderParam = field; + this.push(node); + this.pop(); + return node; + } + /** + * @internal + */ + addSystemComponentNode(name) { + let node = new ViewTreeNodeImpl(name); + this.push(node); + return node; + } + findMethodInvokeBuilderMethod(method) { + let stmts = method.getCfg()?.getStmts(); + if (!stmts) { + return undefined; + } + for (const stmt of stmts) { + let expr; + if (stmt instanceof ArkInvokeStmt) { + expr = stmt.getInvokeExpr(); + } + else if (stmt instanceof ArkAssignStmt) { + let rightOp = stmt.getRightOp(); + if (rightOp instanceof ArkInstanceInvokeExpr || rightOp instanceof ArkStaticInvokeExpr) { + expr = rightOp; + } + } + if (expr === undefined) { + continue; + } + let method = this.findMethod(expr.getMethodSignature()); + if (method?.hasBuilderDecorator()) { + return method; + } + } + return undefined; + } + parseFieldInObjectLiteral(field, cls, transferMap) { + let dstField = cls.getFieldWithName(field.getName()); + if (dstField?.getStateDecorators().length === 0 && !dstField?.hasBuilderParamDecorator()) { + return; + } + let stmts = field.getInitializer(); + if (stmts.length === 0) { + return; + } + let assignStmt = stmts[stmts.length - 1]; + if (!(assignStmt instanceof ArkAssignStmt)) { + return; + } + let value = assignStmt.getRightOp(); + if (value instanceof Local) { + value = backtraceLocalInitValue$1(value); + } + if (dstField?.hasBuilderParamDecorator()) { + let method = this.findBuilderMethod(value); + if (method) { + transferMap.set(dstField, method); + } + } + else { + let srcField; + if (value instanceof ArkInstanceFieldRef) { + srcField = this.getDeclaringArkClass().getFieldWithName(value.getFieldName()); + } + if (srcField && dstField) { + transferMap.set(dstField, srcField); + } + } + } + parseObjectLiteralExpr(cls, object, builder) { + let transferMap = new Map(); + if (object instanceof Local && object.getType() instanceof ClassType) { + let anonymousSig = object.getType().getClassSignature(); + let anonymous = this.findClass(anonymousSig); + anonymous?.getFields().forEach(field => { + this.parseFieldInObjectLiteral(field, cls, transferMap); + }); + } + // If the builder exists, there will be a unique BuilderParam + if (builder) { + cls.getFields().forEach(value => { + if (value.hasBuilderParamDecorator()) { + transferMap.set(value, builder); + } + }); + } + if (transferMap.size === 0) { + return undefined; + } + return transferMap; + } + viewComponentCreationParser(name, stmt, expr) { + let temp = expr.getArg(0); + let arg; + temp.getUsedStmts().forEach(value => { + if (value instanceof ArkAssignStmt && value.getRightOp() instanceof ArkInstanceInvokeExpr) { + const rightOp = value.getRightOp(); + const methodName = rightOp.getMethodSignature().getMethodSubSignature().getMethodName(); + if (methodName === 'constructor') { + arg = rightOp.getArg(0); + } + } + }); + let builderMethod; + let builder = expr.getArg(1); + if (builder) { + let method = this.findMethod(builder.getType().getMethodSignature()); + if (!method?.hasBuilderDecorator()) { + method?.addDecorator(new Decorator(BUILDER_DECORATOR)); + } + if (!method?.hasViewTree()) { + method?.setViewTree(new ViewTreeImpl(method)); + } + if (method) { + builderMethod = method; + } + } + let initValue = backtraceLocalInitValue$1(temp); + if (!(initValue instanceof ArkNewExpr)) { + return undefined; + } + const initValueType = initValue.getType(); + if (!(initValueType instanceof ClassType)) { + return undefined; + } + let clsSignature = initValueType.getClassSignature(); + if (clsSignature) { + let cls = this.findClass(clsSignature); + if (cls && cls.hasComponentDecorator()) { + return this.addCustomComponentNode(cls, arg, builderMethod); + } + else { + logger$p.error(`ViewTree->viewComponentCreationParser not found class ${clsSignature.toString()}. ${stmt.toString()}`); + } + } + return undefined; + } + waterFlowCreationParser(name, stmt, expr) { + let node = this.addSystemComponentNode(name); + let object = expr.getArg(0); + if (object instanceof Local && object.getType() instanceof ClassType) { + let anonymousSig = object.getType().getClassSignature(); + let anonymous = this.findClass(anonymousSig); + let footer = anonymous?.getFieldWithName('footer'); + if (!footer) { + return node; + } + let stmts = footer.getInitializer(); + let assignStmt = stmts[stmts.length - 1]; + if (!(assignStmt instanceof ArkAssignStmt)) { + return node; + } + let value = assignStmt.getRightOp(); + let method = this.findBuilderMethod(value); + if (method?.hasBuilderDecorator()) { + return this.addBuilderNode(method); + } + } + return node; + } + forEachCreationParser(name, stmt, expr) { + let node = this.addSystemComponentNode(name); + let values = expr.getArg(0); + let declaringStmt = values?.getDeclaringStmt(); + if (declaringStmt) { + let stateValues = StateValuesUtils$1.getInstance(this.getDeclaringArkClass()).parseStmtUsesStateValues(declaringStmt); + stateValues.forEach(field => { + node.stateValues.add(field); + this.addStateValue(field, node); + }); + } + let type = expr.getArg(1).getType(); + let method = this.findMethod(type.getMethodSignature()); + if (method && method.getCfg()) { + this.buildViewTreeFromCfg(method.getCfg()); + } + return node; + } + repeatCreationParser(name, stmt, expr) { + let node = this.addSystemComponentNode(name); + let arg = expr.getArg(0); + let declaringStmt = arg?.getDeclaringStmt(); + if (declaringStmt) { + let stateValues = StateValuesUtils$1.getInstance(this.getDeclaringArkClass()).parseStmtUsesStateValues(declaringStmt); + stateValues.forEach(field => { + node.stateValues.add(field); + this.addStateValue(field, node); + }); + } + return node; + } + ifBranchCreationParser(name, stmt, expr) { + this.popComponentExpect(COMPONENT_IF); + return this.addSystemComponentNode(COMPONENT_IF_BRANCH); + } + COMPONENT_CREATE_PARSERS = new Map([ + ['ForEach.create', this.forEachCreationParser.bind(this)], + ['LazyForEach.create', this.forEachCreationParser.bind(this)], + ['Repeat.create', this.repeatCreationParser.bind(this)], + ['View.create', this.viewComponentCreationParser.bind(this)], + ['If.branch', this.ifBranchCreationParser.bind(this)], + ['WaterFlow.create', this.waterFlowCreationParser.bind(this)], + ]); + componentCreateParse(componentName, methodName, stmt, expr) { + let parserFn = this.COMPONENT_CREATE_PARSERS.get(`${componentName}.${methodName}`); + if (parserFn) { + let node = parserFn(componentName, stmt, expr); + node?.addStmt(this, stmt); + return node; + } + this.popAutomicComponent(componentName); + let node = this.addSystemComponentNode(componentName); + node.addStmt(this, stmt); + return node; + } + parseStaticInvokeExpr(local2Node, stmt, expr) { + let methodSignature = expr.getMethodSignature(); + let method = this.findMethod(methodSignature); + if (method?.hasBuilderDecorator()) { + let node = this.addBuilderNode(method); + node.parseStateValues(this, stmt); + return node; + } + let name = methodSignature.getDeclaringClassSignature().getClassName(); + let methodName = methodSignature.getMethodSubSignature().getMethodName(); + if (this.isCreateFunc(methodName)) { + return this.componentCreateParse(name, methodName, stmt, expr); + } + let currentNode = this.top(); + if (name === currentNode?.name) { + currentNode.addStmt(this, stmt); + if (methodName === COMPONENT_POP_FUNCTION) { + this.pop(); + } + return currentNode; + } + else if (name === COMPONENT_IF && methodName === COMPONENT_POP_FUNCTION) { + this.popComponentExpect(COMPONENT_IF); + this.pop(); + } + return undefined; + } + /** + * $temp4.margin({ top: 20 }); + * @param viewTree + * @param local2Node + * @param expr + */ + parseInstanceInvokeExpr(local2Node, stmt, expr) { + let temp = expr.getBase(); + if (local2Node.has(temp)) { + let component = local2Node.get(temp); + if (component?.name === COMPONENT_REPEAT && expr.getMethodSignature().getMethodSubSignature().getMethodName() === 'each') { + let arg = expr.getArg(0); + let type = arg.getType(); + if (type instanceof FunctionType) { + let method = this.findMethod(type.getMethodSignature()); + this.buildViewTreeFromCfg(method?.getCfg()); + } + this.pop(); + } + else { + component?.addStmt(this, stmt); + } + return component; + } + let name = expr.getBase().getName(); + if (name.startsWith(TEMP_LOCAL_PREFIX)) { + let initValue = backtraceLocalInitValue$1(expr.getBase()); + if (initValue instanceof ArkThisRef) { + name = 'this'; + } + } + let methodName = expr.getMethodSignature().getMethodSubSignature().getMethodName(); + let field = this.getDeclaringArkClass().getFieldWithName(methodName); + if (name === 'this' && field?.hasBuilderParamDecorator()) { + return this.addBuilderParamNode(field); + } + let method = this.findMethod(expr.getMethodSignature()); + if (name === 'this' && method?.hasBuilderDecorator()) { + return this.addBuilderNode(method); + } + return undefined; + } + parsePtrInvokeExpr(local2Node, stmt, expr) { + let temp = expr.getFuncPtrLocal(); + if (temp instanceof Local && local2Node.has(temp)) { + let component = local2Node.get(temp); + if (component?.name === COMPONENT_REPEAT && expr.getMethodSignature().getMethodSubSignature().getMethodName() === 'each') { + let arg = expr.getArg(0); + let type = arg.getType(); + if (type instanceof FunctionType) { + let method = this.findMethod(type.getMethodSignature()); + this.buildViewTreeFromCfg(method?.getCfg()); + } + this.pop(); + } + else { + component?.addStmt(this, stmt); + } + return component; + } + else if (temp instanceof ArkInstanceFieldRef) { + let name = temp.getBase().getName(); + if (name.startsWith(TEMP_LOCAL_PREFIX)) { + let initValue = backtraceLocalInitValue$1(temp.getBase()); + if (initValue instanceof ArkThisRef) { + name = 'this'; + } + } + let methodName = temp.getFieldName(); + let field = this.getDeclaringArkClass().getFieldWithName(methodName); + if (name === 'this' && field?.hasBuilderParamDecorator()) { + return this.addBuilderParamNode(field); + } + let method = this.findMethod(expr.getMethodSignature()); + if (name === 'this' && method?.hasBuilderDecorator()) { + return this.addBuilderNode(method); + } + } + return undefined; + } + /** + * $temp3 = View.create($temp2); + * $temp4 = View.pop(); + * $temp4.margin({ top: 20 }); + * + * $temp2 = List.create(); + * $temp5 = $temp2.width('100%'); + * $temp6 = $temp5.height('100%'); + * $temp6.backgroundColor('#FFDCDCDC'); + * @param viewTree + * @param local2Node + * @param stmt + * @returns + */ + parseAssignStmt(local2Node, stmt) { + let left = stmt.getLeftOp(); + let right = stmt.getRightOp(); + if (!(left instanceof Local)) { + return; + } + let component; + if (right instanceof ArkStaticInvokeExpr) { + component = this.parseStaticInvokeExpr(local2Node, stmt, right); + } + else if (right instanceof ArkInstanceInvokeExpr) { + component = this.parseInstanceInvokeExpr(local2Node, stmt, right); + } + else if (right instanceof ArkPtrInvokeExpr) { + component = this.parsePtrInvokeExpr(local2Node, stmt, right); + } + if (component) { + local2Node.set(left, component); + } + } + parseInvokeStmt(local2Node, stmt) { + let expr = stmt.getInvokeExpr(); + if (expr instanceof ArkStaticInvokeExpr) { + this.parseStaticInvokeExpr(local2Node, stmt, expr); + } + else if (expr instanceof ArkInstanceInvokeExpr) { + this.parseInstanceInvokeExpr(local2Node, stmt, expr); + } + else if (expr instanceof ArkPtrInvokeExpr) { + this.parsePtrInvokeExpr(local2Node, stmt, expr); + } + } + buildViewTreeFromCfg(cfg, local2Node = new Map()) { + if (!cfg) { + return; + } + let blocks = cfg.getBlocks(); + for (const block of blocks) { + for (const stmt of block.getStmts()) { + if (!(stmt instanceof ArkInvokeStmt || stmt instanceof ArkAssignStmt)) { + continue; + } + if (stmt instanceof ArkAssignStmt) { + this.parseAssignStmt(local2Node, stmt); + } + else if (stmt instanceof ArkInvokeStmt) { + this.parseInvokeStmt(local2Node, stmt); + } + } + } + } +} +function buildViewTree(render) { + return new ViewTreeImpl(render); +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const LIFECYCLE_METHOD_NAME = [ + 'onCreate', // 组件实例创建 + 'onDestroy', // 组件实例销毁 + 'onWindowStageCreate', // 窗口创建 + 'onWindowStageDestroy', // 窗口销毁 + 'onForeground', // 应用进入前台 + 'onBackground', // 应用进入后台 + 'onBackup', // 应用数据备份 + 'onRestore', // 应用数据恢复 + 'onContinue', + 'onNewWant', + 'onDump', + 'onSaveState', + 'onShare', + 'onPrepareToTerminate', + 'onBackPressed', + 'onSessionCreate', + 'onSessionDestory', + 'onAddForm', + 'onCastToNormalForm', + 'onUpdateForm', + 'onChangeFormVisibility', + 'onFormEvent', + 'onRemoveForm', + 'onConfigurationUpdate', + 'onAcquireFormState', + 'onWindowStageWillDestroy', +]; +const CALLBACK_METHOD_NAME = [ + 'onClick', // 点击事件,当用户点击组件时触发 + 'onTouch', // 触摸事件,当手指在组件上按下、滑动、抬起时触发 + 'onAppear', // 组件挂载显示时触发 + 'onDisAppear', // 组件卸载消失时触发 + 'onDragStart', // 拖拽开始事件,当组件被长按后开始拖拽时触发 + 'onDragEnter', // 拖拽进入组件范围时触发 + 'onDragMove', // 拖拽在组件范围内移动时触发 + 'onDragLeave', // 拖拽离开组件范围内时触发 + 'onDrop', // 拖拽释放目标,当在本组件范围内停止拖拽行为时触发 + 'onKeyEvent', // 按键事件,当组件获焦后,按键动作触发 + 'onFocus', // 焦点事件,当组件获取焦点时触发 + 'onBlur', // 当组件失去焦点时触发的回调 + 'onHover', // 鼠标悬浮事件,鼠标进入或退出组件时触发 + 'onMouse', // 鼠标事件,当鼠标按键点击或在组件上移动时触发 + 'onAreaChange', // 组件区域变化事件,组件尺寸、位置变化时触发 + 'onVisibleAreaChange', // 组件可见区域变化事件,组件在屏幕中的显示区域面积变化时触发 + "onClick", + "onTouch", + "onDetach", + "onAttach", + "onAppear", + "onDisAppear", + "onDragStart", + "onDragEnter", + "onDragMove", + "onDragLeave", + "onDrop", + "onDragEnd", + "onPreDrag", + "onKeyEvent", + "onKeyPreIme", + "onFocus", + "onBlur", + "onMouse", + "onHover", + "onAccessibilityHover", + "onAreaChange", + "onSizeChange", + "onVisibleAreaChange", + "onChildTouchTest", + "onTouchIntercept", + /* 如下为非通用事件 */ + /* 注: 如果事件 E 同时属于类别 A 和类别 B, 且分类顺序为先 A 后 B, + 则仅将其归类到类别 A */ + /* 行列与堆叠 */ + "onFolderStateChange", + "onHoverStatusChange", + /* 栅格与分栏 */ + "onBreakpointChange", + "onHoverStatusChange", + "onChange", + /* 滚动与滑动 */ + "onScrollIndex", + "onReachStart", + "onReachEnd", + "onScrollFrameBegin", + "onScrollStart", + "onScrollStop", + "onItemMove", + "onItemDragStart", + "onItemDragEnter", + "onItemDragMove", + "onItemDragLeave", + "onItemDrop", + "onScrollVisibleContentChange", + "onSelect", + "onScrollBarUpdate", + "onWillScroll", + "onDidScroll", + "onScrollEdge", + "onAnimationStart", + "onAnimationEnd", + "onGestureSwipe", + "customContentTransition", + "onContentDidScroll", + "onStateChange", + "onRefreshing", + "onOffsetChange", + /* 导航与切换 */ + "onTitleModeChange", + "onNavBarStateChange", + "onNavigationModeChange", + "customNavContentTransition", + "onShown", + "onHidden", + "onWillAppear", + "onWillShow", + "onWillHide", + "onWillDisappear", + "onBackPressed", + "onReady", + "onFinish", + "onSkip", + "onNext", + "onPrevious", + "onTabBarClick", + "onContentWillChange", + /* 按钮与选择 */ + "onDateChange", + /* 文本与输入 */ + "onCopy", + "onTextSelectionChange", + "onEditChange", + "onCut", + "onPaste", + "onContentScroll", + "onSubmit", + "onWillInsert", + "onDidInsert", + "onWillDelete", + "onDidDelete", + "onSecurityStateChange", + "aboutToIMEInput", + "onDidIMEInput", + "onIMEInputComplete", + "aboutToDelete", + "onDeleteComplete", + "onSelectionChange", + "onEditingChange", + "onWillChange", + "onDidChange", + "onComplete", + "onError", + "onCreateMenu", + "onMenuItemClick", + /* 图片与视频 */ + "onStart", + "onPause", + "onRepeat", + "onCancel", + "onStop", + "onPrepared", + "onSeeking", + "onSeeked", + "onUpdate", + "onFullscreenChange", + /* 信息展示 */ + "onRequestPopupData", + "onPopupSelect", + "onInc", + "onDec", + "onBounce", + "onPatternComplete", + "onDotConnect", + "onTimer", + /* 渲染绘制 */ + "onLoad", + "onDestroy", + "onTerminated", + /* 菜单 */ + "ContextMenu.close", + /* 动画 */ + "onEnter", + "onExit", + "ParticleOptions", + "EmitterOptions", + "ParticleConfigs", + "PointParticleParameters", + "ImageParticleParameters", + "ParticleColorPropertyOptions", + "ParticleColorPropertyUpdaterConfigs", + "ParticlePropertyOptions", + "ParticlePropertyUpdaterConfigs", + "ParticlePropertyAnimation", + "ParticleType", + "ParticleEmitterShape", + "DistributionType", + "ParticleUpdater", + "DisturbanceFieldOptions", + "DisturbanceFieldShape", + "EmitterProperty", + /* 状态管理与渲染控制 */ + "onMove", + /* 手势处理 */ + // ref[API14]: https://developer.huawei.com/consumer/cn/doc/harmonyos-references/gesture-handling + "onAction", + "onActionEnd", + "onActionCancel", + "onActionStart", + "onActionUpdate", + "onCancel", + "menus", +]; +const COMPONENT_LIFECYCLE_METHOD_NAME = [ + 'build', + 'aboutToAppear', + 'aboutToDisappear', + 'aboutToReuse', + 'aboutToRecycle', + 'onWillApplyTheme', + 'onLayout', + 'onPlaceChildren', + 'onMeasure', + 'onMeasureSize', + 'onPageShow', + 'onPageHide', + 'onFormRecycle', + 'onFormRecover', + 'onBackPress', + 'pageTransition', + 'onDidBuild', +]; +function getCallbackMethodFromStmt(stmt, scene) { + const invokeExpr = stmt.getInvokeExpr(); + if (invokeExpr === undefined || + invokeExpr.getMethodSignature().getDeclaringClassSignature().getClassName() !== '' || + !CALLBACK_METHOD_NAME.includes(invokeExpr.getMethodSignature().getMethodSubSignature().getMethodName())) { + return null; + } + for (const arg of invokeExpr.getArgs()) { + const argType = arg.getType(); + if (argType instanceof FunctionType) { + const cbMethod = scene.getMethod(argType.getMethodSignature()); + if (cbMethod) { + return cbMethod; + } + } + } + return null; +} +function addCfg2Stmt(method) { + const cfg = method.getCfg(); + if (cfg) { + for (const block of cfg.getBlocks()) { + for (const stmt of block.getStmts()) { + stmt.setCfg(cfg); + } + } + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class ArkCodeBuffer { + output = []; + indent = ''; + constructor(indent = '') { + this.indent = indent; + } + write(s) { + this.output.push(s); + return this; + } + writeLine(s) { + this.write(s); + this.write('\n'); + return this; + } + writeSpace(s) { + if (s.length === 0) { + return this; + } + this.write(s); + this.write(' '); + return this; + } + writeStringLiteral(s) { + this.write(`'${s}'`); + return this; + } + writeIndent() { + this.write(this.indent); + return this; + } + incIndent() { + this.indent += ' '; + return this; + } + decIndent() { + if (this.indent.length >= 2) { + this.indent = this.indent.substring(0, this.indent.length - 2); + } + return this; + } + getIndent() { + return this.indent; + } + toString() { + return this.output.join(''); + } + clear() { + this.output = []; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @category save + */ +class Printer { + printer; + constructor(indent = '') { + this.printer = new ArkCodeBuffer(indent); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +function escapeStr(input) { + let str = input; + for (let i = 0; i < str.length; ++i) { + switch (str[i]) { + case '\n': + str = str.substring(0, i) + '\\n' + str.substring(i + 1); + ++i; + break; + case '\t': + str = str.substring(0, i) + ' ' + str.substring(i + 1); + ++i; + break; + case '\\': + if (i + 1 < str.length) { + switch (str[i + 1]) { + case 'l': + continue; // don't disturb \l + case '|': + case '{': + case '}': + str = str.substring(0, i) + str.substring(i + 1); + continue; + } + } + str = str.substring(0, i) + '\\\\' + str.substring(i + 1); + ++i; + break; + case '{': + case '}': + case '<': + case '>': + case '|': + case '"': + str = str.substring(0, i) + '\\' + str[i] + str.substring(i + 1); + ++i; + break; + } + } + return str; +} +class GraphPrinter extends Printer { + graph; + title; + startID = undefined; + constructor(g, t) { + super(); + this.graph = g; + if (t) { + this.title = t; + } + } + setStartID(n) { + this.startID = n; + } + // public dump(): string { + // 公共方法,返回一个字符串 + dump() { + // 清空打印机 + this.printer.clear(); + // 写入图形 + this.writeGraph(); + return this.printer.toString(); + } + writeGraph() { + this.writeHeader(); + this.writeNodes(); + this.writeFooter(); + } + writeNodes() { + let itor = this.graph.nodesItor(); + if (this.startID) { + // from start id + let nodes = new Set(); + let startNode = this.graph.getNode(this.startID); + let worklist = [startNode]; + while (worklist.length > 0) { + let n = worklist.shift(); + if (nodes.has(n)) { + continue; + } + nodes.add(n); + n.getOutgoingEdges()?.forEach(e => worklist.push(e.getDstNode())); + } + itor = nodes.values(); + } + for (let node of itor) { + let nodeAttr = node.getDotAttr(); + if (nodeAttr === '') { + continue; + } + let nodeLabel = escapeStr(node.getDotLabel()); + this.printer.writeLine(`\tNode${node.getID()} [shape=recode,${nodeAttr},label="${nodeLabel}"];`); + for (let edge of node.getOutgoingEdges()) { + this.writeEdge(edge); + } + } + } + writeEdge(edge) { + let edgeAttr = edge.getDotAttr(); + if (edgeAttr === '') { + return; + } + this.printer.writeLine(`\tNode${edge.getSrcID()} -> Node${edge.getDstID()}[${edgeAttr}]`); + } + writeHeader() { + const GraphName = this.graph.getGraphName(); + let graphNameStr = `digraph "${escapeStr(this.title || GraphName || 'unnamed')}" {\n`; + this.printer.writeLine(graphNameStr); + let labelStr = `\tlabel="${escapeStr(this.title || GraphName)}";\n`; + this.printer.writeLine(labelStr); + // TODO: need graph attr? + } + writeFooter() { + this.printer.writeLine('}\n'); + } +} +class GraphJsonPrinter extends Printer { + graph; + // private title?: string; + startID = undefined; + output = { + name: "", + version: "1.0.0", + nodeCount: 0, + edgeCount: 0, + edges: [] + }; + constructor(g, t) { + super(); + this.graph = g; + //this.title = t; + } + setStartID(n) { + this.startID = n; + } + // 核心方法:生成JSON字符串 + dump() { + this.output.edges = []; // 重置边数组 + this.output.name = "PTG"; + const nodes = this.collectNodes(); + this.output.nodeCount = nodes.length; + nodes.forEach(node => this.serializeNodeEdges(node)); + this.output.edgeCount = this.output.edges.length; + this.serializeGraph(); // 序列化图数据 + return JSON.stringify(this.output, null, 2); + } + // 序列化整个图:收集所有需要处理的节点和边 + serializeGraph() { + const nodes = this.collectNodes(); // 获取需要处理的节点(支持从startID开始) + nodes.forEach(node => this.serializeNodeEdges(node)); // 序列化每个节点的出边 + } + // 收集需要处理的节点(支持从startID遍历或全量节点) + collectNodes() { + const nodes = new Set(); + if (this.startID) { + // 从起始节点开始遍历可达节点 + const startNode = this.graph.getNode(this.startID); + if (startNode) + this.traverseReachableNodes(startNode, nodes); + } + else { + // 处理所有节点 + for (const node of this.graph.nodesItor()) { + nodes.add(node); + } + } + return Array.from(nodes); + } + // 从起始节点遍历所有可达节点(深度优先) + traverseReachableNodes(start, nodes) { + if (nodes.has(start)) + return; + nodes.add(start); + // 遍历所有出边的目标节点 + start.getOutgoingEdges().forEach(edge => { + const dstNode = edge.getDstNode(); + this.traverseReachableNodes(dstNode, nodes); + }); + } + // 序列化节点的所有出边 + serializeNodeEdges(node) { + node.getOutgoingEdges().forEach(edge => { + if (edge instanceof UIFuncEdge) { // 确保只处理UIFuncEdge类型 + const edgeJson = this.serializeEdge(edge, node); + this.output.edges.push(edgeJson); + } + }); + } + // 核心:将UIFuncEdge序列化为目标JSON格式的边 + serializeEdge(edge, srcNode) { + const dstNode = edge.getDstNode(); + // 生成节点标识(格式:"类名;页面ID",如"AdvertisingPage;AdvertisingPage_ce4eb654") + const from = this.serializeNodeId(srcNode); + const to = this.serializeNodeId(dstNode); + let lifecycle = false; + const api_name = edge.getAttribute() || 'unkown'; + if (edge.getAttribute() && COMPONENT_LIFECYCLE_METHOD_NAME.includes(api_name)) { + lifecycle = true; + } + // 从边中提取事件信息(event字段) + const events = lifecycle ? [] : this.serializeEdgeEvents(edge); + // 从边中提取生命周期信息(lifecycle字段) + const lifecycles = lifecycle ? this.serializeEdgeLifecycles(edge) : []; + // 生成边ID(格式:"源节点#目标节点#动作ID") + const edgeId = `${from}#${to}#${events[0]?.actionId || 'unknown'}`; + return { + event: events, + from: from, + id: edgeId, + lifecycle: lifecycles, + to: to + }; + } + // 生成节点唯一标识(用于from/to字段) + serializeNodeId(node) { + const className = node.getClass().getName() || 'UnknownClass'; + const pageId = node.getPageId(); + return `${className};${pageId}`; + } + // 从UIFuncEdge提取事件信息(映射到event数组) + serializeEdgeEvents(edge) { + const node = edge.getNode(); // 获取UI节点信息 + // const transitionMethod = edge.getTransitionMethod(); + // const attribute = edge.getAttribute() || ''; + // 构建事件对象(根据edge属性动态生成) + const event = { + actionId: this.generateActionId(edge), // 生成唯一动作ID + actionType: this.getActionType(edge), // 推断动作类型(如onClick) + apiName: this.getApiName(edge), // 事件名称(如类签名) + eventName: this.getEventName(edge), // 事件名称(如类签名) + eventType: this.getEventType(edge), // 事件类型(如Toast触发) + xpath: node?.xpath || 'unknown', // 从UI节点获取xpath + unique_xpath: node?.unique_xpath || 'unknown' // 获取唯一XPath + }; + return [event]; // 目前默认单事件,可扩展多事件 + } + // 从UIFuncEdge提取生命周期信息(映射到lifecycle数组) + serializeEdgeLifecycles(edge) { + const transitionMethod = edge.getTransitionMethod(); + // 构建生命周期对象(根据transitionMethod和stmt推断) + const lifecycle = { + lifecycleId: `lifecycle_${transitionMethod}_${Date.now()}`, // 生成唯一ID + lifecycleName: edge.getApiMethod()?.getName() || 'unkown', + lifecycleType: edge.getAttribute() || 'unkown', + }; + // 只有特定类型的边才包含生命周期信息(如页面跳转) + return [lifecycle]; + } + // 生成动作ID(格式:"事件类型: xpath#事件类型#哈希值") + generateActionId(edge) { + const node = edge.getNode(); + const eventType = this.getEventType(edge); + const xpath = node?.xpath || 'unknown'; + const hash = Math.abs(`${xpath}${eventType}`.hashCode()); // 简单哈希生成唯一值 + return `${eventType}: ${xpath}#${eventType}#${hash}`; + } + // 推断动作类型(如onClick,根据edge属性判断) + getActionType(edge) { + return edge.getType() || 'unknown'; + } + getApiName(edge) { + const attribute = edge.getAttribute(); + if (attribute) { + return attribute; + } + return 'unknown'; + } + // 获取事件名称(如类签名) + getEventName(edge) { + const arkClass = edge.getApiClass(); + return arkClass.getSignature().toString() || 'UnknownEvent'; + } + // 获取事件类型(如Toast触发、页面跳转等) + getEventType(edge) { + const transitionMethod = edge.getTransitionMethod(); + // 根据transitionMethod映射事件类型 + const typeMap = { + 'include': 'Include触发', + 'Tabs': 'Tab切换', + 'push': '页面入栈', + 'pop': '页面出栈', + 'replace': '页面替换' + }; + return typeMap[transitionMethod] || `${transitionMethod}`; // Page迁移、Tab切换、nav迁移等 + } +} +String.prototype.hashCode = function () { + let hash = 0; + for (let i = 0; i < this.length; i++) { + const char = this.charCodeAt(i); + hash = ((hash << 5) - hash) + char; + hash = hash & hash; // 转换为32位整数 + } + return hash; +}; +class PageGuidePrinter extends Printer { + graph; + pageId = undefined; + matchedNodes = []; + dynamic_tree = new ArkUIViewTreeImpl(new ArkMethod()); + static_tree = new ArkUIViewTreeImpl(new ArkMethod()); + output = { + pageInfo: { + pgaePath: "", + pageId: 0, + timestamp: new Date().toISOString() + }, + stats: { + totalMathedNodes: 0 + }, + edges: [] + }; + constructor(graph, t) { + super(); + this.graph = graph; + } + setPageID(n) { + this.pageId = n; + } + setDynamicTree(dynamic_tree) { + this.dynamic_tree = dynamic_tree; + } + setStaticTree(static_tree) { + this.static_tree = static_tree; + } + setMatchedNodes(matchedNodes) { + this.matchedNodes = matchedNodes; + } + // 核心方法:生成JSON字符串 + dump() { + const pageNode = this.graph.getNode(this.pageId); + console.log("pageNode: ", pageNode.getClass().getSignature().toString()); + if (!pageNode) { + return JSON.stringify(this.output, null, 2); // 返回空输出 + } + if (this.pageId) { + this.output.pageInfo.pageId = this.pageId; + } + this.output.pageInfo.pgaePath = pageNode.getClass().getSignature().toString(); + this.output.pageInfo.timestamp = new Date().toISOString(); + this.output.stats.totalMathedNodes = this.matchedNodes.length; + const edges = this.collcetEdges(pageNode); + let has_print_xpath = new Set(); + for (let edge of edges) { + const edgeJson = this.serializeEdge(edge, pageNode); + this.output.edges.push(edgeJson); + // todo check node 有没有被推进去 -- 没有被推进去 + } + // ========================== 第一优先级具有 UI 迁移 行为的 边和 Handler 的节点 ======================== + // 1.遍历动态节点 2.遍历动态节点所映射上的静态节点 3.如果静态节点等于这个边的trigger_node的话,把动态节点推进去 + for (const dynamic_node of this.matchedNodes) { // 考虑把这个 dynamic_node 推到哪个边里 + const static_nodes = this.dynamic_tree.nodes2possiblesnode.get(dynamic_node.unique_xpath); + for (let edge of this.output.edges) { + let is_push = false; + for (const static_node of static_nodes || []) { + for (const event of edge.event) { + if (static_node.unique_xpath == event.unique_xpath) { + is_push = true; + break; + } + if (is_push) + break; + } + if (is_push) + break; + } + if (is_push && dynamic_node.dynamic_attributes) { + edge.nodes.push(dynamic_node.dynamic_attributes); + } + } + } + console.log("has print_xpath size = ", has_print_xpath.size); + let map = new Map(); + let handler_2_static_node = new Map(); + for (const dynamic_node of this.matchedNodes) { + console.log("ready to print dynamic xpath match static : ", dynamic_node.unique_xpath); + const static_nodes = this.dynamic_tree.nodes2possiblesnode.get(dynamic_node.unique_xpath); + if (static_nodes) { + for (const static_node of static_nodes) { + console.log("static node: ", static_node.unique_xpath); + if (static_node.handlers.length != 0) { + console.log("handler size = ", static_node.handlers.length); + for (const handler of static_node.handlers) { + map.get(handler)?.push(dynamic_node) || map.set(handler, [dynamic_node]); + handler_2_static_node.set(handler, static_node); + } + } + } + } + } + for (const static_node of this.static_tree.nodeSet) { + const dynamic_nodes = this.static_tree.nodes2possiblesnode.get(static_node.unique_xpath); + if (dynamic_nodes) { + if (static_node.attributes) { + for (const arr of static_node.attributes) { + console.log("arr: ", arr[0]); + if (CALLBACK_METHOD_NAME.includes(arr[0])) { + let event = { + actionId: 'unknown', // 或自定义唯一ID + actionType: arr[0], // 通常是事件类型,如 'onClick' + eventName: 'unknown', // 事件名 + eventType: arr[0], // 或根据实际类型填写 + apiName: arr[0], // 如果有API名称可以填 + xpath: static_node.xpath || 'unknown', + unique_xpath: static_node.unique_xpath || 'unknown' + }; + let edge = { + event: [event], + from: static_node.classSignature?.toString() || 'unknown', + id: `${static_node.xpath || 'unknown'}#${arr[0]}`, + lifecycle: [], + to: 'unknown', + nodes: [] + }; + for (let node of dynamic_nodes) { + edge.nodes.push(node.dynamic_attributes); + } + this.output.edges.push(edge); + } + } + } + } + } + for (let it of map) { + console.log("handler: ", it[0]); // 对于每一个Handler去创建一个 GuideEdgeJson + let handler = it[0]; + let node = handler_2_static_node.get(handler); + if (!node) + continue; + let event = { + actionId: 'unknown', // 或自定义唯一ID + actionType: handler.type, // 通常是事件类型,如 'onClick' + eventName: 'unknown', // 事件名 + eventType: handler.transitionType || handler.navigationType || 'unknown', // 或根据实际类型填写 + apiName: handler.type, // 如果有API名称可以填 + xpath: node.xpath || 'unknown', + unique_xpath: node.unique_xpath || 'unknown' + }; + let edge = { + event: [event], + from: node.signature?.toString() || 'unknown', + id: 'unknown', + lifecycle: [], + to: handler.target?.name || 'unknown', + nodes: [] + }; + for (let node of it[1]) { + if (node.dynamic_attributes) { + edge.nodes.push(node.dynamic_attributes); + } + } + this.output.edges.push(edge); + } + // for (let edge of this.output.edges) { + // for (const dynamic_node of this.matchedNodes) { + // if (edge.event.length > 0 && edge.event[0].dynamic_xpath == dynamic_node.xpath) { + // if (dynamic_node.dynamic_attributes) { + // edge.nodes.push(dynamic_node.dynamic_attributes); + // } + // } + // } + // } + // console.log("===== matched nodes debug ======"); + // for (const dynamic_node of this.matchedNodes) { + // if (dynamic_node.dynamic_attributes){ + // console.log("id: ",dynamic_node.dynamic_attributes.accessibilityId," xpath: ",dynamic_node.xpath); + // } + // } + return JSON.stringify(this.output, null, 2); + } + // 从起始节点遍历所有可达节点(深度优先) + traverseReachableNodes(start, nodes) { + if (nodes.has(start)) + return; + nodes.add(start); + // 遍历所有出边的目标节点 + start.getOutgoingEdges().forEach(edge => { + const dstNode = edge.getDstNode(); + this.traverseReachableNodes(dstNode, nodes); + }); + } + collcetEdges(node) { + const edges = []; + node.getOutgoingEdges().forEach(edge => { + if (edge instanceof UIFuncEdge) { // 确保只处理UIFuncEdge类型 + edges.push(edge); + } + }); + return edges; + } + // 核心:将UIFuncEdge序列化为目标JSON格式的边 + serializeEdge(edge, srcNode) { + const dstNode = edge.getDstNode(); + // 生成节点标识(格式:"类名;页面ID",如"AdvertisingPage;AdvertisingPage_ce4eb654") + const from = this.serializeNodeId(srcNode); + const to = this.serializeNodeId(dstNode); + let lifecycle = false; + const api_name = edge.getAttribute() || 'unkown'; + if (edge.getAttribute() && COMPONENT_LIFECYCLE_METHOD_NAME.includes(api_name)) { + lifecycle = true; + } + // 从边中提取事件信息(event字段) + const events = lifecycle ? [] : this.serializeEdgeEvents(edge); + // 从边中提取生命周期信息(lifecycle字段) + const lifecycles = lifecycle ? this.serializeEdgeLifecycles(edge) : []; + // 生成边ID(格式:"源节点#目标节点#动作ID") + const edgeId = `${from}#${to}#${events[0]?.actionId || 'unknown'}`; + return { + event: events, + from: from, + id: edgeId, + lifecycle: lifecycles, + to: to, + nodes: [], + }; + } + // 生成节点唯一标识(用于from/to字段) + // 序列化节点ID + serializeNodeId(node) { + // 获取节点类名 + const className = node.getClass().getName() || 'UnknownClass'; + // 获取节点页面ID + const pageId = node.getPageId(); + // 返回序列化后的节点ID + return `${className};${pageId}`; + } + // 从UIFuncEdge提取事件信息(映射到event数组) + serializeEdgeEvents(edge) { + const node = edge.getNode(); // 获取UI节点信息 + // const transitionMethod = edge.getTransitionMethod(); + // const attribute = edge.getAttribute() || ''; + // 构建事件对象(根据edge属性动态生成) + const event = { + actionId: this.generateActionId(edge), // 生成唯一动作ID + actionType: this.getActionType(edge), // 推断动作类型(如onClick) + apiName: this.getApiName(edge), // 事件名称(如类签名) + eventName: this.getEventName(edge), // 事件名称(如类签名) + eventType: this.getEventType(edge), // 事件类型(如Toast触发) + xpath: node?.xpath || 'unknown', // 从UI节点获取xpath + unique_xpath: node?.unique_xpath || 'unknown' // 获取唯一XPath + }; + return [event]; // 目前默认单事件,可扩展多事件 + } + // 从UIFuncEdge提取生命周期信息(映射到lifecycle数组) + serializeEdgeLifecycles(edge) { + const transitionMethod = edge.getTransitionMethod(); + // 构建生命周期对象(根据transitionMethod和stmt推断) + const lifecycle = { + lifecycleId: `lifecycle_${transitionMethod}_${Date.now()}`, // 生成唯一ID + lifecycleName: edge.getApiMethod()?.getName() || 'unkown', + lifecycleType: edge.getAttribute() || 'unkown', + }; + // 只有特定类型的边才包含生命周期信息(如页面跳转) + return [lifecycle]; + } + // 生成动作ID(格式:"事件类型: xpath#事件类型#哈希值") + generateActionId(edge) { + const node = edge.getNode(); + const eventType = this.getEventType(edge); + const xpath = node?.xpath || 'unknown'; + const hash = Math.abs(`${xpath}${eventType}`.hashCode()); // 简单哈希生成唯一值 + return `${eventType}: ${xpath}#${eventType}#${hash}`; + } + // 推断动作类型(如onClick,根据edge属性判断) + getActionType(edge) { + return edge.getType() || 'unknown'; + } + getApiName(edge) { + const attribute = edge.getAttribute(); + if (attribute) { + return attribute; + } + return 'unknown'; + } + // 获取事件名称(如类签名) + getEventName(edge) { + const arkClass = edge.getApiClass(); + return arkClass.getSignature().toString() || 'UnknownEvent'; + } + // 获取事件类型(如Toast触发、页面跳转等) + getEventType(edge) { + const transitionMethod = edge.getTransitionMethod(); + // 根据transitionMethod映射事件类型 + const typeMap = { + 'include': 'Include触发', + 'Tabs': 'Tab切换', + 'push': '页面入栈', + 'pop': '页面出栈', + 'replace': '页面替换' + }; + return typeMap[transitionMethod] || `${transitionMethod}`; // Page迁移、Tab切换、nav迁移等 + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @category save + */ +class DotMethodPrinter extends Printer { + method; + nesting; + constructor(method, nesting = false) { + super(); + this.method = method; + this.nesting = nesting; + } + dump() { + this.printer.clear(); + if (this.nesting) { + this.printer.writeIndent().writeLine(`subgraph "cluster_${this.method.getSignature()}" {`); + } + else { + this.printer.writeIndent().writeLine(`digraph "${this.method.getSignature()}" {`); + } + this.printer.incIndent(); + this.printer.writeIndent().writeLine(`label="${this.method.getSignature()}";`); + let blocks = this.method.getCfg()?.getBlocks(); + let prefix = `Node${this.stringHashCode(this.method.getSignature().toString())}`; + this.printBlocks(blocks, prefix); + this.printer.decIndent(); + this.printer.writeIndent().writeLine('}'); + return this.printer.toString(); + } + stringHashCode(name) { + let hashCode = 0; + for (let i = 0; i < name.length; i++) { + hashCode += name.charCodeAt(i); + } + return Math.abs(hashCode); + } + printBlocks(blocks, prefix) { + if (!blocks) { + return; + } + let blockToNode = new Map(); + let index = 0; + for (let block of blocks) { + let name = prefix + index++; + blockToNode.set(block, name); + /** Node0 [label="entry"]; */ + this.printer.writeIndent().writeLine(`${name} [label="${this.getBlockContent(block, this.printer.getIndent())}"];`); + } + for (let block of blocks) { + for (let nextBlock of block.getSuccessors()) { + // Node0 -> Node1; + this.printer.writeIndent().writeLine(`${blockToNode.get(block)} -> ${blockToNode.get(nextBlock)};`); + } + let exceptionalNextBlock = block.getExceptionalSuccessorBlocks(); + if (!exceptionalNextBlock) { + continue; + } + for (const nextBlock of exceptionalNextBlock) { + this.printer.writeIndent().writeLine(`${blockToNode.get(block)} -> ${blockToNode.get(nextBlock)}[style="dotted"];`); + } + } + } + getBlockContent(block, indent) { + let content = [`id:${block.getId()}`]; + for (let stmt of block.getStmts()) { + content.push(stmt.toString().replace(/"/g, '\\"')); + } + return content.join('\n ' + indent); + } +} +/** + * @category save + */ +class DotClassPrinter extends Printer { + cls; + nesting; + constructor(cls, nesting = false) { + super(); + this.cls = cls; + this.nesting = nesting; + } + dump() { + this.printer.clear(); + if (!this.nesting) { + this.printer.writeLine(`digraph "${this.cls.getName()}" {`); + this.printer.incIndent(); + } + for (let method of this.cls.getMethods()) { + let mtd = new DotMethodPrinter(method, true); + this.printer.write(mtd.dump()); + } + if (!this.nesting) { + this.printer.decIndent(); + this.printer.writeLine(`}`); + } + return this.printer.toString(); + } +} +/** + * @category save + */ +class DotNamespacePrinter extends Printer { + ns; + nesting; + constructor(ns, nesting = false) { + super(); + this.ns = ns; + this.nesting = nesting; + } + dump() { + this.printer.clear(); + if (!this.nesting) { + this.printer.writeLine(`digraph "${this.ns.getName()}" {`); + this.printer.incIndent(); + } + for (let method of this.ns.getAllMethodsUnderThisNamespace()) { + let mtd = new DotMethodPrinter(method, true); + this.printer.write(mtd.dump()); + } + if (!this.nesting) { + this.printer.decIndent(); + this.printer.writeLine(`}`); + } + return this.printer.toString(); + } +} +/** + * @category save + */ +class DotFilePrinter extends Printer { + arkFile; + constructor(arkFile) { + super(); + this.arkFile = arkFile; + } + dump() { + this.printer.clear(); + this.printer.writeLine(`digraph "${this.arkFile.getName()}" {`); + this.printer.incIndent(); + for (let ns of this.arkFile.getNamespaces()) { + let nsPrinter = new DotNamespacePrinter(ns, true); + this.printer.write(nsPrinter.dump()); + } + // print class + for (let cls of this.arkFile.getClasses()) { + let clsPrinter = new DotClassPrinter(cls, true); + this.printer.write(clsPrinter.dump()); + } + this.printer.decIndent(); + this.printer.writeLine('}'); + return this.printer.toString(); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$o = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'PrinterUtils'); +const CLASS_CATEGORY_COMPONENT = 100; +class PrinterUtils { + static classOriginTypeToString = new Map([ + [ClassCategory.CLASS, 'class'], + [ClassCategory.STRUCT, 'struct'], + [ClassCategory.INTERFACE, 'interface'], + [ClassCategory.ENUM, 'enum'], + [ClassCategory.TYPE_LITERAL, 'typeliteral'], + [ClassCategory.OBJECT, 'object'], + [CLASS_CATEGORY_COMPONENT, 'component'], + ]); + static isAnonymousClass(name) { + return name.startsWith(ANONYMOUS_CLASS_PREFIX); + } + static isDefaultClass(name) { + return name === DEFAULT_ARK_CLASS_NAME; + } + static isAnonymousMethod(name) { + return name.startsWith(ANONYMOUS_METHOD_PREFIX); + } + static isConstructorMethod(name) { + return name === 'constructor'; + } + static isDeIncrementStmt(stmt, op) { + if (!(stmt instanceof ArkAssignStmt)) { + return false; + } + let leftOp = stmt.getLeftOp(); + let rightOp = stmt.getRightOp(); + if (!(leftOp instanceof Local) || !(rightOp instanceof ArkNormalBinopExpr)) { + return false; + } + let op1 = rightOp.getOp1(); + let op2 = rightOp.getOp2(); + let operator = rightOp.getOperator(); + if (!(op1 instanceof Local) || !(op2 instanceof Constant)) { + return false; + } + return leftOp.getName() === op1.getName() && operator === op && op2.getValue() === '1'; + } + static isTemp(name) { + return name.startsWith(TEMP_LOCAL_PREFIX); + } + static getOriginType(cls) { + if (cls.hasComponentDecorator()) { + return CLASS_CATEGORY_COMPONENT; + } + return cls.getCategory(); + } + static isComponentPop(invokeExpr) { + let className = invokeExpr.getMethodSignature().getDeclaringClassSignature().getClassName(); + let methodName = invokeExpr.getMethodSignature().getMethodSubSignature().getMethodName(); + if (methodName === COMPONENT_POP_FUNCTION && (isEtsSystemComponent(className) || SPECIAL_CONTAINER_COMPONENT.has(className))) { + return true; + } + return false; + } + static isComponentCreate(invokeExpr) { + let className = invokeExpr.getMethodSignature().getDeclaringClassSignature().getClassName(); + let methodName = invokeExpr.getMethodSignature().getMethodSubSignature().getMethodName(); + if (methodName === COMPONENT_CREATE_FUNCTION && (isEtsSystemComponent(className) || SPECIAL_CONTAINER_COMPONENT.has(className))) { + return true; + } + return false; + } + static isConstructorInvoke(invokeExpr) { + let methodName = invokeExpr.getMethodSignature().getMethodSubSignature().getMethodName(); + return this.isConstructorMethod(methodName); + } + static isComponentAttributeInvoke(invokeExpr, visitor = new Set()) { + if (visitor.has(invokeExpr)) { + return false; + } + visitor.add(invokeExpr); + let base = invokeExpr.getBase(); + if (!(base instanceof Local)) { + logger$o.error(`PrinterUtils->isComponentAttributeInvoke illegal invoke expr ${invokeExpr}`); + return false; + } + let stmt = base.getDeclaringStmt(); + if (!stmt || !(stmt instanceof ArkAssignStmt)) { + return false; + } + let rightOp = stmt.getRightOp(); + if (rightOp instanceof ArkInstanceInvokeExpr) { + return PrinterUtils.isComponentAttributeInvoke(rightOp, visitor); + } + if (rightOp instanceof ArkStaticInvokeExpr) { + return PrinterUtils.isComponentCreate(rightOp); + } + return false; + } + static isComponentIfBranchInvoke(invokeExpr) { + let className = invokeExpr.getMethodSignature().getDeclaringClassSignature().getClassName(); + let methodName = invokeExpr.getMethodSignature().getMethodSubSignature().getMethodName(); + if (className === COMPONENT_IF && methodName === COMPONENT_BRANCH_FUNCTION) { + return true; + } + return false; + } + static isComponentIfElseInvoke(invokeExpr) { + let className = invokeExpr.getMethodSignature().getDeclaringClassSignature().getClassName(); + let methodName = invokeExpr.getMethodSignature().getMethodSubSignature().getMethodName(); + if (className === COMPONENT_IF && methodName === COMPONENT_BRANCH_FUNCTION) { + let arg0 = invokeExpr.getArg(0); + if (arg0.getValue() === '1') { + return true; + } + } + return false; + } + static getStaticInvokeClassFullName(classSignature, namespace) { + let code = []; + let declareNamespace = classSignature.getDeclaringNamespaceSignature(); + while (declareNamespace !== null) { + let namespaceName = declareNamespace.getNamespaceName(); + if (namespaceName.length > 0 && namespaceName !== namespace?.getName()) { + code.unshift(namespaceName); + declareNamespace = declareNamespace.getDeclaringNamespaceSignature(); + } + else { + break; + } + } + let className = classSignature.getClassName(); + if (className && className.length > 0 && !PrinterUtils.isDefaultClass(className)) { + code.push(className); + } + return code.join('.'); + } + static isIdentifierText(text) { + let ch = text.charCodeAt(0); + if (!ts.isIdentifierStart(ch, ts.ScriptTarget.Latest)) { + return false; + } + for (let i = 1; i < text.length; i++) { + if (!ts.isIdentifierPart(text.charCodeAt(i), ts.ScriptTarget.Latest)) { + return false; + } + } + return true; + } + static escape(text) { + return text + .replace(/\\/g, '\\\\') + .replace(/\f/g, `\\f`) + .replace(/\n/g, `\\n`) + .replace(/\r/g, '\\r') + .replace(/\t/g, '\\t') + .replace(/\v/g, '\\v') + .replace(/\?/g, '\\?') + .replace(/\'/g, "\\'") + .replace(/\"/g, '\\"'); + } +} + +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +let printerOptions = { pureTs: false, noMethodBody: false }; +class BasePrinter extends Printer { + constructor(indent) { + super(indent); + } + printDecorator(docorator) { + docorator.forEach(value => { + this.printer.writeIndent().writeLine(value.toString()); + }); + } + printComments(commentsMetadata) { + const comments = commentsMetadata.getComments(); + comments.forEach(comment => { + this.printer.writeIndent().writeLine(comment.content); + }); + } + modifiersToString(modifiers) { + let modifiersStr = modifiers2stringArray(modifiers); + return modifiersStr.join(' '); + } + resolveMethodName(name) { + if (name === '_Constructor') { + return 'constructor'; + } + if (name.startsWith('Get-')) { + return name.replace('Get-', 'get '); + } + if (name.startsWith('Set-')) { + return name.replace('Set-', 'set '); + } + return name; + } + classOriginTypeToString(clsCategory) { + if (printerOptions.pureTs) { + if (clsCategory === ClassCategory.STRUCT) { + clsCategory = ClassCategory.CLASS; + } + } + return PrinterUtils.classOriginTypeToString.get(clsCategory); + } + static getPrinterOptions() { + return printerOptions; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class SourceBase extends BasePrinter { + arkFile; + inBuilder = false; + constructor(arkFile, indent = '') { + super(indent); + this.arkFile = arkFile; + } + getDeclaringArkNamespace() { + return undefined; + } + getArkFile() { + return this.arkFile; + } + getMethod(signature) { + return this.getArkFile().getScene().getMethod(signature); + } + getClass(signature) { + return this.getArkFile().getScene().getClass(signature); + } + getPrinter() { + return this.printer; + } + transTemp2Code(temp) { + return temp.getName(); + } + isInBuilderMethod() { + return this.inBuilder; + } + resolveKeywordType(keywordStr) { + // 'NumberKeyword | NullKeyword | + let types = []; + for (let keyword of keywordStr.split('|')) { + keyword = keyword.trim(); + if (keyword.length === 0) { + continue; + } + if (keyword.endsWith('Keyword')) { + keyword = keyword.substring(0, keyword.length - 'Keyword'.length).toLowerCase(); + } + types.push(keyword); + } + return types.join(' | '); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @category save + */ +class SourceMethod extends SourceBase { + method; + transformer; + constructor(method, indent = '') { + super(method.getDeclaringArkFile(), indent); + this.method = method; + this.transformer = new SourceTransformer(this); + this.inBuilder = this.initInBuilder(); + } + getDeclaringArkNamespace() { + return this.method.getDeclaringArkClass().getDeclaringArkNamespace(); + } + setInBuilder(inBuilder) { + this.inBuilder = inBuilder; + } + dump() { + this.printer.clear(); + const commentsMetadata = this.method.getMetadata(ArkMetadataKind.LEADING_COMMENTS); + if (commentsMetadata instanceof CommentsMetadata) { + const comments = commentsMetadata.getComments(); + comments.forEach(comment => { + this.printer.writeIndent().writeLine(comment.content); + }); + } + if (!this.method.isDefaultArkMethod()) { + this.printMethod(this.method); + } + else { + this.printBody(this.method); + } + return this.printer.toString(); + } + getLine() { + let line = this.method.getLine(); + if (line === null && this.method.getDeclareLineCols()) { + line = getLineNo(this.method.getDeclareLineCols()[0]); + } + if (line === null) { + line = 0; + } + if (line > 0) { + return line; + } + const stmts = []; + const cfg = this.method.getCfg(); + if (cfg) { + cfg.getStmts() + .reverse() + .forEach(stmt => stmts.push(stmt)); + } + for (const stmt of stmts) { + if (stmt.getOriginPositionInfo().getLineNo() > 0) { + return stmt.getOriginPositionInfo().getLineNo(); + } + } + return line; + } + dumpDefaultMethod() { + let srcBody = new SourceBody(this.printer.getIndent(), this.method, false); + return srcBody.getStmts(); + } + printMethod(method) { + this.printDecorator(method.getDecorators()); + let implementationSig = method.getImplementationSignature(); + if (this.method.getDeclareSignatures()) { + for (const methodSig of this.method.getDeclareSignatures()) { + this.printer.writeIndent().writeLine(`${this.methodProtoToString(methodSig)};`); + } + } + if (!implementationSig) { + return; + } + this.printer.writeIndent().write(this.methodProtoToString(implementationSig)); + // abstract function no body + if (SourceMethod.getPrinterOptions().noMethodBody) { + this.printer.writeIndent().writeLine(`;`); + return; + } + this.printer.writeLine(' {'); + this.printer.incIndent(); + this.printBody(method); + this.printer.decIndent(); + this.printer.writeIndent(); + if (PrinterUtils.isAnonymousMethod(method.getName())) { + this.printer.write('}'); + } + else { + this.printer.writeLine('}'); + } + } + printBody(method) { + let srcBody = new SourceBody(this.printer.getIndent(), method, this.inBuilder); + this.printer.write(srcBody.dump()); + } + methodProtoToString(methodSig) { + let code = new ArkCodeBuffer(); + code.writeSpace(this.modifiersToString(this.method.getModifiers())); + if (!PrinterUtils.isAnonymousMethod(methodSig.getMethodSubSignature().getMethodName())) { + if (this.method.getDeclaringArkClass()?.isDefaultArkClass()) { + code.writeSpace('function'); + } + if (this.method.getAsteriskToken()) { + code.writeSpace('*'); + } + code.write(this.resolveMethodName(methodSig.getMethodSubSignature().getMethodName())); + } + const genericTypes = this.method.getGenericTypes(); + if (genericTypes && genericTypes.length > 0) { + code.write(`<${this.transformer.typeArrayToString(genericTypes)}>`); + } + let parameters = []; + methodSig + .getMethodSubSignature() + .getParameters() + .forEach(parameter => { + let str = parameter.getName(); + if (parameter.hasDotDotDotToken()) { + str = `...${parameter.getName()}`; + } + if (parameter.isOptional()) { + str += '?'; + } + if (parameter.getType()) { + str += ': ' + this.transformer.typeToString(parameter.getType()); + } + if (!str.startsWith(LEXICAL_ENV_NAME_PREFIX)) { + parameters.push(str); + } + }); + code.write(`(${parameters.join(', ')})`); + const returnType = methodSig.getMethodSubSignature().getReturnType(); + if (methodSig.getMethodSubSignature().getMethodName() !== 'constructor' && !(returnType instanceof UnknownType)) { + code.write(`: ${this.transformer.typeToString(returnType)}`); + } + if (PrinterUtils.isAnonymousMethod(methodSig.getMethodSubSignature().getMethodName())) { + code.write(' =>'); + } + return code.toString(); + } + toArrowFunctionTypeString() { + let code = new ArkCodeBuffer(); + let parameters = []; + this.method.getParameters().forEach(parameter => { + let str = parameter.getName(); + if (parameter.isOptional()) { + str += '?'; + } + if (parameter.getType()) { + str += ': ' + this.transformer.typeToString(parameter.getType()); + } + parameters.push(str); + }); + code.write(`(${parameters.join(', ')}) => `); + const returnType = this.method.getReturnType(); + if (!(returnType instanceof UnknownType)) { + code.writeSpace(`${this.transformer.typeToString(returnType)}`); + } + return code.toString(); + } + initInBuilder() { + return (this.method.hasBuilderDecorator() || + ((this.method.getName() === 'build' || this.method.getName() === 'pageTransition') && + !this.method.isStatic() && + this.method.getDeclaringArkClass().hasViewTree())); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$n = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'SourceTransformer'); +class SourceTransformer { + context; + constructor(context) { + this.context = context; + } + anonymousMethodToString(method, indent) { + let mtdPrinter = new SourceMethod(method, indent); + mtdPrinter.setInBuilder(this.context.isInBuilderMethod()); + return mtdPrinter.dump().trimStart(); + } + anonymousClassToString(cls, indent) { + let clsPrinter = new SourceClass(cls, indent); + return clsPrinter.dump().trimStart(); + } + instanceInvokeExprToString(invokeExpr) { + let methodName = invokeExpr.getMethodSignature().getMethodSubSignature().getMethodName(); + if (methodName === INSTANCE_INIT_METHOD_NAME) { + return ''; + } + let args = []; + invokeExpr.getArgs().forEach(v => { + args.push(this.valueToString(v)); + }); + let genericCode = this.genericTypesToString(invokeExpr.getRealGenericTypes()); + if (PrinterUtils.isComponentAttributeInvoke(invokeExpr) && this.context.isInBuilderMethod()) { + return `.${methodName}${genericCode}(${args.join(', ')})`; + } + return `${this.valueToString(invokeExpr.getBase())}.${methodName}${genericCode}(${args.join(', ')})`; + } + transBuilderMethod(className, methodName, args, invokeExpr, genericCode) { + if (className === COMPONENT_CUSTOMVIEW) { + if (methodName === COMPONENT_CREATE_FUNCTION) { + // Anonymous @Builder method + if (args.length > 1) { + // remove the substring '() =>' or '(x, y): type =>' at the beginning of args[1] + const pattern = /^\([^)]*\)\s*:\s*\w*\s*=>\s*/; + args[1] = args[1].replace(pattern, ''); + } + return `${args.join(' ')}`; + } + if (methodName === COMPONENT_POP_FUNCTION) { + return ''; + } + } + if (PrinterUtils.isComponentCreate(invokeExpr)) { + if (className === COMPONENT_IF) { + return `if (${args.join(', ')})`; + } + return `${className}${genericCode}(${args.join(', ')})`; + } + if (PrinterUtils.isComponentIfBranchInvoke(invokeExpr)) { + let arg0 = invokeExpr.getArg(0); + if (arg0.getValue() === '0') { + return ``; + } + else { + return '} else {'; + } + } + if (PrinterUtils.isComponentPop(invokeExpr)) { + return '}'; + } + return null; + } + staticInvokeExprToString(invokeExpr) { + let methodSignature = invokeExpr.getMethodSignature(); + let method = this.context.getMethod(methodSignature); + if (method && PrinterUtils.isAnonymousMethod(method.getName())) { + return this.anonymousMethodToString(method, this.context.getPrinter().getIndent()); + } + let classSignature = methodSignature.getDeclaringClassSignature(); + let className = PrinterUtils.getStaticInvokeClassFullName(classSignature, this.context.getDeclaringArkNamespace()); + let methodName = methodSignature.getMethodSubSignature().getMethodName(); + let args = []; + invokeExpr.getArgs().forEach(v => { + args.push(this.valueToString(v)); + }); + let genericCode = this.genericTypesToString(invokeExpr.getRealGenericTypes()); + if (this.context.isInBuilderMethod()) { + const res = this.transBuilderMethod(className, methodName, args, invokeExpr, genericCode); + if (res !== null) { + return res; + } + } + if (className && className.length > 0 && methodName !== SUPER_NAME) { + return `${className}.${methodName}${genericCode}(${args.join(', ')})`; + } + return `${methodName}${genericCode}(${args.join(', ')})`; + } + genericTypesToString(types) { + if (!types) { + return ''; + } + let code = this.typeArrayToString(types); + if (code.length > 0) { + return `<${code}>`; + } + return ''; + } + typeArrayToString(types, split = ', ') { + let typesStr = []; + types.forEach(t => { + typesStr.push(this.typeToString(t)); + }); + return typesStr.join(split); + } + static constToString(value) { + if (value.getType().toString() === 'string') { + return `'${PrinterUtils.escape(value.getValue())}'`; + } + else if (value.getType().toString() === BIGINT_KEYWORD) { + return `${value.getValue()}n`; + } + else { + return value.getValue(); + } + } + exprToString(expr) { + if (expr instanceof ArkInstanceInvokeExpr) { + return `${this.instanceInvokeExprToString(expr)}`; + } + if (expr instanceof ArkStaticInvokeExpr) { + return `${this.staticInvokeExprToString(expr)}`; + } + if (expr instanceof ArkNewArrayExpr) { + return `new Array<${this.typeToString(expr.getBaseType())}>(${expr.getSize()})`; + } + if (expr instanceof ArkNewExpr) { + return `new ${this.typeToString(expr.getType())}()`; + } + if (expr instanceof ArkDeleteExpr) { + return `delete ${this.valueToString(expr.getField())}`; + } + if (expr instanceof AbstractBinopExpr) { + let op1 = expr.getOp1(); + let op2 = expr.getOp2(); + let operator = expr.getOperator(); + return `${this.valueToString(op1, operator)} ${operator} ${this.valueToString(op2, operator)}`; + } + if (expr instanceof ArkTypeOfExpr) { + return `typeof(${this.valueToString(expr.getOp())})`; + } + if (expr instanceof ArkInstanceOfExpr) { + return `${this.valueToString(expr.getOp())} instanceof ${this.typeToString(expr.getType())}`; + } + if (expr instanceof ArkCastExpr) { + let baseOp = expr.getOp(); + return `${this.valueToString(baseOp)} as ${this.typeToString(expr.getType())}`; + } + if (expr instanceof ArkUnopExpr) { + return `${expr.getOperator()}${this.valueToString(expr.getOp())}`; + } + if (expr instanceof ArkAwaitExpr) { + return `await ${this.valueToString(expr.getPromise())}`; + } + if (expr instanceof ArkYieldExpr) { + return `yield ${this.valueToString(expr.getYieldValue())}`; + } + logger$n.info(`exprToString ${expr.constructor} not support.`); + // ArkPhiExpr + return `${expr}`; + } + refToString(value) { + if (value instanceof ArkInstanceFieldRef) { + return `${this.valueToString(value.getBase())}.${value.getFieldName()}`; + } + if (value instanceof ArkStaticFieldRef) { + return `${value.getFieldSignature().getBaseName()}.${value.getFieldName()}`; + } + if (value instanceof ArkArrayRef) { + let index = value.getIndex(); + if (index instanceof Constant && index.getType() instanceof StringType && PrinterUtils.isTemp(index.getValue())) { + return `${this.valueToString(value.getBase())}[${this.valueToString(new Local(index.getValue()))}]`; + } + return `${this.valueToString(value.getBase())}[${this.valueToString(value.getIndex())}]`; + } + if (value instanceof ArkThisRef) { + return 'this'; + } + // ArkCaughtExceptionRef + logger$n.info(`refToString ${value.constructor} not support.`); + return `${value}`; + } + valueToString(value, operator) { + if (value instanceof AbstractExpr) { + return this.exprToString(value); + } + if (value instanceof AbstractRef) { + return this.refToString(value); + } + if (value instanceof Constant) { + return SourceTransformer.constToString(value); + } + if (value instanceof Local) { + return this.localToString(value, operator); + } + logger$n.info(`valueToString ${value.constructor} not support.`); + return `${value}`; + } + localToString(value, operator) { + if (PrinterUtils.isAnonymousMethod(value.getName())) { + let methodSignature = value.getType().getMethodSignature(); + let anonymousMethod = this.context.getMethod(methodSignature); + if (anonymousMethod) { + return this.anonymousMethodToString(anonymousMethod, this.context.getPrinter().getIndent()); + } + } + if (PrinterUtils.isAnonymousClass(value.getName())) { + let clsSignature = value.getType().getClassSignature(); + let cls = this.context.getClass(clsSignature); + if (cls) { + return this.anonymousClassToString(cls, this.context.getPrinter().getIndent()); + } + } + if (operator === exports.NormalBinaryOperator.Division || operator === exports.NormalBinaryOperator.Multiplication || operator === exports.NormalBinaryOperator.Remainder) { + if (PrinterUtils.isTemp(value.getName())) { + let stmt = value.getDeclaringStmt(); + if (stmt instanceof ArkAssignStmt && stmt.getRightOp() instanceof ArkNormalBinopExpr) { + return `(${this.context.transTemp2Code(value)})`; + } + } + } + return this.context.transTemp2Code(value); + } + literalObjectToString(type) { + let name = type.getClassSignature().getClassName(); + if (PrinterUtils.isAnonymousClass(name)) { + let cls = this.context.getClass(type.getClassSignature()); + if (cls) { + return this.anonymousClassToString(cls, this.context.getPrinter().getIndent()); + } + } + return name; + } + typeToString(type) { + if (type instanceof LiteralType) { + return this.literalType2string(type); + } + if (type instanceof PrimitiveType || type instanceof GenericType) { + return type.getName(); + } + if (type instanceof UnionType || type instanceof IntersectionType) { + return this.multipleType2string(type); + } + if (type instanceof UnknownType) { + return 'any'; + } + if (type instanceof VoidType) { + return 'void'; + } + if (type instanceof ClassType) { + return this.classType2string(type); + } + if (type instanceof ArrayType) { + return this.arrayType2string(type); + } + if (type instanceof TupleType) { + return this.tupleType2string(type); + } + if (type instanceof FunctionType) { + let methodSignature = type.getMethodSignature(); + let method = this.context.getMethod(methodSignature); + if (method && PrinterUtils.isAnonymousMethod(method.getName())) { + return new SourceMethod(method).toArrowFunctionTypeString(); + } + } + if (type instanceof UnclearReferenceType) { + return this.unclearReferenceType2string(type); + } + if (type instanceof AliasType) { + return this.aliasType2string(type); + } + if (type instanceof KeyofTypeExpr) { + return this.keyofTypeExpr2string(type); + } + if (type instanceof TypeQueryExpr) { + return this.typeQueryExpr2string(type); + } + if (!type) { + return 'any'; + } + logger$n.info(`valueToString ${type.constructor} not support.`); + return type.toString(); + } + literalType2string(type) { + let literalName = type.getLiteralName(); + if (typeof literalName === 'string' && literalName.endsWith('Keyword')) { + return literalName.substring(0, literalName.length - 'Keyword'.length).toLowerCase(); + } + return `${literalName}`; + } + multipleType2string(type) { + let typesStr = []; + for (const member of type.getTypes()) { + if (member instanceof UnionType || member instanceof IntersectionType) { + typesStr.push(`(${this.typeToString(member)})`); + } + else { + typesStr.push(this.typeToString(member)); + } + } + if (type instanceof UnionType) { + return typesStr.join(' | '); + } + else { + return typesStr.join(' & '); + } + } + arrayType2string(type) { + const readonly = type.getReadonlyFlag() ? 'readonly ' : ''; + const dimensions = []; + for (let i = 0; i < type.getDimension(); i++) { + dimensions.push('[]'); + } + let baseType = type.getBaseType(); + if (baseType instanceof UnionType || baseType instanceof IntersectionType || baseType instanceof AbstractTypeExpr) { + return `${readonly}(${this.typeToString(baseType)})${dimensions.join('')}`; + } + return `${readonly}${this.typeToString(baseType)}${dimensions.join('')}`; + } + tupleType2string(type) { + const readonly = type.getReadonlyFlag() ? 'readonly ' : ''; + let typesStr = []; + for (const member of type.getTypes()) { + typesStr.push(this.typeToString(member)); + } + return `${readonly}[${typesStr.join(', ')}]`; + } + aliasType2string(type) { + let typesStr = []; + let genericTypes = type.getRealGenericTypes() ?? type.getGenericTypes(); + if (genericTypes) { + for (const gType of genericTypes) { + typesStr.push(this.typeToString(gType)); + } + } + if (typesStr.length > 0) { + return `${type.getName()}<${typesStr.join(', ')}>`; + } + return type.getName(); + } + keyofTypeExpr2string(type) { + if (type.getOpType() instanceof UnionType || type.getOpType() instanceof IntersectionType) { + return `keyof (${this.typeToString(type.getOpType())})`; + } + return `keyof ${this.typeToString(type.getOpType())}`; + } + typeQueryExpr2string(type) { + const gTypes = type.getGenerateTypes(); + const genericStr = this.genericTypesToString(gTypes); + const opValue = type.getOpValue(); + if (opValue instanceof ArkBaseModel) { + if (opValue instanceof ArkClass || opValue instanceof ArkMethod || opValue instanceof ArkNamespace || opValue instanceof ArkField) { + return `typeof ${opValue.getName()}${genericStr}`; + } + else if (opValue instanceof ExportInfo) { + return `typeof ${opValue.getExportClauseName()}${genericStr}`; + } + else if (opValue instanceof ImportInfo) { + return `typeof ${opValue.getImportClauseName()}${genericStr}`; + } + else { + return `typeof *invalid*`; + } + } + else { + return `typeof ${this.valueToString(opValue)}${genericStr}`; + } + } + unclearReferenceType2string(type) { + let genericTypes = type.getGenericTypes(); + if (genericTypes.length > 0) { + return `${type.getName()}<${genericTypes.map(value => this.typeToString(value)).join(', ')}>`; + } + return type.getName(); + } + classType2string(type) { + const name = PrinterUtils.getStaticInvokeClassFullName(type.getClassSignature()); + if (PrinterUtils.isDefaultClass(name)) { + return 'any'; + } + if (PrinterUtils.isAnonymousClass(name)) { + let cls = this.context.getClass(type.getClassSignature()); + if (cls && cls.getCategory() === ClassCategory.TYPE_LITERAL) { + return this.anonymousClassToString(cls, this.context.getPrinter().getIndent()); + } + return 'Object'; + } + let genericTypes = type.getRealGenericTypes(); + if (genericTypes && genericTypes.length > 0) { + return `${name}${this.genericTypesToString(genericTypes)}`; + } + return name; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$m = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'SourceStmt'); +const IGNOR_TYPES = new Set(['any', 'Map', 'Set']); +class SourceStmt { + original; + context; + line; + text = ''; + transformer; + constructor(context, original) { + this.original = original; + this.context = context; + this.line = original.getOriginPositionInfo().getLineNo(); + this.transformer = new SourceTransformer(context); + } + getLine() { + return this.line; + } + setLine(line) { + this.line = line; + } + dump() { + this.beforeDump(); + let code = this.dumpTs(); + this.afterDump(); + return code; + } + beforeDump() { } + afterDump() { } + dumpTs() { + let content = []; + const commentsMetadata = this.original.getMetadata(ArkMetadataKind.LEADING_COMMENTS); + if (commentsMetadata instanceof CommentsMetadata) { + const comments = commentsMetadata.getComments(); + comments.forEach(comment => { + content.push(`${this.printer.getIndent()}${comment.content}\n`); + }); + } + if (this.text.length > 0) { + content.push(`${this.printer.getIndent()}${this.text}\n`); + } + return content.join(''); + } + get printer() { + return this.context.getPrinter(); + } + toString() { + return this.text; + } + setText(text) { + this.text = text; + } + getIntent() { + return this.context.getPrinter().getIndent(); + } + isLocalTempValue(value) { + if (!(value instanceof Local)) { + return false; + } + return PrinterUtils.isTemp(value.getName()); + } +} +var AssignStmtDumpType; +(function (AssignStmtDumpType) { + AssignStmtDumpType[AssignStmtDumpType["NORMAL"] = 0] = "NORMAL"; + AssignStmtDumpType[AssignStmtDumpType["TEMP_REPLACE"] = 1] = "TEMP_REPLACE"; + AssignStmtDumpType[AssignStmtDumpType["COMPONENT_CREATE"] = 2] = "COMPONENT_CREATE"; +})(AssignStmtDumpType || (AssignStmtDumpType = {})); +class SourceAssignStmt extends SourceStmt { + leftOp = ValueUtil.getUndefinedConst(); + rightOp = ValueUtil.getUndefinedConst(); + leftCode = ''; + rightCode = ''; + dumpType; + leftTypeCode; + constructor(context, original) { + super(context, original); + this.leftTypeCode = ''; + } + transfer2ts() { + this.leftOp = this.original.getLeftOp(); + this.rightOp = this.original.getRightOp(); + if ((this.leftOp instanceof Local && this.leftOp.getName() === 'this') || + (this.rightOp instanceof Constant && this.rightOp.getValue() === 'undefined') || + this.rightOp instanceof ArkParameterRef || + this.rightOp instanceof ClosureFieldRef) { + this.setText(''); + this.dumpType = AssignStmtDumpType.NORMAL; + return; + } + this.leftCode = this.transformer.valueToString(this.leftOp); + if (this.leftOp instanceof Local && this.rightOp instanceof ArkNewExpr) { + this.transferRightNewExpr(); + } + else if (this.leftOp instanceof Local && this.rightOp instanceof ArkNewArrayExpr) { + this.transferRightNewArrayExpr(); + } + else if (this.rightOp instanceof ArkStaticInvokeExpr && PrinterUtils.isComponentCreate(this.rightOp)) { + this.transferRightComponentCreate(); + } + else if (this.rightOp instanceof ArkInstanceInvokeExpr && PrinterUtils.isConstructorInvoke(this.rightOp)) { + this.transferConstructorInvokeExpr(this.rightOp); + } + else if (this.rightOp instanceof ArkInstanceInvokeExpr && PrinterUtils.isComponentAttributeInvoke(this.rightOp)) { + this.transferRightComponentAttribute(); + } + else { + this.rightCode = this.transformer.valueToString(this.rightOp); + } + if (this.isLocalTempValue(this.leftOp)) { + this.context.setTempCode(this.leftOp.getName(), this.rightCode); + } + if ((this.leftOp instanceof ArkInstanceFieldRef && this.leftOp.getBase().getName() === 'this') || this.leftOp instanceof ArkStaticFieldRef) { + this.context.setTempCode(this.leftOp.getFieldName(), this.rightCode); + } + if (this.dumpType === undefined) { + this.setText(`${this.leftCode} = ${this.rightCode}`); + this.dumpType = AssignStmtDumpType.TEMP_REPLACE; + } + let leftOpType = this.leftOp.getType(); + if (leftOpType instanceof ClassType) { + let name = leftOpType.getClassSignature().getClassName(); + if (PrinterUtils.isAnonymousClass(name)) { + this.leftTypeCode = 'any'; + } + else { + this.leftTypeCode = name; + } + } + else { + this.leftTypeCode = this.transformer.typeToString(leftOpType); + } + if (IGNOR_TYPES.has(this.leftTypeCode)) { + this.leftTypeCode = ''; + } + } + beforeDump() { + if (this.dumpType !== AssignStmtDumpType.TEMP_REPLACE) { + return; + } + if (this.context.hasTempVisit(this.leftCode)) { + this.setText(''); + return; + } + else if (PrinterUtils.isTemp(this.leftCode)) { + this.setText(`${this.rightCode};`); + return; + } + if (this.leftOp instanceof Local && this.context.getLocals().has(this.leftOp.getName()) && !this.isLocalTempValue(this.leftOp)) { + if (this.context.isLocalDefined(this.leftOp)) { + this.setText(`${this.leftCode} = ${this.rightCode};`); + return; + } + let flag = this.leftOp.getConstFlag() ? 'const' : 'let'; + if (this.context.getArkFile().getExportInfoBy(this.leftCode) && this.context.isInDefaultMethod()) { + this.setText(`export ${flag} ${this.leftCode} = ${this.rightCode};`); + } + else { + if (this.leftTypeCode.length > 0) { + this.setText(`${flag} ${this.leftCode}: ${this.leftTypeCode} = ${this.rightCode};`); + } + else { + this.setText(`${flag} ${this.leftCode} = ${this.rightCode};`); + } + } + this.context.defineLocal(this.leftOp); + } + else { + this.setText(`${this.leftCode} = ${this.rightCode};`); + } + } + afterDump() { + if (this.dumpType === AssignStmtDumpType.COMPONENT_CREATE) { + this.printer.incIndent(); + } + } + getClassOriginType(type) { + if (!(type instanceof ClassType)) { + return undefined; + } + let signature = type.getClassSignature(); + let cls = this.context.getClass(signature); + if (!cls) { + return undefined; + } + return PrinterUtils.getOriginType(cls); + } + /** + * temp1 = new Person + * temp1.constructor(10) + */ + transferRightNewExpr() { + let originType = this.getClassOriginType(this.rightOp.getType()); + if (this.context.getStmtReader().hasNext()) { + let stmt = this.context.getStmtReader().next(); + if (stmt instanceof ArkAssignStmt && stmt.getRightOp() instanceof ArkInstanceInvokeExpr) { + let instanceInvokeExpr = stmt.getRightOp(); + if ('constructor' === instanceInvokeExpr.getMethodSignature().getMethodSubSignature().getMethodName() && + instanceInvokeExpr.getBase().getName() === this.leftOp.getName()) { + this.handleConstructorInvoke(instanceInvokeExpr, originType); + return; + } + } + { + this.context.getStmtReader().rollback(); + } + } + if (originType === CLASS_CATEGORY_COMPONENT) { + this.rightCode = `${this.transformer.typeToString(this.rightOp.getType())}()`; + } + else if (originType === ClassCategory.TYPE_LITERAL || originType === ClassCategory.OBJECT) { + this.rightCode = `${this.transformer.typeToString(this.rightOp.getType())}`; + } + else { + this.rightCode = `new ${this.transformer.typeToString(this.rightOp.getType())}()`; + } + } + handleConstructorInvoke(instanceInvokeExpr, originType) { + let args = []; + instanceInvokeExpr.getArgs().forEach(v => { + args.push(this.transformer.valueToString(v)); + }); + if (originType === CLASS_CATEGORY_COMPONENT) { + this.rightCode = `${this.transformer.typeToString(this.rightOp.getType())}(${args.join(', ')})`; + } + else if (originType === ClassCategory.TYPE_LITERAL || originType === ClassCategory.OBJECT) { + this.rightCode = `${this.transformer.literalObjectToString(this.rightOp.getType())}`; + } + else { + this.rightCode = `new ${this.transformer.typeToString(this.rightOp.getType())}(${args.join(', ')})`; + } + } + transferConstructorInvokeExpr(expr) { + let rightCode = this.transformer.valueToString(this.rightOp); + const pattern = /\([^)]*\)\.constructor/; + this.rightCode = rightCode.replace(pattern, ''); + this.dumpType = AssignStmtDumpType.NORMAL; + } + /** + * $temp0 = newarray[4] + * $temp0[0] = 1 + * $temp0[1] = 2 + * $temp0[2] = 3 + */ + transferRightNewArrayExpr() { + let arrayExpr = new SourceNewArrayExpr(this.rightOp); + let localName = this.leftOp.getName(); + while (this.context.getStmtReader().hasNext()) { + let stmt = this.context.getStmtReader().next(); + if (stmt instanceof ArkAssignStmt) { + let left = stmt.getLeftOp(); + if (left instanceof ArkArrayRef && left.getBase().getName() === localName) { + arrayExpr.addInitValue(this.transformer.valueToString(stmt.getRightOp())); + } + else { + this.context.getStmtReader().rollback(); + break; + } + } + else { + this.context.getStmtReader().rollback(); + break; + } + } + this.rightCode = arrayExpr.toString(); + } + transferRightComponentCreate() { + this.rightCode = this.transformer.valueToString(this.rightOp); + if (this.context.getStmtReader().hasNext()) { + let stmt = this.context.getStmtReader().next(); + if (stmt instanceof ArkInvokeStmt) { + let expr = stmt.getInvokeExpr(); + if (expr instanceof ArkStaticInvokeExpr && PrinterUtils.isComponentPop(expr)) { + this.setText(`${this.rightCode}`); + this.dumpType = AssignStmtDumpType.NORMAL; + return; + } + } + this.context.getStmtReader().rollback(); + } + this.setText(`${this.rightCode} {`); + this.dumpType = AssignStmtDumpType.COMPONENT_CREATE; + } + transferRightComponentAttribute() { + this.rightCode = this.transformer.valueToString(this.rightOp); + this.setText(`${this.rightCode}`); + this.dumpType = AssignStmtDumpType.NORMAL; + } +} +class SourceInvokeStmt extends SourceStmt { + constructor(context, original) { + super(context, original); + } + transfer2ts() { + let invokeExpr = this.original.getInvokeExpr(); + let code = ''; + let isAttr = false; + if (invokeExpr instanceof ArkStaticInvokeExpr) { + if (PrinterUtils.isComponentPop(invokeExpr)) { + code = '}'; + isAttr = true; + } + else { + code = this.transformer.staticInvokeExprToString(invokeExpr); + isAttr = PrinterUtils.isComponentIfElseInvoke(invokeExpr); + } + } + else if (invokeExpr instanceof ArkInstanceInvokeExpr) { + code = this.transformer.instanceInvokeExprToString(invokeExpr); + isAttr = PrinterUtils.isComponentAttributeInvoke(invokeExpr); + } + if (code.length > 0 && !isAttr) { + this.setText(`${code};`); + } + else { + this.setText(`${code}`); + } + } + beforeDump() { + let invokeExpr = this.original.getInvokeExpr(); + if ((invokeExpr instanceof ArkStaticInvokeExpr && PrinterUtils.isComponentPop(invokeExpr)) || + (invokeExpr instanceof ArkStaticInvokeExpr && PrinterUtils.isComponentIfElseInvoke(invokeExpr))) { + this.printer.decIndent(); + return; + } + } + afterDump() { + let invokeExpr = this.original.getInvokeExpr(); + if (invokeExpr instanceof ArkStaticInvokeExpr && PrinterUtils.isComponentIfElseInvoke(invokeExpr)) { + this.printer.incIndent(); + return; + } + } +} +class SourceIfStmt extends SourceStmt { + constructor(context, original) { + super(context, original); + } + transfer2ts() { + let code; + let expr = this.original.getConditionExpr(); + code = `if (${this.transformer.valueToString(expr.getOp1())}`; + code += ` ${expr.getOperator()} `; + code += `${this.transformer.valueToString(expr.getOp2())}) {`; + this.setText(code); + } + afterDump() { + this.printer.incIndent(); + } +} +class SourceWhileStmt extends SourceStmt { + block; + constructor(context, original, block) { + super(context, original); + this.block = block; + } + afterDump() { + this.printer.incIndent(); + } + /** + * $temp2 = $temp1.next() + * $temp3 = $temp2.done() + * if $temp3 === true + * $temp4 = $temp2.value + * $temp5 = <> cast + * @returns + */ + forOf2ts() { + let expr = this.original.getConditionExpr(); + let temp3 = expr.getOp1(); + let op2 = expr.getOp2(); + let firstStmt = this.context.getStmtReader().first(); + if (!(firstStmt instanceof ArkAssignStmt)) { + return false; + } + if (!(this.isLocalTempValue(temp3) && op2 instanceof Constant && op2.getValue() === 'true')) { + return false; + } + let stmt = temp3.getDeclaringStmt(); + if (!(stmt instanceof ArkAssignStmt)) { + return false; + } + let done = stmt.getRightOp(); + if (!(done instanceof ArkInstanceFieldRef)) { + return false; + } + if (done.getFieldSignature().toString() !== '@ES2015/BuiltinClass: IteratorResult.done') { + return false; + } + let temp2 = done.getBase(); + if (!(temp2 instanceof Local)) { + return false; + } + stmt = temp2.getDeclaringStmt(); + if (!(stmt instanceof ArkAssignStmt)) { + return false; + } + let next = stmt.getRightOp(); + if (!(next instanceof ArkInstanceInvokeExpr)) { + return false; + } + if (next.getMethodSignature().getMethodSubSignature().getMethodName() !== 'next') { + return false; + } + let temp1 = next.getBase(); + if (!(temp1 instanceof Local)) { + return false; + } + stmt = temp1.getDeclaringStmt(); + if (!(stmt instanceof ArkAssignStmt)) { + return false; + } + let iterator = stmt.getRightOp(); + if (!(iterator instanceof ArkInstanceInvokeExpr)) { + return false; + } + if (iterator.getMethodSignature().getMethodSubSignature().getMethodName() !== 'iterator') { + return false; + } + let successors = this.block.getSuccessors(); + if (successors.length !== 2) { + return false; + } + let stmts = successors[0].getStmts(); + if (stmts.length < 2) { + return false; + } + stmt = stmts[1]; + if (!(stmt instanceof ArkAssignStmt)) { + return false; + } + this.context.setSkipStmt(stmts[0]); + this.context.setSkipStmt(stmts[1]); + while (this.context.getStmtReader().hasNext()) { + this.context.getStmtReader().next(); + } + let v = stmt.getLeftOp(); + let valueName = v.getName(); + if (!this.isLocalTempValue(v)) { + this.setText(`for (let ${valueName} of ${this.transformer.valueToString(iterator.getBase())}) {`); + this.context.setTempVisit(temp1.getName()); + this.context.setTempVisit(temp3.getName()); + return true; + } + // iterate map 'for (let [key, value] of map)' + let stmtReader = new StmtReader(stmts); + stmtReader.next(); + stmtReader.next(); + let arrayValueNames = []; + while (stmtReader.hasNext()) { + stmt = stmtReader.next(); + if (!(stmt instanceof ArkAssignStmt)) { + break; + } + let ref = stmt.getRightOp(); + if (!(ref instanceof ArkArrayRef)) { + break; + } + if (ref.getBase().getName() !== valueName) { + break; + } + let name = stmt.getLeftOp().getName(); + arrayValueNames.push(name); + this.context.setTempVisit(name); + } + this.setText(`for (let [${arrayValueNames.join(', ')}] of ${this.transformer.valueToString(iterator.getBase())}) {`); + this.context.setTempVisit(temp3.getName()); + return true; + } + transfer2ts() { + if (this.forOf2ts()) { + return; + } + let code; + let expr = this.original.getConditionExpr(); + code = `while (${this.valueToString(expr.getOp1())}`; + code += ` ${expr.getOperator().trim()} `; + code += `${this.valueToString(expr.getOp2())}) {`; + this.setText(code); + } + valueToString(value) { + if (!(value instanceof Local)) { + return this.transformer.valueToString(value); + } + for (const stmt of this.block.getStmts()) { + if (!(stmt instanceof ArkAssignStmt)) { + continue; + } + if (PrinterUtils.isDeIncrementStmt(stmt, exports.NormalBinaryOperator.Addition) && stmt.getLeftOp().getName() === value.getName()) { + this.context.setSkipStmt(stmt); + return `${value.getName()}++`; + } + if (PrinterUtils.isDeIncrementStmt(stmt, exports.NormalBinaryOperator.Subtraction) && stmt.getLeftOp().getName() === value.getName()) { + this.context.setSkipStmt(stmt); + return `${value.getName()}--`; + } + } + return this.transformer.valueToString(value); + } +} +class SourceForStmt extends SourceWhileStmt { + incBlock; + constructor(context, original, block, incBlock) { + super(context, original, block); + this.incBlock = incBlock; + } + transfer2ts() { + let code; + let expr = this.original.getConditionExpr(); + code = `for (; ${this.transformer.valueToString(expr.getOp1())}`; + code += ` ${expr.getOperator().trim()} `; + code += `${this.transformer.valueToString(expr.getOp2())}; `; + let stmtReader = new StmtReader(this.incBlock.getStmts()); + while (stmtReader.hasNext()) { + let sourceStmt = stmt2SourceStmt(this.context, stmtReader.next()); + sourceStmt.transfer2ts(); + code += sourceStmt.toString(); + if (stmtReader.hasNext()) { + code += ', '; + } + } + code += `) {`; + this.setText(code); + } +} +class SourceDoStmt extends SourceStmt { + constructor(context, stmt) { + super(context, stmt); + } + transfer2ts() { + this.setText('do {'); + } + afterDump() { + this.printer.incIndent(); + } +} +class SourceDoWhileStmt extends SourceWhileStmt { + constructor(context, stmt, block) { + super(context, stmt, block); + } + transfer2ts() { + let code; + let expr = this.original.getConditionExpr(); + code = `} while (${this.valueToString(expr.getOp1())}`; + code += ` ${expr.getOperator().trim()} `; + code += `${this.valueToString(expr.getOp2())})`; + this.setText(code); + } + beforeDump() { + this.printer.decIndent(); + } + afterDump() { } +} +class SourceElseStmt extends SourceStmt { + constructor(context, original) { + super(context, original); + } + transfer2ts() { + this.setText('} else {'); + } + beforeDump() { + this.printer.decIndent(); + } + afterDump() { + this.printer.incIndent(); + } +} +class SourceContinueStmt extends SourceStmt { + constructor(context, original) { + super(context, original); + } + // trans 2 break or continue + transfer2ts() { + this.setText('continue;'); + } +} +class SourceBreakStmt extends SourceStmt { + constructor(context, original) { + super(context, original); + } + // trans 2 break or continue + transfer2ts() { + this.setText('break;'); + } +} +class SourceReturnStmt extends SourceStmt { + constructor(context, original) { + super(context, original); + } + transfer2ts() { + this.setText(`return ${this.transformer.valueToString(this.original.getOp())};`); + } +} +class SourceReturnVoidStmt extends SourceStmt { + constructor(context, original) { + super(context, original); + } + transfer2ts() { + if (this.original.getOriginPositionInfo().getLineNo() <= 0) { + this.setText(''); + } + else { + this.setText('return;'); + } + } +} +class SourceCompoundEndStmt extends SourceStmt { + constructor(context, stmt, text) { + super(context, stmt); + this.setText(text); + } + transfer2ts() { } + beforeDump() { + this.printer.decIndent(); + } +} +class SourceCommonStmt extends SourceStmt { + constructor(context, stmt) { + super(context, stmt); + } + transfer2ts() { + this.setText(this.original.toString()); + } +} +class SourceThrowStmt extends SourceStmt { + constructor(context, original) { + super(context, original); + } + transfer2ts() { + this.setText(`throw ${this.transformer.valueToString(this.original.getOp())};`); + } +} +class SourceTypeAliasStmt extends SourceStmt { + aliasType; + constructor(context, original, aliasType) { + super(context, original); + this.aliasType = aliasType; + } + transfer2ts() { + let modifiersArray = modifiers2stringArray(this.aliasType.getModifiers()); + let modifier = modifiersArray.length > 0 ? `${modifiersArray.join(' ')} ` : ''; + const expr = this.original.getAliasTypeExpr(); + let typeOf = expr.getTransferWithTypeOf() ? 'typeof ' : ''; + let realGenericTypes = expr.getRealGenericTypes() ? `<${expr.getRealGenericTypes().join(', ')}>` : ''; + let genericTypes = this.aliasType.getGenericTypes() ? `<${this.transformer.typeArrayToString(this.aliasType.getGenericTypes())}>` : ''; + let typeObject = expr.getOriginalObject(); + if (typeObject instanceof Type) { + if (typeObject instanceof AliasType) { + this.setText(`${modifier}type ${this.aliasType.getName()}${genericTypes} = ${typeOf}${typeObject.getName()}${realGenericTypes};`); + } + else { + this.setText(`${modifier}type ${this.aliasType.getName()}${genericTypes} = ${typeOf}${this.transformer.typeToString(typeObject)}${realGenericTypes};`); + } + return; + } + if (typeObject instanceof ImportInfo) { + let exprStr = `import('${typeObject.getFrom()}')`; + if (typeObject.getImportClauseName() !== '') { + exprStr = `${exprStr}.${typeObject.getImportClauseName()}`; + } + this.setText(`${modifier}type ${this.aliasType.getName()}${genericTypes} = ${typeOf}${exprStr}${realGenericTypes};`); + return; + } + if (typeObject instanceof Local) { + this.setText(`${modifier}type ${this.aliasType.getName()}${genericTypes} = ${typeOf}${this.transformer.valueToString(typeObject)}${realGenericTypes};`); + return; + } + if (typeObject instanceof ArkClass) { + let classTS = this.generateClassTS(typeObject); + this.setText(`${modifier}type ${this.aliasType.getName()}${genericTypes} = ${typeOf}${classTS}${realGenericTypes};`); + return; + } + if (typeObject instanceof ArkMethod) { + this.setText(`${modifier}type ${this.aliasType.getName()}${genericTypes} = ${typeOf}${typeObject.getName()}${realGenericTypes};`); + return; + } + this.setText(`${modifier}type ${this.aliasType.getName()}${genericTypes} = ${typeOf}${typeObject.getName()}${realGenericTypes};`); + } + generateClassTS(arkClass) { + let res = ''; + let classType = new ClassType(arkClass.getSignature()); + if (arkClass.getCategory() === ClassCategory.TYPE_LITERAL || arkClass.getCategory() === ClassCategory.OBJECT) { + res = this.transformer.literalObjectToString(classType); + } + else { + res = this.transformer.typeToString(classType); + } + return res; + } +} +class SourceTryStmt extends SourceStmt { + constructor(context, stmt) { + super(context, stmt); + } + transfer2ts() { + this.setText('try {'); + } + afterDump() { + this.printer.incIndent(); + } +} +class SourceCatchStmt extends SourceStmt { + block; + constructor(context, stmt, block) { + super(context, stmt); + this.block = block; + } + transfer2ts() { + if (this.block) { + let stmt = this.block.getStmts()[0]; + if (stmt instanceof ArkAssignStmt) { + if (stmt.getLeftOp() instanceof Local) { + let name = stmt.getLeftOp().getName(); + this.setText(`} catch (${name}) {`); + this.context.setSkipStmt(stmt); + return; + } + } + } + this.setText('} catch (e) {'); + } + beforeDump() { + this.printer.decIndent(); + } + afterDump() { + this.printer.incIndent(); + } +} +class SourceFinallyStmt extends SourceStmt { + constructor(context, stmt) { + super(context, stmt); + } + transfer2ts() { + this.setText('} finally {'); + } + beforeDump() { + this.printer.decIndent(); + } + afterDump() { + this.printer.incIndent(); + } +} +class SourceNewArrayExpr { + expr; + values; + constructor(expr) { + this.expr = expr; + this.values = []; + } + addInitValue(value) { + this.values.push(value); + } + toString() { + return `[${this.values.join(', ')}]`; + } +} +function stmt2SourceStmt(context, stmt) { + if (stmt instanceof ArkAssignStmt) { + return new SourceAssignStmt(context, stmt); + } + if (stmt instanceof ArkInvokeStmt) { + return new SourceInvokeStmt(context, stmt); + } + if (stmt instanceof ArkReturnVoidStmt) { + return new SourceReturnVoidStmt(context, stmt); + } + if (stmt instanceof ArkReturnStmt) { + return new SourceReturnStmt(context, stmt); + } + if (stmt instanceof ArkThrowStmt) { + return new SourceThrowStmt(context, stmt); + } + if (stmt instanceof ArkAliasTypeDefineStmt) { + return new SourceTypeAliasStmt(context, stmt, stmt.getAliasType()); + } + logger$m.info(`stmt2SourceStmt ${stmt.constructor} not support.`); + return new SourceCommonStmt(context, stmt); +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var CodeBlockType; +(function (CodeBlockType) { + CodeBlockType[CodeBlockType["NORMAL"] = 0] = "NORMAL"; + CodeBlockType[CodeBlockType["IF"] = 1] = "IF"; + CodeBlockType[CodeBlockType["ELSE"] = 2] = "ELSE"; + CodeBlockType[CodeBlockType["BREAK"] = 3] = "BREAK"; + CodeBlockType[CodeBlockType["CONTINUE"] = 4] = "CONTINUE"; + CodeBlockType[CodeBlockType["DO"] = 5] = "DO"; + CodeBlockType[CodeBlockType["DO_WHILE"] = 6] = "DO_WHILE"; + CodeBlockType[CodeBlockType["WHILE"] = 7] = "WHILE"; + CodeBlockType[CodeBlockType["FOR"] = 8] = "FOR"; + CodeBlockType[CodeBlockType["COMPOUND_END"] = 9] = "COMPOUND_END"; + CodeBlockType[CodeBlockType["TRY"] = 10] = "TRY"; + CodeBlockType[CodeBlockType["CATCH"] = 11] = "CATCH"; + CodeBlockType[CodeBlockType["FINALLY"] = 12] = "FINALLY"; +})(CodeBlockType || (CodeBlockType = {})); +class AbstractFlowGraph { + nodes = []; + entry; + block2NodeMap; + structOf = new Map(); + structTypes = new Map(); + structBlocks = new Map(); + loopMap = new Map(); + constructor(cfg, traps) { + this.block2NodeMap = new Map(); + for (const bb of cfg.getBlocks()) { + let an = new AbstractNode(); + an.setBlock(bb); + this.block2NodeMap.set(bb, an); + } + for (const bb of cfg.getBlocks()) { + let an = this.block2NodeMap.get(bb); + for (const succ of bb.getSuccessors()) { + an.addSucc(this.block2NodeMap.get(succ)); + } + for (const pred of bb.getPredecessors()) { + an.addPred(this.block2NodeMap.get(pred)); + } + } + let trapRegions = this.buildTrap(traps); + this.searchTrapFinallyNodes(trapRegions); + this.trapsStructuralAnalysis(trapRegions); + this.entry = this.block2NodeMap.get(cfg.getStartingBlock()); + this.entry = this.structuralAnalysis(this.entry); + } + getEntry() { + return this.entry; + } + getForIncBlock(block) { + let node = this.block2NodeMap.get(block); + let loop = this.loopMap.get(node); + return loop.inc.getBlock(); + } + preOrder(node, callback, visitor = new Set()) { + visitor.add(node); + node.traversal(callback, CodeBlockType.NORMAL); + for (const succ of node.getSucc()) { + if (!visitor.has(succ)) { + this.preOrder(succ, callback, visitor); + } + } + } + structuralAnalysis(entry, scope) { + let preds = entry.getPred(); + let entryBak = entry; + this.nodes = this.dfsPostOrder(entry, scope); + this.entry = entry; + this.buildCyclicStructural(); + // acyclic structural + let postMax = this.nodes.length; + let change = true; + while (postMax > 1 && change) { + change = false; + for (let i = 0; i < postMax; i++) { + let node = this.nodes[i]; + let nset = new Set(); + let rtype = this.identifyRegionType(node, nset, scope); + if (!rtype) { + continue; + } + let p = this.reduce(rtype, nset); + if (!p) { + continue; + } + scope?.add(p); + if (nset.has(entry)) { + entry = p; + } + this.nodes = this.dfsPostOrder(entry, scope); + change = postMax !== this.nodes.length; + postMax = this.nodes.length; + } + } + for (const pred of preds) { + pred.replaceSucc(entryBak, entry); + entry.addPred(pred); + } + return entry; + } + dfsPostOrder(node, scope, visitor = new Set(), postOrder = []) { + visitor.add(node); + for (const succ of node.getSucc()) { + if (visitor.has(succ)) { + continue; + } + if (scope && !scope.has(succ)) { + continue; + } + this.dfsPostOrder(succ, scope, visitor, postOrder); + } + postOrder.push(node); + return postOrder; + } + buildCyclicStructural() { + for (const loop of this.prepareBuildLoops()) { + let nset = new Set(); + for (const n of loop) { + if (this.structOf.has(n)) { + nset.add(this.structOf.get(n)); + } + else { + nset.add(n); + } + } + let rtype = this.cyclicRegionType(nset); + let region = this.createRegion(rtype, nset); + region.revise(); + this.structTypes.set(region, rtype); + let blocks = new Set(); + for (const s of nset) { + this.handleRegion(s, region, blocks); + } + this.structBlocks.set(region, blocks); + this.loopMap.set(region.header, region); + } + } + handleRegion(s, region, blocks) { + if (!this.structOf.has(s)) { + this.structOf.set(s, region); + } + if (this.structBlocks.has(s)) { + for (const b of this.structBlocks.get(s)) { + blocks.add(b); + } + } + else { + blocks.add(s); + } + } + prepareBuildLoops() { + let dom = this.buildDominator(); + let loops = []; + for (const header of this.nodes) { + let innermost; + let longest = 0; + let backEdges = this.getBackEdges(dom, header); + if (backEdges.size === 0) { + continue; + } + if (this.isSelfLoopNode(header)) { + loops.push(new Set([header])); + } + for (const start of backEdges) { + let loop = this.naturalLoop(start, header); + if (!innermost || loop.size > longest) { + innermost = loop; + longest = loop.size; + } + } + loops.push(innermost); + } + loops.sort((a, b) => a.size - b.size); + return loops; + } + buildDominator() { + let domin = new Map(); + domin.set(this.entry, new Set([this.entry])); + for (const node of this.nodes) { + if (node !== this.entry) { + domin.set(node, new Set(this.nodes)); + } + } + let change = true; + while (change) { + change = false; + for (const node of this.nodes) { + if (node === this.entry) { + continue; + } + let t = new Set(domin.get(node)); + for (const p of node.getPred()) { + t = this.setIntersect(t, domin.get(p)); + } + t.add(node); + if (!this.isSetEqual(t, domin.get(node))) { + change = true; + domin.set(node, t); + } + } + } + return domin; + } + getBackEdges(dom, header) { + let backEdges = new Set(); + for (const n of header.getPred()) { + // h dom n && n -> h + if (dom.get(n)?.has(header)) { + backEdges.add(n); + } + } + return backEdges; + } + naturalLoop(backEdgeStart, backEdgeEnd) { + let stack = []; + let loop = new Set([backEdgeEnd, backEdgeStart]); + stack.push(backEdgeStart); + while (stack.length > 0) { + let m = stack.shift(); + for (const pred of m.getPred()) { + if (loop.has(pred)) { + continue; + } + loop.add(pred); + stack.push(pred); + } + } + return loop; + } + isSelfLoopNode(node) { + let inSucc = false; + let inPred = false; + for (const pred of node.getPred()) { + if (pred === node) { + inPred = true; + } + } + for (const succ of node.getSucc()) { + if (succ === node) { + inSucc = true; + } + } + return inSucc && inPred; + } + isForLoopIncNode(node) { + for (const loop of this.loopMap.values()) { + if (loop.getType() === RegionType.FOR_LOOP_REGION) { + if (node === loop.inc) { + return true; + } + } + } + return false; + } + isValidInBlocks(node, scope) { + if (this.isForLoopIncNode(node) || node.hasIfStmt()) { + return false; + } + if (scope && !scope.has(node)) { + return false; + } + return true; + } + isIfRegion(node, nodeSet) { + nodeSet.clear(); + if (node.getSucc().length !== 2) { + return false; + } + let m = node.getSucc()[0]; + let n = node.getSucc()[1]; + if (m.getSucc().length === 1 && m.getSucc()[0] === n) { + nodeSet.add(node).add(m); + return true; + } + return false; + } + isIfExitRegion(node, nodeSet) { + nodeSet.clear(); + if (node.getSucc().length !== 2) { + return false; + } + let m = node.getSucc()[0]; + if (m.hasReturnStmt()) { + nodeSet.add(node).add(m); + return true; + } + return false; + } + isIfElseRegion(node, nodeSet) { + nodeSet.clear(); + if (node.getSucc().length !== 2) { + return false; + } + let m = node.getSucc()[0]; + let n = node.getSucc()[1]; + if ((m.getSucc().length === 1 && + n.getSucc().length === 1 && + m.getPred().length === 1 && + n.getPred().length === 1 && + m.getSucc()[0] === n.getSucc()[0]) || + (m.getSucc().length === 0 && n.getSucc().length === 0)) { + nodeSet.add(node).add(m).add(n); + return true; + } + return false; + } + isBlockRegion(node, nodeSet, scope) { + let n = node; + let p = true; + let s = n.getSucc().length === 1; + nodeSet.clear(); + let blocks = []; + while (p && s && !nodeSet.has(n) && this.isValidInBlocks(n, scope)) { + nodeSet.add(n); + blocks.push(n); + n = n.getSucc()[0]; + p = n.getPred().length === 1; + s = n.getSucc().length === 1; + } + if (p && this.isValidInBlocks(n, scope)) { + if (!nodeSet.has(n)) { + blocks.push(n); + } + nodeSet.add(n); + } + n = node; + p = n.getPred().length === 1; + s = true; + while (p && s && this.isValidInBlocks(n, scope)) { + if (!nodeSet.has(n)) { + blocks.unshift(n); + } + nodeSet.add(n); + n = n.getPred()[0]; + if (nodeSet.has(n)) { + break; + } + p = n.getPred().length === 1; + s = n.getSucc().length === 1; + } + if (s && this.isValidInBlocks(n, scope)) { + if (!nodeSet.has(n)) { + blocks.unshift(n); + } + nodeSet.add(n); + } + nodeSet.clear(); + for (const n of blocks) { + nodeSet.add(n); + } + if (nodeSet.size >= 2) { + return true; + } + return false; + } + isIfBreakRegion(node, nodeSet, loop) { + let m = node.getSucc()[0]; + nodeSet.clear(); + if (this.isExitLoop(m, this.structBlocks.get(loop))) { + nodeSet.add(node); + return true; + } + if (m.getSucc().length === 1 && this.isExitLoop(m.getSucc()[0], this.structBlocks.get(loop))) { + nodeSet.add(node).add(m); + return true; + } + return false; + } + isIfContinueRegion(node, nodeSet, loop) { + nodeSet.clear(); + let m = node.getSucc()[0]; + let n = node.getSucc()[1]; + if (loop.control.has(m)) { + nodeSet.add(node); + return true; + } + if (m.getSucc().length === 1 && loop.control.has(m.getSucc()[0]) && !loop.control.has(n) && !this.isIfElseRegion(node, nodeSet)) { + nodeSet.add(node).add(m); + return true; + } + return false; + } + isWhileRegion(node, nodeSet, loop) { + nodeSet.clear(); + let m = node.getSucc()[0]; + if (loop.header === node && m.getSucc().length === 1 && m.getPred().length === 1 && m.getSucc()[0] === node) { + nodeSet.add(node).add(m); + return true; + } + return false; + } + isForRegion(node, nodeSet, loop) { + nodeSet.clear(); + if (loop.header === node && loop.getType() === RegionType.FOR_LOOP_REGION) { + let forLoop = loop; + let blocks = node.getSucc()[0]; + if (forLoop.inc.getPred().length === 1 && forLoop.inc.getPred()[0] === blocks && blocks.getSucc().length === 1) { + nodeSet.add(node).add(forLoop.inc).add(blocks); + return true; + } + } + return false; + } + isDoWhileRegion(node, nodeSet, loop) { + nodeSet.clear(); + if (loop.back === node && loop.getType() === RegionType.DO_WHILE_LOOP_REGION) { + let blocks = node.getPred()[0]; + if (blocks.getSucc().length === 1 && blocks.getSucc()[0] === node && node.getSucc()[0] === blocks) { + nodeSet.add(blocks).add(node); + return true; + } + } + return false; + } + identifyRegionType(node, nodeSet, scope) { + if (this.isBlockRegion(node, nodeSet, scope)) { + return RegionType.BLOCK_REGION; + } + let inLoop = false; + let region = this.structOf.get(node); + if (region && LOOP_TYPES.has(region?.getType())) { + inLoop = true; + } + if (new Set(node.getPred()).has(node) && new Set(node.getSucc()).has(node)) { + nodeSet.add(node); + if (inLoop) { + return region?.getType(); + } + return RegionType.SELF_LOOP_REGION; + } + if (node.getSucc().length !== 2) { + return undefined; + } + if (inLoop) { + let loop = region; + if (!loop.control.has(node)) { + if (this.isIfBreakRegion(node, nodeSet, loop)) { + return RegionType.IF_THEN_BREAK_REGION; + } + if (this.isIfContinueRegion(node, nodeSet, loop)) { + return RegionType.IF_THEN_CONTINUE_REGION; + } + } + if (this.isWhileRegion(node, nodeSet, loop)) { + return RegionType.WHILE_LOOP_REGION; + } + if (this.isForRegion(node, nodeSet, loop)) { + return RegionType.FOR_LOOP_REGION; + } + if (this.isDoWhileRegion(node, nodeSet, loop)) { + return RegionType.DO_WHILE_LOOP_REGION; + } + } + // check for if + if (this.isIfExitRegion(node, nodeSet)) { + return RegionType.IF_THEN_EXIT_REGION; + } + if (this.isIfRegion(node, nodeSet)) { + return RegionType.IF_REGION; + } + // check for an if else + if (this.isIfElseRegion(node, nodeSet)) { + return RegionType.IF_ELSE_REGION; + } + return undefined; + } + cyclicRegionType(nodeSet) { + let nodes = Array.from(nodeSet); + let header = nodes[0]; + if (nodeSet.size === 1) { + let tail = nodes[0].getBlock()?.getTail(); + if (tail instanceof ArkIfStmt) { + return RegionType.DO_WHILE_LOOP_REGION; + } + return RegionType.WHILE_LOOP_REGION; + } + let back = nodes[1]; + // exit loop from back + if (!this.hasExitLoopSucc(header, nodeSet) && this.hasExitLoopSucc(back, nodeSet)) { + return RegionType.DO_WHILE_LOOP_REGION; + } + if (this.hasExitLoopSucc(header, nodeSet) && this.hasExitLoopSucc(back, nodeSet)) { + // header true exit loop --> exit is break + if (!nodeSet.has(header.getSucc()[0])) { + return RegionType.DO_WHILE_LOOP_REGION; + } + } + // for + if (back.getSucc().length === 1 && back.getBlock()?.getStmts()?.length === 1) { + let isForLoop = true; + for (const pred of header.getPred()) { + if (nodeSet.has(pred) && pred !== back) { + isForLoop = false; + } + } + if (isForLoop) { + return RegionType.FOR_LOOP_REGION; + } + } + return RegionType.WHILE_LOOP_REGION; + } + hasExitLoopSucc(node, nodeSet) { + for (const succ of node.getSucc()) { + if (!nodeSet.has(succ)) { + return true; + } + } + return false; + } + isExitLoop(node, nodeSet) { + if (this.structBlocks.has(node)) { + for (const n of this.structBlocks.get(node)) { + if (!nodeSet.has(n)) { + return true; + } + } + } + else { + if (!nodeSet.has(node)) { + return true; + } + } + return false; + } + createRegion(rtype, nodeSet) { + let node; + if (rtype === RegionType.BLOCK_REGION) { + node = new BlockRegion(nodeSet); + } + else if (rtype === RegionType.IF_ELSE_REGION) { + node = new IfElseRegion(nodeSet); + } + else if (rtype === RegionType.IF_REGION) { + node = new IfRegion(nodeSet); + } + else if (rtype === RegionType.IF_THEN_EXIT_REGION) { + node = new IfExitRegion(nodeSet); + } + else if (rtype === RegionType.IF_THEN_BREAK_REGION) { + node = new IfBreakRegion(nodeSet); + } + else if (rtype === RegionType.IF_THEN_CONTINUE_REGION) { + node = new IfContinueRegion(nodeSet); + } + else if (rtype === RegionType.SELF_LOOP_REGION) { + node = new SelfLoopRegion(nodeSet); + } + else if (rtype === RegionType.WHILE_LOOP_REGION) { + let whileLoop = new WhileLoopRegion(nodeSet); + this.loopMap.set(whileLoop.header, whileLoop); + node = whileLoop; + } + else if (rtype === RegionType.FOR_LOOP_REGION) { + let forLoop = new ForLoopRegion(nodeSet); + this.loopMap.set(forLoop.header, forLoop); + node = forLoop; + } + else if (rtype === RegionType.DO_WHILE_LOOP_REGION) { + let doWhileLoop = new DoWhileLoopRegion(nodeSet); + this.loopMap.set(doWhileLoop.header, doWhileLoop); + node = doWhileLoop; + } + else if (rtype === RegionType.TRY_CATCH_REGION || rtype === RegionType.TRY_FINALLY_REGION || rtype === RegionType.TRY_CATCH_FINALLY_REGION) { + node = new TrapRegion(nodeSet, rtype); + } + return node; + } + reduce(rtype, nodeSet) { + let region = this.createRegion(rtype, nodeSet); + region?.replace(); + if (region === undefined) { + return undefined; + } + this.structTypes.set(region, rtype); + let blocks = new Set(); + for (const s of nodeSet) { + this.structOf.set(s, region); + if (this.structBlocks.has(s)) { + for (const b of this.structBlocks.get(s)) { + blocks.add(b); + } + } + else { + blocks.add(s); + } + } + this.structBlocks.set(region, blocks); + return region; + } + setIntersect(a, b) { + let r = new Set(); + if (!b) { + return r; + } + for (const n of b) { + if (a.has(n)) { + r.add(n); + } + } + return r; + } + isSetEqual(a, b) { + if (a.size !== b.size) { + return false; + } + return this.setIntersect(a, b).size === a.size; + } + buildTrap(traps) { + if (!traps) { + return []; + } + traps.sort((a, b) => a.getTryBlocks().length + a.getCatchBlocks().length - (b.getTryBlocks().length + b.getCatchBlocks().length)); + let trapRegions = []; + for (const trap of traps) { + let region = new NaturalTrapRegion(trap, this.block2NodeMap); + let findTrapRegion = this.getNaturalTrapRegion(region); + if (!findTrapRegion) { + for (const n of region.getNodes()) { + this.structOf.set(n, region); + } + trapRegions.push(region); + continue; + } + if (findTrapRegion.type === RegionType.TRY_FINALLY_REGION) { + findTrapRegion.trySet = region.trySet; + findTrapRegion.catchSet = region.catchSet; + region = findTrapRegion; + } + else { + findTrapRegion.finallySet = region.finallySet; + region = findTrapRegion; + } + for (const n of region.getNodes()) { + this.structOf.set(n, region); + } + region.type = RegionType.TRY_CATCH_FINALLY_REGION; + } + this.structOf.clear(); + return trapRegions; + } + searchTrapFinallyNodes(trapRegions) { + // search finally + for (const region of trapRegions) { + if (region.type === RegionType.TRY_CATCH_REGION) { + continue; + } + this.bfs(region); + } + } + bfs(region) { + let finallyNodes = new Set(); + let count = region.finallySet.size; + let queue = [region.getSucc()[0]]; + while (queue.length > 0 && finallyNodes.size < count) { + let node = queue[0]; + queue.splice(0, 1); + finallyNodes.add(node); + region.identifyFinallySet.add(node); + for (const succ of node.getSucc()) { + if (!finallyNodes.has(succ)) { + queue.push(succ); + } + } + } + } + getNaturalTrapRegion(trap) { + let findTrap = this.findNaturalTrapRegion(trap.trySet); + if (findTrap) { + return findTrap; + } + if (trap.catchSet) { + findTrap = this.findNaturalTrapRegion(trap.catchSet); + } + if (findTrap) { + return findTrap; + } + if (trap.finallySet) { + findTrap = this.findNaturalTrapRegion(trap.finallySet); + } + return findTrap; + } + findNaturalTrapRegion(nodes) { + let findTrap; + for (const node of nodes) { + if (!this.structOf.has(node)) { + return undefined; + } + if (!findTrap) { + findTrap = this.structOf.get(node); + continue; + } + if (findTrap !== this.structOf.get(node)) { + return undefined; + } + } + return findTrap; + } + trapsStructuralAnalysis(trapRegions) { + trapRegions.sort((a, b) => a.size() - b.size()); + for (const trap of trapRegions) { + let tryNode = this.trapsSubStructuralAnalysis(trap.trySet); + let catchNode = this.trapsSubStructuralAnalysis(trap.catchSet); + let finnallyNode = this.trapsSubStructuralAnalysis(trap.identifyFinallySet); + if (catchNode === undefined) { + this.reduce(RegionType.TRY_FINALLY_REGION, new Set([tryNode, finnallyNode])); + } + else if (finnallyNode === undefined) { + this.reduce(RegionType.TRY_CATCH_REGION, new Set([tryNode, catchNode])); + } + else { + this.reduce(RegionType.TRY_CATCH_FINALLY_REGION, new Set([tryNode, catchNode, finnallyNode])); + } + } + } + trapsSubStructuralAnalysis(nodes) { + if (!nodes) { + return undefined; + } + let entry = Array.from(nodes)[0]; + if (nodes.size <= 1) { + return entry; + } + for (const node of nodes) { + if (this.structOf.has(node)) { + nodes.add(this.structOf.get(node)); + } + } + return this.structuralAnalysis(entry, nodes); + } +} +var RegionType; +(function (RegionType) { + RegionType[RegionType["ABSTRACT_NODE"] = 0] = "ABSTRACT_NODE"; + RegionType[RegionType["TRY_NODE"] = 1] = "TRY_NODE"; + RegionType[RegionType["CATCH_NODE"] = 2] = "CATCH_NODE"; + RegionType[RegionType["FINALLY_NODE"] = 3] = "FINALLY_NODE"; + /* Sequence of blocks. */ + RegionType[RegionType["BLOCK_REGION"] = 4] = "BLOCK_REGION"; + RegionType[RegionType["IF_REGION"] = 5] = "IF_REGION"; + RegionType[RegionType["IF_ELSE_REGION"] = 6] = "IF_ELSE_REGION"; + RegionType[RegionType["IF_THEN_EXIT_REGION"] = 7] = "IF_THEN_EXIT_REGION"; + RegionType[RegionType["IF_THEN_BREAK_REGION"] = 8] = "IF_THEN_BREAK_REGION"; + RegionType[RegionType["IF_THEN_CONTINUE_REGION"] = 9] = "IF_THEN_CONTINUE_REGION"; + RegionType[RegionType["SELF_LOOP_REGION"] = 10] = "SELF_LOOP_REGION"; + RegionType[RegionType["NATURAL_LOOP_REGION"] = 11] = "NATURAL_LOOP_REGION"; + RegionType[RegionType["WHILE_LOOP_REGION"] = 12] = "WHILE_LOOP_REGION"; + RegionType[RegionType["DO_WHILE_LOOP_REGION"] = 13] = "DO_WHILE_LOOP_REGION"; + RegionType[RegionType["FOR_LOOP_REGION"] = 14] = "FOR_LOOP_REGION"; + RegionType[RegionType["CASE_REGION"] = 15] = "CASE_REGION"; + RegionType[RegionType["SWITCH_REGION"] = 16] = "SWITCH_REGION"; + RegionType[RegionType["TRY_CATCH_REGION"] = 17] = "TRY_CATCH_REGION"; + RegionType[RegionType["TRY_FINALLY_REGION"] = 18] = "TRY_FINALLY_REGION"; + RegionType[RegionType["TRY_CATCH_FINALLY_REGION"] = 19] = "TRY_CATCH_FINALLY_REGION"; +})(RegionType || (RegionType = {})); +const LOOP_TYPES = new Set([ + RegionType.SELF_LOOP_REGION, + RegionType.NATURAL_LOOP_REGION, + RegionType.WHILE_LOOP_REGION, + RegionType.FOR_LOOP_REGION, + RegionType.DO_WHILE_LOOP_REGION, +]); +class AbstractNode { + type; + predNodes = []; + succNodes = []; + bb; + constructor() { + this.type = RegionType.ABSTRACT_NODE; + } + traversal(callback, type) { + callback(this.bb, type); + } + getType() { + return this.type; + } + getSucc() { + return this.succNodes; + } + addSucc(node) { + this.succNodes.push(node); + } + replaceSucc(src, dst) { + for (let i = 0; i < this.succNodes.length; i++) { + if (this.succNodes[i] === src) { + this.succNodes[i] = dst; + break; + } + } + } + removeSucc(src) { + for (let i = 0; i < this.predNodes.length; i++) { + if (this.succNodes[i] === src) { + this.succNodes.splice(i, 1); + break; + } + } + } + getPred() { + return this.predNodes; + } + addPred(block) { + let set = new Set(this.predNodes); + if (set.has(block)) { + return; + } + this.predNodes.push(block); + } + replacePred(src, dst) { + for (let i = 0; i < this.predNodes.length; i++) { + if (this.predNodes[i] === src) { + this.predNodes[i] = dst; + break; + } + } + } + removePred(src) { + for (let i = 0; i < this.predNodes.length; i++) { + if (this.predNodes[i] === src) { + this.predNodes.splice(i, 1); + break; + } + } + } + setBlock(bb) { + this.bb = bb; + } + getBlock() { + return this.bb; + } + hasIfStmt() { + if (!this.bb) { + return false; + } + for (let stmt of this.bb.getStmts()) { + if (stmt instanceof ArkIfStmt) { + return true; + } + } + return false; + } + hasReturnStmt() { + if (!this.bb) { + return false; + } + for (let stmt of this.bb.getStmts()) { + if (stmt instanceof ArkReturnStmt) { + return true; + } + } + return false; + } +} +class Region extends AbstractNode { + nset; + constructor(nset, type) { + super(); + this.nset = nset; + this.type = type; + } + getBlock() { + if (this.nset.size === 0) { + return undefined; + } + return Array.from(this.nset)[0].getBlock(); + } +} +class BlockRegion extends Region { + blocks; + constructor(nset) { + super(nset, RegionType.BLOCK_REGION); + this.blocks = Array.from(nset); + } + replace() { + for (let pred of this.blocks[0].getPred()) { + pred.replaceSucc(this.blocks[0], this); + this.addPred(pred); + } + for (let succ of this.blocks[this.blocks.length - 1].getSucc()) { + succ.replacePred(this.blocks[this.blocks.length - 1], this); + this.addSucc(succ); + } + } + traversal(callback) { + for (const node of this.blocks) { + node.traversal(callback, CodeBlockType.NORMAL); + } + } +} +class NaturalLoopRegion extends Region { + header; + back; + control; + constructor(nset, type = RegionType.NATURAL_LOOP_REGION) { + super(nset, type); + let nodes = Array.from(nset); + this.header = nodes[0]; + if (nset.size > 1) { + this.back = nodes[1]; + } + else { + this.back = nodes[0]; + } + this.control = new Set([this.header]); + } + replace() { + for (let pred of this.header.getPred()) { + if (!this.nset.has(pred)) { + pred.replaceSucc(this.header, this); + this.addPred(pred); + } + } + let succNodes = new Set(); + for (let node of this.nset) { + for (let succ of node.getSucc()) { + if (!this.nset.has(succ)) { + succNodes.add(succ); + } + } + } + if (succNodes.size === 0) { + return; + } + let pred = Array.from(succNodes)[0]; + let replaced = false; + for (let succ of pred.getPred()) { + if (this.nset.has(succ)) { + if (!replaced) { + pred.replacePred(succ, this); + this.addSucc(pred); + replaced = true; + } + else { + pred.removePred(succ); + } + } + } + } + revise() { + // add node to loop sets + for (const node of this.nset) { + for (const succ of node.getSucc()) { + if (!this.nset.has(succ) && succ !== this.getExitNode() && succ.getSucc().length === 1 && succ.getSucc()[0] === this.getExitNode()) { + this.nset.add(succ); + } + } + } + } +} +class SelfLoopRegion extends NaturalLoopRegion { + constructor(nset) { + super(nset, RegionType.SELF_LOOP_REGION); + this.back = this.header; + } + replace() { + for (let pred of this.header.getPred()) { + if (pred !== this.header) { + pred.replaceSucc(this.header, this); + this.addPred(pred); + } + } + for (let succ of this.header.getSucc()) { + if (succ !== this.header) { + succ.replacePred(this.header, this); + this.addSucc(succ); + } + } + } + getExitNode() { + return this.header.getSucc()[1]; + } +} +class WhileLoopRegion extends NaturalLoopRegion { + constructor(nset) { + super(nset, RegionType.WHILE_LOOP_REGION); + } + traversal(callback) { + this.header.traversal(callback, CodeBlockType.WHILE); + if (this.header !== this.back) { + this.back.traversal(callback, CodeBlockType.NORMAL); + } + callback(undefined, CodeBlockType.COMPOUND_END); + } + getExitNode() { + return this.header.getSucc()[1]; + } +} +class DoWhileLoopRegion extends NaturalLoopRegion { + constructor(nset) { + super(nset, RegionType.DO_WHILE_LOOP_REGION); + this.control.clear(); + this.control.add(this.back); + } + traversal(callback) { + callback(undefined, CodeBlockType.DO); + if (this.header !== this.back) { + this.header.traversal(callback, CodeBlockType.NORMAL); + } + this.back.traversal(callback, CodeBlockType.DO_WHILE); + } + getExitNode() { + return this.back.getSucc()[1]; + } +} +class ForLoopRegion extends NaturalLoopRegion { + inc; + constructor(nset) { + super(nset, RegionType.FOR_LOOP_REGION); + this.inc = this.back; + this.control.add(this.inc); + } + traversal(callback) { + this.header.traversal(callback, CodeBlockType.FOR); + for (const node of this.nset) { + if (node !== this.header && node !== this.inc) { + node.traversal(callback, CodeBlockType.NORMAL); + } + } + callback(undefined, CodeBlockType.COMPOUND_END); + } + getExitNode() { + return this.header.getSucc()[1]; + } +} +class IfRegion extends Region { + contition; + then; + constructor(nset) { + super(nset, RegionType.IF_REGION); + let nodes = Array.from(nset); + this.contition = nodes[0]; + this.then = nodes[1]; + } + replace() { + this.replaceContitionPred(); + for (let succ of this.then.getSucc()) { + if (succ !== this.then) { + succ.replacePred(this.then, this); + succ.removePred(this.contition); + this.addSucc(succ); + } + } + } + traversal(callback) { + this.contition.traversal(callback, CodeBlockType.IF); + this.then.traversal(callback, CodeBlockType.NORMAL); + callback(undefined, CodeBlockType.COMPOUND_END); + } + replaceContitionPred() { + for (let pred of this.contition.getPred()) { + if (pred !== this.contition) { + pred.replaceSucc(this.contition, this); + this.addPred(pred); + } + } + } +} +class IfExitRegion extends IfRegion { + constructor(nset) { + super(nset); + this.type = RegionType.IF_THEN_EXIT_REGION; + } + replace() { + this.replaceContitionPred(); + let succ = this.contition.getSucc()[1]; + succ.replacePred(this.contition, this); + this.addSucc(succ); + } +} +class IfBreakRegion extends IfRegion { + constructor(nset) { + super(nset); + this.type = RegionType.IF_THEN_BREAK_REGION; + } + replace() { + this.replaceContitionPred(); + let succ = this.contition.getSucc()[1]; + succ.replacePred(this.contition, this); + this.addSucc(succ); + if (this.then) { + succ = this.then.getSucc()[0]; + succ.removePred(this.then); + } + else { + succ = this.contition.getSucc()[0]; + succ.removePred(this.contition); + } + } + traversal(callback) { + this.contition.traversal(callback, CodeBlockType.IF); + this.then?.traversal(callback, CodeBlockType.NORMAL); + callback(undefined, CodeBlockType.BREAK); + callback(undefined, CodeBlockType.COMPOUND_END); + } +} +class IfContinueRegion extends IfBreakRegion { + constructor(nset) { + super(nset); + this.type = RegionType.IF_THEN_CONTINUE_REGION; + } + traversal(callback) { + this.contition.traversal(callback, CodeBlockType.IF); + this.then?.traversal(callback, CodeBlockType.NORMAL); + callback(undefined, CodeBlockType.CONTINUE); + callback(undefined, CodeBlockType.COMPOUND_END); + } +} +class IfElseRegion extends Region { + contition; + then; + else; + constructor(nset) { + super(nset, RegionType.IF_ELSE_REGION); + let nodes = Array.from(nset); + this.contition = nodes[0]; + this.then = nodes[1]; + this.else = nodes[2]; + } + replace() { + for (let pred of this.contition.getPred()) { + if (pred !== this.contition) { + pred.replaceSucc(this.contition, this); + this.addPred(pred); + } + } + for (let succ of this.then.getSucc()) { + if (succ !== this.then) { + succ.replacePred(this.then, this); + succ.removePred(this.else); + this.addSucc(succ); + } + } + } + traversal(callback) { + this.contition.traversal(callback, CodeBlockType.IF); + this.then.traversal(callback, CodeBlockType.NORMAL); + callback(undefined, CodeBlockType.ELSE); + this.else.traversal(callback, CodeBlockType.NORMAL); + callback(undefined, CodeBlockType.COMPOUND_END); + } +} +class TrapRegion extends Region { + tryNode; + catchNode; + finallyNode; + constructor(nset, type) { + super(nset, type); + let nodes = Array.from(nset); + this.tryNode = nodes[0]; + if (type === RegionType.TRY_CATCH_REGION) { + this.catchNode = nodes[1]; + } + else if (type === RegionType.TRY_FINALLY_REGION) { + this.finallyNode = nodes[1]; + } + else { + this.catchNode = nodes[1]; + this.finallyNode = nodes[2]; + } + } + replace() { + for (let pred of this.tryNode.getPred()) { + if (pred !== this.tryNode) { + pred.replaceSucc(this.tryNode, this); + this.addPred(pred); + } + } + if (this.finallyNode) { + for (let succ of this.finallyNode.getSucc()) { + if (succ !== this.finallyNode) { + succ.replacePred(this.finallyNode, this); + this.addSucc(succ); + } + } + } + else { + for (let succ of this.tryNode.getSucc()) { + if (succ !== this.tryNode) { + succ.replacePred(this.tryNode, this); + this.addSucc(succ); + } + } + } + } + traversal(callback) { + callback(undefined, CodeBlockType.TRY); + this.tryNode.traversal(callback, CodeBlockType.NORMAL); + if (this.catchNode) { + callback(this.catchNode.getBlock(), CodeBlockType.CATCH); + this.catchNode?.traversal(callback, CodeBlockType.NORMAL); + } + if (this.finallyNode) { + callback(undefined, CodeBlockType.FINALLY); + this.finallyNode?.traversal(callback, CodeBlockType.NORMAL); + } + callback(undefined, CodeBlockType.COMPOUND_END); + } +} +class NaturalTrapRegion extends Region { + trySet; + catchSet; + finallySet; + identifyFinallySet; + constructor(trap, block2NodeMap) { + super(new Set(), RegionType.TRY_CATCH_FINALLY_REGION); + this.trySet = new Set(); + this.catchSet = new Set(); + this.identifyFinallySet = new Set(); + for (const block of trap.getTryBlocks()) { + this.trySet.add(block2NodeMap.get(block)); + } + for (const block of trap.getCatchBlocks()) { + this.catchSet.add(block2NodeMap.get(block)); + } + if (this.isFinallyNode(Array.from(this.catchSet)[this.catchSet.size - 1])) { + this.type = RegionType.TRY_FINALLY_REGION; + this.finallySet = this.catchSet; + this.catchSet = undefined; + } + else { + this.type = RegionType.TRY_CATCH_REGION; + } + } + isFinallyNode(node) { + let block = node.getBlock(); + if (!block) { + return false; + } + let stmtLen = block.getStmts().length; + if (stmtLen < 1) { + return false; + } + let stmtLast = block.getStmts()[stmtLen - 1]; + return stmtLast instanceof ArkThrowStmt; + } + size() { + let size = this.trySet.size; + if (this.catchSet) { + size += this.catchSet.size; + } + if (this.finallySet) { + size += this.finallySet.size; + } + return size; + } + replace() { } + getNodes() { + let nodes = Array.from(this.trySet); + if (this.catchSet) { + nodes.push(...this.catchSet); + } + if (this.finallySet) { + nodes.push(...this.finallySet); + } + return nodes; + } + getSucc() { + let succ = new Set(); + for (const node of this.trySet) { + for (const s of node.getSucc()) { + if (!this.trySet.has(s)) { + succ.add(s); + } + } + } + return Array.from(succ); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$l = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'SourceBody'); +class SourceBody { + printer; + arkBody; + stmts = []; + method; + cfgUtils; + tempCodeMap; + tempVisitor; + skipStmts; + stmtReader; + definedLocals; + inBuilder; + lastStmt; + constructor(indent, method, inBuilder) { + this.printer = new ArkCodeBuffer(indent); + this.method = method; + this.arkBody = method.getBody(); + this.cfgUtils = new AbstractFlowGraph(method.getCfg(), this.arkBody.getTraps()); + this.tempCodeMap = new Map(); + this.tempVisitor = new Set(); + this.definedLocals = new Set(); + this.inBuilder = inBuilder; + this.skipStmts = new Set(); + this.stmtReader = new StmtReader([]); + this.lastStmt = this.arkBody.getCfg().getStartingStmt(); + this.buildSourceStmt(); + } + setSkipStmt(stmt) { + this.skipStmts.add(stmt); + } + isInBuilderMethod() { + return this.inBuilder; + } + isInDefaultMethod() { + return this.method.isDefaultArkMethod(); + } + getArkFile() { + return this.method.getDeclaringArkFile(); + } + getDeclaringArkNamespace() { + return this.method.getDeclaringArkClass().getDeclaringArkNamespace(); + } + getMethod(signature) { + let method = this.method.getDeclaringArkFile().getScene().getMethod(signature); + if (method) { + return method; + } + return this.method.getDeclaringArkClass().getMethodWithName(signature.getMethodSubSignature().getMethodName()); + } + getClass(signature) { + return ModelUtils.getClass(this.method, signature); + } + getLocals() { + return this.arkBody.getLocals(); + } + defineLocal(local) { + this.definedLocals.add(local); + } + isLocalDefined(local) { + return this.definedLocals.has(local); + } + getStmtReader() { + return this.stmtReader; + } + setTempCode(temp, code) { + this.tempCodeMap.set(temp, code); + } + transTemp2Code(temp) { + if (this.tempCodeMap.has(temp.getName()) && PrinterUtils.isTemp(temp.getName())) { + this.tempVisitor.add(temp.getName()); + return this.tempCodeMap.get(temp.getName()); + } + return temp.getName(); + } + getTempCodeMap() { + return this.tempCodeMap; + } + hasTempVisit(temp) { + return this.tempVisitor.has(temp); + } + setTempVisit(temp) { + this.tempVisitor.add(temp); + } + getPrinter() { + return this.printer; + } + dump() { + this.printStmts(); + return this.printer.toString(); + } + buildSourceStmt() { + this.cfgUtils.preOrder(this.cfgUtils.getEntry(), (block, type) => { + this.buildBasicBlock(block, type); + }); + } + buildBasicBlock(block, type) { + if (type === CodeBlockType.BREAK) { + this.pushStmt(new SourceBreakStmt(this, this.lastStmt)); + return; + } + else if (type === CodeBlockType.CONTINUE) { + this.pushStmt(new SourceContinueStmt(this, this.lastStmt)); + } + else if (type === CodeBlockType.COMPOUND_END) { + this.pushStmt(new SourceCompoundEndStmt(this, this.lastStmt, '}')); + } + else if (type === CodeBlockType.ELSE) { + this.pushStmt(new SourceElseStmt(this, this.lastStmt)); + } + else if (type === CodeBlockType.DO) { + this.pushStmt(new SourceDoStmt(this, this.lastStmt)); + } + else if (type === CodeBlockType.TRY) { + this.pushStmt(new SourceTryStmt(this, this.lastStmt)); + } + else if (type === CodeBlockType.CATCH) { + this.pushStmt(new SourceCatchStmt(this, this.lastStmt, block)); + // catch need read block first stmt, using return to void walk block twice. + return; + } + else if (type === CodeBlockType.FINALLY) { + this.pushStmt(new SourceFinallyStmt(this, this.lastStmt)); + } + if (!block) { + return; + } + let originalStmts = this.sortStmt(block.getStmts()); + this.stmtReader = new StmtReader(originalStmts); + while (this.stmtReader.hasNext()) { + let stmt = this.stmtReader.next(); + if (this.skipStmts.has(stmt)) { + continue; + } + if (stmt instanceof ArkIfStmt) { + if (type === CodeBlockType.IF) { + this.pushStmt(new SourceIfStmt(this, stmt)); + } + else if (type === CodeBlockType.WHILE) { + this.pushStmt(new SourceWhileStmt(this, stmt, block)); + } + else if (type === CodeBlockType.FOR) { + let inc = this.cfgUtils.getForIncBlock(block); + this.pushStmt(new SourceForStmt(this, stmt, block, inc)); + } + else if (type === CodeBlockType.DO_WHILE) { + this.pushStmt(new SourceDoWhileStmt(this, stmt, block)); + } + } + else { + this.pushStmt(stmt2SourceStmt(this, stmt)); + } + this.lastStmt = stmt; + } + } + printStmts() { + for (let stmt of this.stmts) { + if (this.skipStmts.has(stmt.original)) { + continue; + } + this.printer.write(stmt.dump()); + } + } + getStmts() { + return this.stmts.filter(value => !this.skipStmts.has(value.original)); + } + pushStmt(stmt) { + let lastLine = this.getLastLine(); + if (stmt.getLine() < lastLine) { + stmt.setLine(lastLine + 0.1); + } + stmt.transfer2ts(); + this.stmts.push(stmt); + } + getLastLine() { + if (this.stmts.length > 0) { + return this.stmts[this.stmts.length - 1].getLine(); + } + return 0; + } + /* + * temp9 = new <>.<>(); temp10 = new Array(3); + * temp10 = new Array(3); temp10[0] = 'Cat'; + * temp10[0] = 'Cat'; ==> temp10[1] = 'Dog'; + * temp10[1] = 'Dog'; temp10[2] = 'Hamster'; + * temp10[2] = 'Hamster'; temp9 = new <>.<>(); + * temp9.constructor(temp10); temp9.constructor(temp10); + */ + sortStmt(stmts) { + for (let i = stmts.length - 1; i > 0; i--) { + if (stmts[i] instanceof ArkInvokeStmt && stmts[i].getInvokeExpr()) { + let instanceInvokeExpr = stmts[i].getInvokeExpr(); + if ('constructor' !== instanceInvokeExpr.getMethodSignature().getMethodSubSignature().getMethodName()) { + continue; + } + let localName = instanceInvokeExpr.getBase().getName(); + let newExprIdx = findNewExpr(i, localName); + if (newExprIdx >= 0 && newExprIdx < i - 1) { + moveStmt(i, newExprIdx); + } + } + } + return stmts; + function findNewExpr(constructorIdx, name) { + for (let j = constructorIdx - 1; j >= 0; j--) { + if (!(stmts[j] instanceof ArkAssignStmt)) { + continue; + } + const leftOp = stmts[j].getLeftOp(); + if (leftOp instanceof Local && leftOp.getName() === name) { + return j; + } + } + return -1; + } + function moveStmt(constructorIdx, newExprIdx) { + let back = stmts[newExprIdx]; + for (let i = newExprIdx; i < constructorIdx - 1; i++) { + stmts[i] = stmts[i + 1]; + } + stmts[constructorIdx - 1] = back; + } + } +} +class StmtReader { + stmts = []; + pos; + constructor(stmts) { + this.stmts = stmts; + this.pos = 0; + } + first() { + return this.stmts[0]; + } + hasNext() { + return this.pos < this.stmts.length; + } + next() { + if (!this.hasNext()) { + logger$l.error('SourceBody: StmtReader->next No more stmt.'); + throw new Error('No more stmt.'); + } + let stmt = this.stmts[this.pos]; + this.pos++; + return stmt; + } + rollback() { + if (this.pos === 0) { + logger$l.error('SourceBody: StmtReader->rollback No more stmt to rollback.'); + throw new Error('No more stmt to rollback.'); + } + this.pos--; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @category save + */ +class SourceField extends SourceBase { + field; + transformer; + initializer; + constructor(field, indent = '', initializer) { + super(field.getDeclaringArkClass().getDeclaringArkFile(), indent); + this.field = field; + this.transformer = new SourceTransformer(this); + this.initializer = initializer; + } + getLine() { + return this.field.getOriginPosition().getLineNo(); + } + dump() { + this.printer.clear(); + const commentsMetadata = this.field.getMetadata(ArkMetadataKind.LEADING_COMMENTS); + if (commentsMetadata instanceof CommentsMetadata) { + const comments = commentsMetadata.getComments(); + comments.forEach(comment => { + this.printer.writeIndent().writeLine(comment.content); + }); + } + this.printDecorator(this.field.getDecorators()); + this.printer.writeIndent(); + if (this.field.getCategory() !== FieldCategory.ENUM_MEMBER) { + this.printer.writeSpace(this.modifiersToString(this.field.getModifiers())); + } + this.printer.write(this.field.getName()); + if (this.field.getQuestionToken()) { + this.printer.write('?'); + } + if (this.field.getExclamationToken()) { + this.printer.write('!'); + } + // property.getInitializer() PropertyAccessExpression ArrowFunction ClassExpression FirstLiteralToken StringLiteral + if (!(this.field.getType() instanceof UnknownType) && this.field.getCategory() !== FieldCategory.ENUM_MEMBER) { + this.printer.write(`: ${this.transformer.typeToString(this.field.getType())}`); + } + if (this.initializer.has(this.field.getName())) { + this.printer.write(` = ${this.initializer.get(this.field.getName())}`); + } + if (this.field.getCategory() === FieldCategory.ENUM_MEMBER) { + this.printer.writeLine(','); + } + else { + this.printer.writeLine(';'); + } + return this.printer.toString(); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @category save + */ +class SourceClass extends SourceBase { + cls; + transformer; + constructor(cls, indent = '') { + super(cls.getDeclaringArkFile(), indent); + this.cls = cls; + this.transformer = new SourceTransformer(this); + } + getDeclaringArkNamespace() { + return this.cls.getDeclaringArkNamespace(); + } + getLine() { + return this.cls.getLine(); + } + dump() { + this.printer.clear(); + if (this.cls.getCategory() === ClassCategory.OBJECT) { + return this.dumpObject(); + } + if (this.cls.getCategory() === ClassCategory.TYPE_LITERAL) { + return this.dumpTypeLiteral(); + } + const commentsMetadata = this.cls.getMetadata(ArkMetadataKind.LEADING_COMMENTS); + if (commentsMetadata instanceof CommentsMetadata) { + const comments = commentsMetadata.getComments(); + comments.forEach(comment => { + this.printer.writeIndent().writeLine(comment.content); + }); + } + this.printDecorator(this.cls.getDecorators()); + // print export class name<> + extends c0 implements x1, x2 { + this.printer + .writeIndent() + .writeSpace(this.modifiersToString(this.cls.getModifiers())) + .write(`${this.classOriginTypeToString(this.cls.getCategory())} `); + if (!PrinterUtils.isAnonymousClass(this.cls.getName())) { + this.printer.write(this.cls.getName()); + } + const genericsTypes = this.cls.getGenericsTypes(); + if (genericsTypes) { + this.printer.write(`<${this.transformer.typeArrayToString(genericsTypes)}>`); + } + if (this.cls.getSuperClassName() && !this.cls.hasComponentDecorator()) { + this.printer.write(` extends ${this.cls.getSuperClassName()}`); + } + if (this.cls.getImplementedInterfaceNames().length > 0) { + this.printer.write(` implements ${this.cls.getImplementedInterfaceNames().join(', ')}`); + } + this.printer.writeLine(' {'); + this.printer.incIndent(); + let items = []; + items.push(...this.printFields()); + items.push(...this.printMethods()); + items.sort((a, b) => a.getLine() - b.getLine()); + items.forEach((v) => { + this.printer.write(v.dump()); + }); + this.printer.decIndent(); + this.printer.writeIndent().write('}'); + if (!PrinterUtils.isAnonymousClass(this.cls.getName())) { + this.printer.writeLine(''); + } + return this.printer.toString(); + } + dumpObject() { + this.printer.write('{'); + this.cls.getFields().forEach((field, index, array) => { + let name = PrinterUtils.escape(field.getName()); + if (PrinterUtils.isIdentifierText(field.getName())) { + this.printer.write(name); + } + else { + this.printer.write(`'${name}'`); + } + let instanceInitializer = this.parseFieldInitMethod(INSTANCE_INIT_METHOD_NAME); + if (instanceInitializer.has(field.getName())) { + this.printer.write(`: ${instanceInitializer.get(field.getName())}`); + } + if (index !== array.length - 1) { + this.printer.write(`, `); + } + }); + this.printer.write('}'); + return this.printer.toString(); + } + dumpTypeLiteral() { + this.printer.write('{'); + this.cls.getFields().forEach((field, index, array) => { + let name = PrinterUtils.escape(field.getName()); + if (PrinterUtils.isIdentifierText(field.getName())) { + this.printer.write(`${name}: ${this.transformer.typeToString(field.getType())}`); + } + else { + this.printer.write(`'${name}': ${this.transformer.typeToString(field.getType())}`); + } + if (index !== array.length - 1) { + this.printer.write(`, `); + } + }); + this.printer.write('}'); + return this.printer.toString(); + } + printMethods() { + let items = []; + for (let method of this.cls.getMethods()) { + if (method.isGenerated() || (PrinterUtils.isConstructorMethod(method.getName()) && this.cls.hasViewTree())) { + continue; + } + if (method.isDefaultArkMethod()) { + items.push(...new SourceMethod(method, this.printer.getIndent()).dumpDefaultMethod()); + } + else if (!PrinterUtils.isAnonymousMethod(method.getName())) { + items.push(new SourceMethod(method, this.printer.getIndent())); + } + } + return items; + } + printFields() { + let instanceInitializer = this.parseFieldInitMethod(INSTANCE_INIT_METHOD_NAME); + let staticInitializer = this.parseFieldInitMethod(STATIC_INIT_METHOD_NAME); + let items = []; + for (let field of this.cls.getFields()) { + if (field.getCategory() === FieldCategory.GET_ACCESSOR) { + continue; + } + if (field.isStatic()) { + items.push(new SourceField(field, this.printer.getIndent(), staticInitializer)); + } + else { + items.push(new SourceField(field, this.printer.getIndent(), instanceInitializer)); + } + } + return items; + } + parseFieldInitMethod(name) { + let method = this.cls.getMethodWithName(name); + if (!method || method?.getBody() === undefined) { + return new Map(); + } + let srcBody = new SourceBody(this.printer.getIndent(), method, false); + srcBody.dump(); + return srcBody.getTempCodeMap(); + } +} + +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class ExportPrinter extends BasePrinter { + info; + constructor(info, indent = '') { + super(indent); + this.info = info; + } + getLine() { + return this.info.getOriginTsPosition().getLineNo(); + } + dump() { + this.printer.clear(); + const commentsMetadata = this.info.getMetadata(ArkMetadataKind.LEADING_COMMENTS); + if (commentsMetadata instanceof CommentsMetadata) { + this.printComments(commentsMetadata); + } + if (!this.info.getFrom() && + (this.info.isExport() || this.info.getExportClauseType() === ExportType.LOCAL || this.info.getExportClauseType() === ExportType.TYPE)) { + return this.printer.toString(); + } + if (this.info.getExportClauseName() === '*') { + // just like: export * as xx from './yy' + if (this.info.getNameBeforeAs() && this.info.getNameBeforeAs() !== '*') { + this.printer.writeIndent().write(`export ${this.info.getNameBeforeAs()} as ${this.info.getExportClauseName()}`); + } + else { + this.printer.writeIndent().write(`export ${this.info.getExportClauseName()}`); + } + } + else { + // just like: export {xxx as x} from './yy' + if (this.info.getNameBeforeAs()) { + this.printer.write(`export {${this.info.getNameBeforeAs()} as ${this.info.getExportClauseName()}}`); + } + else { + this.printer.write(`export {${this.info.getExportClauseName()}}`); + } + } + if (this.info.getFrom()) { + this.printer.write(` from '${this.info.getFrom()}'`); + } + this.printer.writeLine(';'); + return this.printer.toString(); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @category save + */ +class SourceNamespace extends SourceBase { + ns; + constructor(ns, indent = '') { + super(ns.getDeclaringArkFile(), indent); + this.ns = ns; + } + getLine() { + return this.ns.getLine(); + } + printDefaultClassInNamespace(items, cls) { + for (let method of cls.getMethods()) { + if (method.isDefaultArkMethod()) { + items.push(...new SourceMethod(method, this.printer.getIndent()).dumpDefaultMethod()); + } + else if (!PrinterUtils.isAnonymousMethod(method.getName())) { + items.push(new SourceMethod(method, this.printer.getIndent())); + } + } + } + dump() { + const commentsMetadata = this.ns.getMetadata(ArkMetadataKind.LEADING_COMMENTS); + if (commentsMetadata instanceof CommentsMetadata) { + const comments = commentsMetadata.getComments(); + comments.forEach(comment => { + this.printer.writeIndent().writeLine(comment.content); + }); + } + this.printer.writeIndent().writeSpace(this.modifiersToString(this.ns.getModifiers())).writeLine(`namespace ${this.ns.getName()} {`); + this.printer.incIndent(); + let items = []; + // print class + for (let cls of this.ns.getClasses()) { + if (PrinterUtils.isAnonymousClass(cls.getName())) { + continue; + } + if (cls.isDefaultArkClass()) { + this.printDefaultClassInNamespace(items, cls); + } + else { + items.push(new SourceClass(cls, this.printer.getIndent())); + } + } + // print namespace + for (let childNs of this.ns.getNamespaces()) { + items.push(new SourceNamespace(childNs, this.printer.getIndent())); + } + // print exportInfos + for (let exportInfo of this.ns.getExportInfos()) { + items.push(new ExportPrinter(exportInfo, this.printer.getIndent())); + } + items.sort((a, b) => a.getLine() - b.getLine()); + items.forEach((v) => { + this.printer.write(v.dump()); + }); + this.printer.decIndent(); + this.printer.writeIndent().writeLine('}'); + return this.printer.toString(); + } +} + +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class ImportPrinter extends BasePrinter { + infos; + constructor(infos, indent = '') { + super(indent); + this.infos = infos; + } + getLine() { + return this.infos[0].getOriginTsPosition().getLineNo(); + } + dump() { + const commentsMetadata = this.infos[0].getMetadata(ArkMetadataKind.LEADING_COMMENTS); + if (commentsMetadata instanceof CommentsMetadata) { + this.printComments(commentsMetadata); + } + let clauseNames = []; + let namedImports = []; + for (const info of this.infos) { + if (info.getImportType() === 'Identifier') { + // sample: import fs from 'fs' + clauseNames.push(info.getImportClauseName()); + } + else if (info.getImportType() === 'NamedImports') { + // sample: import {xxx} from './yyy' + if (info.getNameBeforeAs()) { + namedImports.push(`${info.getNameBeforeAs()} as ${info.getImportClauseName()}`); + } + else { + namedImports.push(info.getImportClauseName()); + } + } + else if (info.getImportType() === 'NamespaceImport') { + // sample: import * as ts from 'ohos-typescript' + clauseNames.push(`* as ${info.getImportClauseName()}`); + } + else if (info.getImportType() === 'EqualsImport') { + // sample: import mmmm = require('./xxx') + this.printer.writeIndent().writeLine(`import ${info.getImportClauseName()} = require('${info.getFrom()}');`); + } + else { + // sample: import '../xxx' + this.printer.writeIndent().writeLine(`import '${info.getFrom()}';`); + } + } + if (namedImports.length > 0) { + clauseNames.push(`{${namedImports.join(', ')}}`); + } + this.printer.writeIndent().writeLine(`import ${clauseNames.join(', ')} from '${this.infos[0].getFrom()}';`); + return this.printer.toString(); + } +} +function mergeImportInfos(infos) { + let map = new Map(); + for (let info of infos) { + let key = `${info.getOriginTsPosition().getLineNo()}-${info.getFrom()}`; + let merge = map.get(key) || []; + merge.push(info); + map.set(key, merge); + } + return map; +} +function printImports(imports, indent) { + let mergeImports = mergeImportInfos(imports); + let items = []; + for (const [_, importInfos] of mergeImports) { + items.push(new ImportPrinter(importInfos, indent)); + } + return items; +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @category save + */ +class SourceFilePrinter extends Printer { + arkFile; + items = []; + constructor(arkFile) { + super(); + this.arkFile = arkFile; + } + printDefaultClassInFile(cls) { + for (let method of cls.getMethods()) { + if (method.isDefaultArkMethod()) { + this.items.push(...new SourceMethod(method, this.printer.getIndent()).dumpDefaultMethod()); + } + else if (!PrinterUtils.isAnonymousMethod(method.getName())) { + this.items.push(new SourceMethod(method)); + } + } + } + dump() { + this.printer.clear(); + // print imports + this.items.push(...printImports(this.arkFile.getImportInfos(), this.printer.getIndent())); + // print namespace + for (let ns of this.arkFile.getNamespaces()) { + this.items.push(new SourceNamespace(ns)); + } + // print class + for (let cls of this.arkFile.getClasses()) { + if (cls.isDefaultArkClass()) { + this.printDefaultClassInFile(cls); + } + else if (!PrinterUtils.isAnonymousClass(cls.getName())) { + this.items.push(new SourceClass(cls)); + } + } + // print export + for (let info of this.arkFile.getExportInfos()) { + this.items.push(new ExportPrinter(info)); + } + this.items.sort((a, b) => a.getLine() - b.getLine()); + this.items.forEach((v) => { + this.printer.write(v.dump()); + }); + return this.printer.toString(); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class JsonPrinter extends Printer { + arkFile; + constructor(arkFile) { + super(); + this.arkFile = arkFile; + } + dump() { + const jsonObject = this.serializeArkFile(this.arkFile); + return JSON.stringify(jsonObject, null, 2); + } + serializeArkFile(file) { + return { + signature: this.serializeFileSignature(file.getFileSignature()), + namespaces: file.getNamespaces().map(ns => this.serializeNamespace(ns)), + classes: file.getClasses().map(cls => this.serializeClass(cls)), + importInfos: file.getImportInfos().map(info => this.serializeImportInfo(info)), + exportInfos: file.getExportInfos().map(info => this.serializeExportInfo(info)), + }; + } + serializeNamespace(namespace) { + return { + signature: this.serializeNamespaceSignature(namespace.getSignature()), + classes: namespace.getClasses().map(cls => this.serializeClass(cls)), + namespaces: namespace.getNamespaces().map(ns => this.serializeNamespace(ns)), + }; + } + serializeClass(clazz) { + return { + signature: this.serializeClassSignature(clazz.getSignature()), + modifiers: clazz.getModifiers(), + decorators: clazz.getDecorators().map((decorator) => this.serializeDecorator(decorator)), + typeParameters: clazz.getGenericsTypes()?.map((type) => this.serializeType(type)), + category: clazz.getCategory(), + superClassName: clazz.getSuperClassName(), + implementedInterfaceNames: clazz.getImplementedInterfaceNames(), + fields: clazz.getFields().map(field => this.serializeField(field)), + methods: clazz.getMethods(true).map(method => this.serializeMethod(method)), + }; + } + serializeField(field) { + return { + signature: this.serializeFieldSignature(field.getSignature()), + modifiers: field.getModifiers(), + decorators: field.getDecorators().map(decorator => this.serializeDecorator(decorator)), + questionToken: field.getQuestionToken(), + exclamationToken: field.getExclamationToken(), + }; + } + serializeMethod(method) { + let body = method.getBody(); + return { + signature: this.serializeMethodSignature(method.getSignature()), + modifiers: method.getModifiers(), + decorators: method.getDecorators().map(decorator => this.serializeDecorator(decorator)), + typeParameters: method.getGenericTypes()?.map(type => this.serializeType(type)), + body: body && this.serializeMethodBody(body), + }; + } + serializeMethodBody(body) { + return { + locals: Array.from(body.getLocals().values()).map(local => this.serializeLocal(local)), + cfg: this.serializeCfg(body.getCfg()), + }; + } + serializeMethodParameter(parameter) { + return { + name: parameter.getName(), + type: this.serializeType(parameter.getType()), + isOptional: parameter.isOptional(), + isRest: parameter.hasDotDotDotToken(), + }; + } + serializeImportInfo(importInfo) { + return { + importClauseName: importInfo.getImportClauseName(), + importType: importInfo.getImportType(), + importFrom: importInfo.getFrom(), + nameBeforeAs: importInfo.getNameBeforeAs(), + modifiers: importInfo.getModifiers(), + originTsPosition: this.serializeLineColPosition(importInfo.getOriginTsPosition()), + }; + } + serializeExportInfo(exportInfo) { + return { + exportClauseName: exportInfo.getExportClauseName(), + exportClauseType: exportInfo.getExportClauseType(), + exportFrom: exportInfo.getFrom(), + nameBeforeAs: exportInfo.getNameBeforeAs(), + isDefault: exportInfo.isDefault(), + modifiers: exportInfo.getModifiers(), + originTsPosition: this.serializeLineColPosition(exportInfo.getOriginTsPosition()), + }; + } + serializeDecorator(decorator) { + return { + kind: decorator.getKind(), + }; + } + serializeLineColPosition(position) { + return { + line: position.getLineNo(), + col: position.getColNo(), + }; + } + serializeType(type) { + if (type === undefined) { + throw new Error('Type is undefined'); + } + if (type instanceof AnyType) { + return { + _: 'AnyType', + }; + } + else if (type instanceof UnknownType) { + return { + _: 'UnknownType', + }; + } + else if (type instanceof VoidType) { + return { + _: 'VoidType', + }; + } + else if (type instanceof NeverType) { + return { + _: 'NeverType', + }; + } + else if (type instanceof UnionType) { + return { + _: 'UnionType', + types: type.getTypes().map(type => this.serializeType(type)), + }; + } + else if (type instanceof IntersectionType) { + return { + _: 'IntersectionType', + types: type.getTypes().map((type) => this.serializeType(type)), + }; + } + else if (type instanceof TupleType) { + return { + _: 'TupleType', + types: type.getTypes().map(type => this.serializeType(type)), + }; + } + else if (type instanceof BooleanType) { + return { + _: 'BooleanType', + }; + } + else if (type instanceof NumberType) { + return { + _: 'NumberType', + }; + } + else if (type instanceof BigIntType) { + return { + _: 'BigIntType', + }; + } + else if (type instanceof StringType) { + return { + _: 'StringType', + }; + } + else if (type instanceof NullType) { + return { + _: 'NullType', + }; + } + else if (type instanceof UndefinedType) { + return { + _: 'UndefinedType', + }; + } + else if (type instanceof LiteralType) { + return { + _: 'LiteralType', + literal: type.getLiteralName(), + }; + } + else if (type instanceof ClassType) { + return { + _: 'ClassType', + signature: this.serializeClassSignature(type.getClassSignature()), + typeParameters: type.getRealGenericTypes()?.map(type => this.serializeType(type)), + }; + } + else if (type instanceof FunctionType) { + return { + _: 'FunctionType', + signature: this.serializeMethodSignature(type.getMethodSignature()), + typeParameters: type.getRealGenericTypes()?.map(type => this.serializeType(type)), + }; + } + else if (type instanceof ArrayType) { + return { + _: 'ArrayType', + elementType: this.serializeType(type.getBaseType()), + dimensions: type.getDimension(), + }; + } + else if (type instanceof UnclearReferenceType) { + return { + _: 'UnclearReferenceType', + name: type.getName(), + typeParameters: type.getGenericTypes().map(type => this.serializeType(type)), + }; + } + else if (type instanceof GenericType) { + let constraint = type.getConstraint(); + let defaultType = type.getDefaultType(); + return { + _: 'GenericType', + name: type.getName(), + constraint: constraint && this.serializeType(constraint), + defaultType: defaultType && this.serializeType(defaultType), + }; + } + else if (type instanceof AliasType) { + return { + _: 'AliasType', + name: type.getName(), + originalType: this.serializeType(type.getOriginalType()), + signature: this.serializeAliasTypeSignature(type.getSignature()), + }; + } + else if (type instanceof AnnotationNamespaceType) { + return { + _: 'AnnotationNamespaceType', + originType: type.getOriginType(), + namespaceSignature: this.serializeNamespaceSignature(type.getNamespaceSignature()), + }; + } + else if (type instanceof AnnotationTypeQueryType) { + return { + _: 'AnnotationTypeQueryType', + originType: type.getOriginType(), + }; + } + else if (type instanceof EnumValueType) { + const c = type.getConstant(); + return { + _: 'EnumValueType', + signature: this.serializeFieldSignature(type.getFieldSignature()), + constant: c && this.serializeValue(c), + }; + } + else { + console.warn(`Unhandled Type: ${type.constructor.name} (${type.toString()})`); + return { + _: type.constructor.name, + text: type.toString(), + }; + } + } + serializeFileSignature(file) { + return { + projectName: file.getProjectName(), + fileName: file.getFileName(), + }; + } + serializeNamespaceSignature(namespace) { + let dns = namespace.getDeclaringNamespaceSignature(); + return { + name: namespace.getNamespaceName(), + declaringFile: this.serializeFileSignature(namespace.getDeclaringFileSignature()), + declaringNamespace: dns && this.serializeNamespaceSignature(dns), + }; + } + serializeClassSignature(clazz) { + let dns = clazz.getDeclaringNamespaceSignature(); + return { + name: clazz.getClassName(), + declaringFile: this.serializeFileSignature(clazz.getDeclaringFileSignature()), + declaringNamespace: dns && this.serializeNamespaceSignature(dns), + }; + } + serializeFieldSignature(field) { + let declaringSignature = field.getDeclaringSignature(); + let declaringClass; + if (declaringSignature instanceof ClassSignature) { + declaringClass = this.serializeClassSignature(declaringSignature); + } + else { + declaringClass = this.serializeNamespaceSignature(declaringSignature); + } + return { + declaringClass, + name: field.getFieldName(), + type: this.serializeType(field.getType()), + }; + } + serializeMethodSignature(method) { + return { + declaringClass: this.serializeClassSignature(method.getDeclaringClassSignature()), + name: method.getMethodSubSignature().getMethodName(), + parameters: method + .getMethodSubSignature() + .getParameters() + .map(param => this.serializeMethodParameter(param)), + returnType: this.serializeType(method.getType()), + }; + } + serializeAliasTypeSignature(signature) { + return { + name: signature.getName(), + method: this.serializeMethodSignature(signature.getDeclaringMethodSignature()), + }; + } + serializeCfg(cfg) { + const visited = new Set(); + const stack = []; + const startingBlock = cfg.getStartingBlock(); + if (startingBlock) { + stack.push(startingBlock); + } + let id = 0; + while (stack.length > 0) { + const block = stack.pop(); + if (visited.has(block)) { + continue; + } + visited.add(block); + block.setId(id++); + stack.push(...block.getSuccessors()); + } + return { + blocks: Array.from(visited).map(block => this.serializeBasicBlock(block)), + }; + } + serializeBasicBlock(block) { + const successors = block.getSuccessors().map(succ => succ.getId()); + successors.sort((a, b) => a - b); + const predecessors = block.getPredecessors().map(pred => pred.getId()); + predecessors.sort((a, b) => a - b); + return { + id: block.getId(), + successors, + predecessors, + stmts: block.getStmts().map(stmt => this.serializeStmt(stmt)), + }; + } + serializeLocal(local) { + return { + name: local.getName(), + type: this.serializeType(local.getType()), + }; + } + serializeConstant(constant) { + return { + value: constant.getValue(), + type: this.serializeType(constant.getType()), + }; + } + serializeValue(value) { + if (value === undefined) { + throw new Error('Value is undefined'); + } + if (value instanceof Local) { + return { + _: 'Local', + ...this.serializeLocal(value), + }; + } + else if (value instanceof Constant) { + return { + _: 'Constant', + ...this.serializeConstant(value), + }; + } + else if (value instanceof ArkNewExpr) { + return { + _: 'NewExpr', + classType: this.serializeType(value.getClassType()), + }; + } + else if (value instanceof ArkNewArrayExpr) { + return { + _: 'NewArrayExpr', + elementType: this.serializeType(value.getBaseType()), + size: this.serializeValue(value.getSize()), + }; + } + else if (value instanceof ArkDeleteExpr) { + return { + _: 'DeleteExpr', + arg: this.serializeValue(value.getField()), + }; + } + else if (value instanceof ArkAwaitExpr) { + return { + _: 'AwaitExpr', + arg: this.serializeValue(value.getPromise()), + }; + } + else if (value instanceof ArkYieldExpr) { + return { + _: 'YieldExpr', + arg: this.serializeValue(value.getYieldValue()), + }; + } + else if (value instanceof ArkTypeOfExpr) { + return { + _: 'TypeOfExpr', + arg: this.serializeValue(value.getOp()), + }; + } + else if (value instanceof ArkInstanceOfExpr) { + return { + _: 'InstanceOfExpr', + arg: this.serializeValue(value.getOp()), + checkType: this.serializeType(value.getCheckType()), + }; + } + else if (value instanceof ArkCastExpr) { + return { + _: 'CastExpr', + arg: this.serializeValue(value.getOp()), + type: this.serializeType(value.getType()), + }; + } + else if (value instanceof ArkPhiExpr) { + const args = value.getArgs(); + const argToBlock = value.getArgToBlock(); + return { + _: 'PhiExpr', + args: args.map(arg => this.serializeValue(arg)), + blocks: args.map(arg => argToBlock.get(arg).getId()), + type: this.serializeType(value.getType()), + }; + } + else if (value instanceof ArkConditionExpr) { + return { + _: 'ConditionExpr', + op: value.getOperator(), + left: this.serializeValue(value.getOp1()), + right: this.serializeValue(value.getOp2()), + type: this.serializeType(value.getType()), + }; + } + else if (value instanceof ArkNormalBinopExpr) { + return { + _: 'BinopExpr', + op: value.getOperator(), + left: this.serializeValue(value.getOp1()), + right: this.serializeValue(value.getOp2()), + }; + } + else if (value instanceof ArkUnopExpr) { + return { + _: 'UnopExpr', + op: value.getOperator(), + arg: this.serializeValue(value.getOp()), + }; + } + else if (value instanceof ArkInstanceInvokeExpr) { + return { + _: 'InstanceCallExpr', + instance: this.serializeValue(value.getBase()), + method: this.serializeMethodSignature(value.getMethodSignature()), + args: value.getArgs().map(arg => this.serializeValue(arg)), + }; + } + else if (value instanceof ArkStaticInvokeExpr) { + return { + _: 'StaticCallExpr', + method: this.serializeMethodSignature(value.getMethodSignature()), + args: value.getArgs().map(arg => this.serializeValue(arg)), + }; + } + else if (value instanceof ArkPtrInvokeExpr) { + return { + _: 'PtrCallExpr', + ptr: this.serializeValue(value.getFuncPtrLocal()), + method: this.serializeMethodSignature(value.getMethodSignature()), + args: value.getArgs().map(arg => this.serializeValue(arg)), + }; + } + else if (value instanceof ArkThisRef) { + return { + _: 'ThisRef', + type: this.serializeType(value.getType()), + }; + } + else if (value instanceof ArkParameterRef) { + return { + _: 'ParameterRef', + index: value.getIndex(), + type: this.serializeType(value.getType()), + }; + } + else if (value instanceof ArkArrayRef) { + return { + _: 'ArrayRef', + array: this.serializeValue(value.getBase()), + index: this.serializeValue(value.getIndex()), + type: this.serializeType(value.getType()), + }; + } + else if (value instanceof ArkCaughtExceptionRef) { + return { + _: 'CaughtExceptionRef', + type: this.serializeType(value.getType()), + }; + } + else if (value instanceof GlobalRef) { + let ref = value.getRef(); + return { + _: 'GlobalRef', + name: value.getName(), + ref: ref ? this.serializeValue(ref) : null, + }; + } + else if (value instanceof ClosureFieldRef) { + return { + _: 'ClosureFieldRef', + base: this.serializeLocal(value.getBase()), + fieldName: value.getFieldName(), + type: this.serializeType(value.getType()), + }; + } + else if (value instanceof ArkInstanceFieldRef) { + return { + _: 'InstanceFieldRef', + instance: this.serializeValue(value.getBase()), + field: this.serializeFieldSignature(value.getFieldSignature()), + }; + } + else if (value instanceof ArkStaticFieldRef) { + return { + _: 'StaticFieldRef', + field: this.serializeFieldSignature(value.getFieldSignature()), + }; + } + else { + console.warn(`Unhandled Value: ${value.constructor.name} (${value.toString()})`); + return { + _: value.constructor.name, + text: value.toString(), + type: this.serializeType(value.getType()), + }; + } + } + serializeStmt(stmt) { + if (stmt instanceof ArkAssignStmt) { + return { + _: 'AssignStmt', + left: this.serializeValue(stmt.getLeftOp()), + right: this.serializeValue(stmt.getRightOp()), + }; + } + else if (stmt instanceof ArkInvokeStmt) { + return { + _: 'CallStmt', + expr: this.serializeValue(stmt.getInvokeExpr()), + }; + } + else if (stmt instanceof ArkIfStmt) { + return { + _: 'IfStmt', + condition: this.serializeValue(stmt.getConditionExpr()), + }; + } + else if (stmt instanceof ArkReturnVoidStmt) { + return { + _: 'ReturnVoidStmt', + }; + } + else if (stmt instanceof ArkReturnStmt) { + return { + _: 'ReturnStmt', + arg: this.serializeValue(stmt.getOp()), + }; + } + else if (stmt instanceof ArkThrowStmt) { + return { + _: 'ThrowStmt', + arg: this.serializeValue(stmt.getOp()), + }; + } + else { + console.warn(`Unhandled Stmt: ${stmt.constructor.name} (${stmt.toString()})`); + return { + _: stmt.constructor.name, + text: stmt.toString(), + }; + } + } +} + +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @category save + */ +class ArkIRFieldPrinter extends BasePrinter { + field; + constructor(field, indent = '') { + super(indent); + this.field = field; + } + getLine() { + return this.field.getOriginPosition().getLineNo(); + } + dump() { + this.printer.clear(); + const commentsMetadata = this.field.getMetadata(ArkMetadataKind.LEADING_COMMENTS); + if (commentsMetadata instanceof CommentsMetadata) { + this.printComments(commentsMetadata); + } + this.printDecorator(this.field.getDecorators()); + this.printer.writeIndent(); + if (this.field.getCategory() !== FieldCategory.ENUM_MEMBER) { + this.printer.writeSpace(this.modifiersToString(this.field.getModifiers())); + } + this.printer.write(this.field.getName()); + if (this.field.getQuestionToken()) { + this.printer.write('?'); + } + if (this.field.getExclamationToken()) { + this.printer.write('!'); + } + // property.getInitializer() PropertyAccessExpression ArrowFunction ClassExpression FirstLiteralToken StringLiteral + if (!(this.field.getType() instanceof UnknownType) && this.field.getCategory() !== FieldCategory.ENUM_MEMBER) { + this.printer.write(`: ${this.field.getType().toString()}`); + } + if (this.field.getCategory() === FieldCategory.ENUM_MEMBER) { + this.printer.writeLine(','); + } + else { + this.printer.writeLine(''); + } + return this.printer.toString(); + } +} + +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @category save + */ +class ArkIRMethodPrinter extends BasePrinter { + method; + constructor(method, indent = '') { + super(indent); + this.method = method; + } + dump() { + this.printer.clear(); + const commentsMetadata = this.method.getMetadata(ArkMetadataKind.LEADING_COMMENTS); + if (commentsMetadata instanceof CommentsMetadata) { + this.printComments(commentsMetadata); + } + this.printMethod(this.method); + return this.printer.toString(); + } + getLine() { + let line = this.method.getLine(); + if (line === null) { + line = 0; + } + if (line > 0) { + return line; + } + const stmts = []; + const cfg = this.method.getCfg(); + if (cfg) { + cfg.getStmts() + .reverse() + .forEach(stmt => stmts.push(stmt)); + } + for (const stmt of stmts) { + if (stmt.getOriginPositionInfo().getLineNo() > 0) { + return stmt.getOriginPositionInfo().getLineNo(); + } + } + return line; + } + printMethod(method) { + this.printDecorator(method.getDecorators()); + this.printer.writeIndent().write(this.methodProtoToString(method)); + // abstract function no body + if (!method.getBody()) { + this.printer.writeLine(''); + return; + } + this.printer.writeLine(' {'); + this.printer.incIndent(); + this.printBody(method); + this.printer.decIndent(); + this.printer.writeIndent().writeLine('}'); + } + printBody(method) { + if (method.getCfg()) { + this.printCfg(method.getCfg()); + } + } + methodProtoToString(method) { + let code = new ArkCodeBuffer(); + code.writeSpace(this.modifiersToString(method.getModifiers())); + if (method.getAsteriskToken()) { + code.writeSpace('*'); + } + code.write(this.resolveMethodName(method.getName())); + const genericTypes = method.getGenericTypes(); + if (genericTypes && genericTypes.length > 0) { + let typeParameters = []; + genericTypes.forEach(genericType => { + typeParameters.push(genericType.toString()); + }); + code.write(`<${genericTypes.join(', ')}>`); + } + let parameters = []; + method.getParameters().forEach(parameter => { + let str = parameter.getName(); + if (parameter.hasDotDotDotToken()) { + str = `...${parameter.getName()}`; + } + if (parameter.isOptional()) { + str += '?'; + } + if (parameter.getType()) { + str += ': ' + parameter.getType().toString(); + } + parameters.push(str); + }); + code.write(`(${parameters.join(', ')})`); + const returnType = method.getReturnType(); + code.write(`: ${returnType.toString()}`); + return code.toString(); + } + printCfg(cfg) { + let blocks = cfg.getBlocks(); + let firstBB = true; + for (const block of blocks) { + if (!firstBB) { + this.printer.writeLine(''); + } + this.printBasicBlock(block); + if (firstBB) { + firstBB = false; + } + } + } + printBasicBlock(block) { + let successors = block.getSuccessors(); + this.printer.writeIndent().writeLine(`label${block.getId()}:`); + this.printer.incIndent(); + if (successors.length === 1) { + block.getStmts().map(stmt => { + this.printer.writeIndent().writeLine(stmt.toString()); + }); + this.printer.writeIndent().writeLine(`goto label${successors[0].getId()}`); + } + else if (successors.length === 2) { + for (const stmt of block.getStmts()) { + if (stmt instanceof ArkIfStmt) { + this.printer.writeIndent().writeLine(`${stmt.toString()} goto label${successors[0].getId()} label${successors[1].getId()}`); + } + else { + this.printer.writeIndent().writeLine(stmt.toString()); + } + } + } + else { + block.getStmts().map(stmt => { + this.printer.writeIndent().writeLine(stmt.toString()); + }); + } + this.printer.decIndent(); + } +} + +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @category save + */ +class ArkIRClassPrinter extends BasePrinter { + cls; + constructor(cls, indent = '') { + super(indent); + this.cls = cls; + } + getLine() { + return this.cls.getLine(); + } + dump() { + this.printer.clear(); + const commentsMetadata = this.cls.getMetadata(ArkMetadataKind.LEADING_COMMENTS); + if (commentsMetadata instanceof CommentsMetadata) { + this.printComments(commentsMetadata); + } + this.printDecorator(this.cls.getDecorators()); + // print export class name<> + extends c0 implements x1, x2 { + this.printer + .writeIndent() + .writeSpace(this.modifiersToString(this.cls.getModifiers())) + .write(`${this.classOriginTypeToString(this.cls.getCategory())} `); + this.printer.write(this.cls.getName()); + const genericsTypes = this.cls.getGenericsTypes(); + if (genericsTypes) { + this.printer.write(`<${genericsTypes.map(v => v.toString()).join(', ')}>`); + } + if (this.cls.getSuperClassName() && !this.cls.hasComponentDecorator()) { + this.printer.write(` extends ${this.cls.getSuperClassName()}`); + } + if (this.cls.getImplementedInterfaceNames().length > 0) { + this.printer.write(` implements ${this.cls.getImplementedInterfaceNames().join(', ')}`); + } + this.printer.writeLine(' {'); + this.printer.incIndent(); + let items = []; + let fieldItems = this.printFields(); + fieldItems.sort((a, b) => a.getLine() - b.getLine()); + items.push(...fieldItems); + let methodItems = this.printMethods(); + methodItems.sort((a, b) => a.getLine() - b.getLine()); + items.push(...methodItems); + let isFirstMethod = true; + let hasField = false; + items.forEach((v) => { + if (v instanceof ArkIRMethodPrinter) { + if (!isFirstMethod || hasField) { + this.printer.writeLine(''); + } + else { + isFirstMethod = false; + } + } + else if (v instanceof ArkIRFieldPrinter) { + hasField = true; + } + this.printer.write(v.dump()); + }); + this.printer.decIndent(); + this.printer.writeIndent().writeLine('}'); + return this.printer.toString(); + } + printMethods() { + let items = []; + for (let method of this.cls.getMethods(true)) { + items.push(new ArkIRMethodPrinter(method, this.printer.getIndent())); + } + return items; + } + printFields() { + let items = []; + for (let field of this.cls.getFields()) { + items.push(new ArkIRFieldPrinter(field, this.printer.getIndent())); + } + return items; + } +} + +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @category save + */ +class ArkIRNamespacePrinter extends BasePrinter { + ns; + constructor(ns, indent = '') { + super(indent); + this.ns = ns; + } + getLine() { + return this.ns.getLine(); + } + dump() { + const commentsMetadata = this.ns.getMetadata(ArkMetadataKind.LEADING_COMMENTS); + if (commentsMetadata instanceof CommentsMetadata) { + const comments = commentsMetadata.getComments(); + comments.forEach(comment => { + this.printer.writeIndent().writeLine(comment.content); + }); + } + this.printer.writeIndent().writeSpace(this.modifiersToString(this.ns.getModifiers())).writeLine(`namespace ${this.ns.getName()} {`); + this.printer.incIndent(); + let items = []; + // print class + for (let cls of this.ns.getClasses()) { + items.push(new ArkIRClassPrinter(cls, this.printer.getIndent())); + } + // print namespace + for (let childNs of this.ns.getNamespaces()) { + items.push(new ArkIRNamespacePrinter(childNs, this.printer.getIndent())); + } + // print exportInfos + for (let exportInfo of this.ns.getExportInfos()) { + items.push(new ExportPrinter(exportInfo, this.printer.getIndent())); + } + items.sort((a, b) => a.getLine() - b.getLine()); + items.forEach((v) => { + this.printer.write(v.dump()); + }); + this.printer.decIndent(); + this.printer.writeIndent().writeLine('}'); + return this.printer.toString(); + } +} + +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @category save + */ +class ArkIRFilePrinter extends Printer { + arkFile; + items = []; + constructor(arkFile) { + super(); + this.arkFile = arkFile; + } + dump() { + // print imports + this.items.push(...printImports(this.arkFile.getImportInfos(), this.printer.getIndent())); + // print namespace + for (let ns of this.arkFile.getNamespaces()) { + this.items.push(new ArkIRNamespacePrinter(ns)); + } + // print class + for (let cls of this.arkFile.getClasses()) { + this.items.push(new ArkIRClassPrinter(cls)); + } + // print export + for (let info of this.arkFile.getExportInfos()) { + this.items.push(new ExportPrinter(info)); + } + this.items.sort((a, b) => a.getLine() - b.getLine()); + this.items.forEach((v) => { + this.printer.write(v.dump()); + }); + return this.printer.toString(); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @example + * // dump method IR to ts source + * let method: Method = xx; + * let srcPrinter = new SourceMethodPrinter(method); + * PrinterBuilder.dump(srcPrinter, 'output.ts'); + * + * + * // dump method cfg to dot + * let dotPrinter = new DotMethodPrinter(method); + * PrinterBuilder.dump(dotPrinter, 'output.dot'); + * + * // dump project + * let printer = new PrinterBuilder('output'); + * for (let f of scene.getFiles()) { + * printer.dumpToTs(f); + * } + * + * @category save + */ +class PrinterBuilder { + outputDir; + constructor(outputDir = '') { + this.outputDir = outputDir; + } + static dump(source, output) { + fs$1.writeFileSync(output, source.dump()); + } + getOutputDir(arkFile) { + if (this.outputDir === '') { + return path$1.join(arkFile.getProjectDir(), '..', 'output'); + } + else { + return path$1.join(this.outputDir); + } + } + dumpToDot(arkFile, output = undefined) { + let filename = output; + if (filename === undefined) { + filename = path$1.join(this.getOutputDir(arkFile), arkFile.getName() + '.dot'); + } + fs$1.mkdirSync(path$1.dirname(filename), { recursive: true }); + let printer = new DotFilePrinter(arkFile); + PrinterBuilder.dump(printer, filename); + } + dumpToTs(arkFile, output = undefined) { + let filename = output; + if (filename === undefined) { + filename = path$1.join(this.getOutputDir(arkFile), arkFile.getName()); + } + if (path$1.extname(filename) === '') { + filename += '.ts'; + } + fs$1.mkdirSync(path$1.dirname(filename), { recursive: true }); + let printer = new SourceFilePrinter(arkFile); + PrinterBuilder.dump(printer, filename); + } + dumpToJson(arkFile, output = undefined) { + let filename = output; + if (filename === undefined) { + filename = path$1.join(this.getOutputDir(arkFile), arkFile.getName() + '.json'); + } + fs$1.mkdirSync(path$1.dirname(filename), { recursive: true }); + let printer = new JsonPrinter(arkFile); + PrinterBuilder.dump(printer, filename); + } + dumpToIR(arkFile, output = undefined) { + let filename = output; + if (filename === undefined) { + filename = path$1.join(this.getOutputDir(arkFile), arkFile.getName()); + } + filename += '.ir'; + fs$1.mkdirSync(path$1.dirname(filename), { recursive: true }); + let printer = new ArkIRFilePrinter(arkFile); + PrinterBuilder.dump(printer, filename); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class BaseEdge { + src; + dst; + kind; + constructor(s, d, k) { + this.src = s; + this.dst = d; + this.kind = k; + } + getSrcID() { + return this.src.getID(); + } + getDstID() { + return this.dst.getID(); + } + getSrcNode() { + return this.src; + } + getDstNode() { + return this.dst; + } + getKind() { + return this.kind; + } + setKind(kind) { + this.kind = kind; + } + getEndPoints() { + return { + src: this.src.getID(), + dst: this.dst.getID(), + }; + } + getDotAttr() { + return 'color=black'; + } +} +class BaseNode { + id; + kind; + inEdges = new Set(); + outEdges = new Set(); + constructor(id, k) { + this.id = id; + this.kind = k; + } + getID() { + return this.id; + } + getKind() { + return this.kind; + } + setKind(kind) { + this.kind = kind; + } + hasIncomingEdges() { + return this.inEdges.size !== 0; + } + hasOutgoingEdges() { + return this.outEdges.size === 0; + } + hasIncomingEdge(e) { + return this.inEdges.has(e); + } + hasOutgoingEdge(e) { + return this.outEdges.has(e); + } + addIncomingEdge(e) { + this.inEdges.add(e); + } + addOutgoingEdge(e) { + this.outEdges.add(e); + } + removeIncomingEdge(e) { + return this.inEdges.delete(e); + } + removeOutgoingEdge(e) { + return this.outEdges.delete(e); + } + getIncomingEdge() { + return this.inEdges; + } + getOutgoingEdges() { + return this.outEdges; + } + getDotAttr() { + return 'shape=box'; + } +} +class BaseExplicitGraph { + edgeNum = 0; + nodeNum = 0; + idToNodeMap; + edgeMarkSet; + constructor() { + this.idToNodeMap = new Map(); + this.edgeMarkSet = new Set(); + } + getNodeNum() { + return this.nodeNum; + } + nodesItor() { + return this.idToNodeMap.values(); + } + addNode(n) { + this.idToNodeMap.set(n.getID(), n); + this.nodeNum++; + } + getNode(id) { + if (!this.idToNodeMap.has(id)) { + throw new Error(`Can find Node # ${id}`); + } + return this.idToNodeMap.get(id); + } + hasNode(id) { + return this.idToNodeMap.has(id); + } + removeNode(id) { + if (this.idToNodeMap.delete(id)) { + this.nodeNum--; + return true; + } + return false; + } + hasEdge(src, dst) { + for (let e of src.getOutgoingEdges()) { + if (e.getDstNode() === dst) { + return true; + } + } + return false; + } + ifEdgeExisting(edge) { + let edgeMark = `${edge.getSrcID()}-${edge.getDstID()}:${edge.getKind()}`; + if (this.edgeMarkSet.has(edgeMark)) { + return true; + } + this.edgeMarkSet.add(edgeMark); + return false; + } + getNodesIter() { + return this.idToNodeMap.values(); + } + // 获取图中的所有边 + getEd() { + const edges = new Set(); + // 遍历所有节点,收集节点的出边和入边 + for (let node of this.idToNodeMap.values()) { + for (let edge of node.getOutgoingEdges()) { + edges.add(edge); // 将出边加入集合 + } + for (let edge of node.getIncomingEdge()) { + edges.add(edge); // 将入边加入集合 + } + } + return edges; + } +} + +// import { StringConstant } from "../../core/base/Constant"; +let index_2_pageId = new Map(); +const tabBar_2_TabContent = new Map(); +// const lifecycleUiAbility: Set = new Set([ +// "onCreate", // triggered when ui ability is created +// "WindowStageCreate", // triggered when window is created +// "onWindowStageDestroy", // triggered when window is destroyed +// "WindowStageWillDestroy", // triggered when window is about to be destroyed +// "onForeground", // triggered when app switches to foreground +// "onBackground", // triggered when app switches to background +// "onDestroy", // triggered when ui ability is destroyed +// "onNewWan", // only works for the second time ui ability is started and in singleton mode +// ]); +// const lifecycleView: Set = new Set([ +// "onPageShow", // triggered every time page is shown +// "onPageHide", // triggered every time page is hidden +// "onBackPress", // triggered when user presses back button +// "aboutToAppear", // triggered when component is about to appear, specific timing is after creating a new instance of custom component and before executing its `build` function +// "onDidBuild", // triggered after custom component's `build` function execution is complete +// "aboutToDisappear" // triggered before custom component's destructor is invoked +// ]); +// https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-system-router-V5#routerpush ps:已停止维护 +const SystemRouterMethodsSet = new Set([ + "push", + "replace", + "back", + "clear", +]); +// https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-router-V5#routerclear ps:不推荐 +const OhosRouterMethodsSet = new Set([ + "pushUrl", + "replaceUrl", + "pushNamedRoute", // 命名路由 + "replaceNamedRoute", // 命名路由 + "back", + "clear", + "push", + "replace", +]); +// https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-window-V5#setuicontent9 +const OhosWindowMethodsSet = new Set([ + "loadContent", + "loadContentByName", // 命名路由 + "setUIContent", + "startAbility", + "startAbilityForResult", + "terminateSelf", + "terminateSelfWithResult" +]); +// https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-navigation-navigation-V5#%E9%A1%B5%E9%9D%A2%E8%B7%B3%E8%BD%AC +//https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-basic-components-navigation-V5#%E6%8E%A5%E5%8F%A3 +const NavigationMethodsSet = new Set([ + "pushPath", + "pushPathByName", + "pushDestination", + "pushDestinationByName", + "pop", + "popToName", + "popToIndex", + "clear", + "replacePath", + "replacePathByName", + "replaceDestination", + "removeByName", + "removeByIndexes", + "removeByNavDestinationId", + "moveToTop", + "moveIndexToTop", + "setInterception", + // Navigation自定义动态路由 +]); +// 启动模式的枚举类型 @Note: how to use ? +exports.LaunchMode = void 0; +(function (LaunchMode) { + LaunchMode[LaunchMode["STANDARD"] = 0] = "STANDARD"; + LaunchMode[LaunchMode["MOVE_TO_TOP_SINGLETON"] = 1] = "MOVE_TO_TOP_SINGLETON"; + LaunchMode[LaunchMode["POP_TO_SINGLETON"] = 2] = "POP_TO_SINGLETON"; + LaunchMode[LaunchMode["NEW_INSTANCE"] = 3] = "NEW_INSTANCE"; +})(exports.LaunchMode || (exports.LaunchMode = {})); +class Stack { + items = []; + push(item) { + this.items.push(item); + } + pop() { + return this.items.pop(); + } + //获取栈顶元素,但不移除它 + peek() { + return this.items[this.items.length - 1]; + } + isEmpty() { + return this.items.length === 0; + } + size() { + return this.items.length; + } + popIndex(i) { + if (i < 0 || i >= this.size()) { + return undefined; + } + let cnt = this.size() - i; + while (cnt--) { + this.pop(); + } + return this.pop(); + } +} +class UIFuncEdge extends BaseEdge { + transition_method; + arkclass; + arkmethod; + attribute; // 允许为 null + node; // 允许为 null + stmt; + type; + //private analyzedMethods: Set = new Set(); + constructor(src, dst, transition_method, arkclass, arkmethod, attribute, node, stmt, type) { + super(src, dst, 0); // ?? 0是什么 + this.transition_method = transition_method; + this.arkclass = arkclass; + this.arkmethod = arkmethod; + this.attribute = attribute; + this.node = node; + this.stmt = stmt; + this.type = type; + } + static TransType = { + PUSH: 'PUSH', + POP: 'POP', + REPLACE: 'REPLACE', + REMOVE: 'REMOVE', + MOVE: 'MOVE', + CLEAR: 'CLEAR', + }; + transTypeToString(transType) { + return UIFuncEdge.TransType[transType] || ''; + } + getStmt() { + return this.stmt; + } + getTransitionMethod() { + return this.transition_method; + } + getApiClass() { + return this.arkclass; + } + getType() { + return this.type; + } + getApiMethod() { + return this.arkmethod; + } + getAttribute() { + return this.attribute; + } + getNode() { + return this.node; + } + setNode(node) { + this.node = node; + } + setAttribute(attribute) { + this.attribute = attribute; + } + // 根据实际需要添加一些属性描述边的特性 + getDotAttr() { + // 获取 Dot 标签的内容 + const label = this.getDotLabel(); + let color = "blue"; + // 根据 transition_method 值决定颜色 + if (this.transition_method == "include") { + color = "red"; + } + if (this.transition_method == "Tabs") { + color = "green"; + } + // 将颜色和标签合并为一个字符串 + return `color=${color}, label="${label}"`; + } + getDotLabel() { + // 使用可选链操作符(?.)来防止 arkclass 或 arkmethod 为 undefined 时出错 + const className = this.transition_method === "include" ? "null" : this.arkclass.getName(); + return `Transition: ${this.transition_method}, Class: ${className}, attribute: ${this.attribute} ,node :${this.node?.xpath} , componet:${this.node?.name}`; + } +} +class UIFuncBackEdge { + src; + transition_method; + arkclass; + arkmethod; + attribute; // 允许为 null + node; // 允许为 null + backindex; + stmt; + constructor(src, transition_method, arkclass, arkmethod, attribute, node, backindex, stmt) { + this.src = src; + this.transition_method = transition_method; + this.arkclass = arkclass; + this.arkmethod = arkmethod; + this.attribute = attribute; + this.node = node; + this.backindex = backindex; + this.stmt = stmt; + } + getStmt() { + return this.stmt; + } + getSrc() { + return this.src; + } + getTransistionMethod() { + return this.transition_method; + } + getArkClass() { + return this.arkclass; + } + getArkMethod() { + return this.arkmethod; + } + getAttribute() { + return this.attribute; + } + getViewTreeNode() { + return this.node; + } + getbackIndex() { + return this.backindex; + } +} +class UIFuncNode extends BaseNode { + pageId; + class; + type; + dialog_info; + dialogNode; + backindex_edges = []; + router_map; + backtop_edges = []; + clear_edges = []; + constructor(id, pageId, arkclass) { + super(id, 0); // 假设节点的类型是基础类型 0 + this.pageId = pageId; + this.class = arkclass; + this.backindex_edges = []; + this.backtop_edges = []; + this.clear_edges = []; + this.router_map = new Map(); + this.type = "unkown"; + this.dialog_info = "unkown"; + } + getPageId() { + return this.pageId; + } + getClass() { + return this.class; + } + getDotAttr() { + return 'shape=ellipse'; // 节点的形状设置为椭圆 + } + getDotLabel() { + const className = this.class.getSignature().toString(); + if (this.dialogNode) { + return `ID: ${this.pageId}\nDialog: ${this.dialog_info}`; + } + return `ID: ${this.pageId}\nFile: ${className}`; + } +} +class UIFuncGraph extends BaseExplicitGraph { + pageToUIFuncNodeMap = new Map(); + entries; + ArkClass2PageIDMap = new Map(); + scene; + analyzedCfg = new Set(); + analyzedCfg2 = new Set(); + //private navi_route_map: RouteMap[] = []; + constructor(s) { + super(); + this.scene = s; + } + // 向图中添加Page Node + addClass(pageId, arkclass) { + if (!this.pageToUIFuncNodeMap.has(pageId)) { + const node = new UIFuncNode(this.nodeNum++, pageId, arkclass); + this.addNode(node); + this.pageToUIFuncNodeMap.set(pageId, node); + this.ArkClass2PageIDMap.set(arkclass, pageId); + } + } + addDialogNode(pageId, view_node) { + if (!this.pageToUIFuncNodeMap.has(pageId)) { + let rand_class; + for (const it of this.scene.getClasses()) { + if (it) { + rand_class = it; + } + break; + } + if (rand_class) { + const node = new UIFuncNode(this.nodeNum++, pageId, rand_class); + node.dialog_info = `dialog_${view_node.name}`; + if (view_node.signature) { + node.dialog_info = view_node.signature.toString(); + } + node.dialogNode = view_node; + this.addNode(node); + this.pageToUIFuncNodeMap.set(pageId, node); + } + } + } + getGraphName() { + return "UIFuncGraph"; + } + getNodeByPageId(pageId) { + return this.pageToUIFuncNodeMap.get(pageId); + } + getPageIdByArkFile(arkclass) { + return this.ArkClass2PageIDMap.get(arkclass); + } + getNodeByArkClass(arkclass) { + let pageId = this.ArkClass2PageIDMap.get(arkclass); + if (pageId !== undefined) { + return this.getNodeByPageId(pageId); // 确保 pageId 不是 undefined + } + return undefined; // 如果 pageId 是 undefined,返回 undefined + } + getNodeByInfo(info) { + for (let it of this.pageToUIFuncNodeMap.values()) { + if (it.dialog_info == info) { + return it; + } + } + return undefined; + } + // 向图中添加 边 + addEdge(edge) { + //console.log("add Edge: ", edge); + //console.log("add attribute: ",edge.getAttribute()); + // 2025.7.21 注释 + // if (this.ifEdgeExisting(edge) && this.getExistingEdge(edge)) { + // console.log("已经有了"); + // let old_edge = this.getExistingEdge(edge); + // if (old_edge) { + // console.log("old_edge 也有"); + // let node = edge.getNode(); + // if (node) { + // old_edge.setNode(node); + // let attribute = edge.getAttribute(); + // if (attribute) { + // console.log("修改attribute"); + // old_edge.setAttribute(attribute); + // } + // } + // } + // return; + // } + if (this.ifEdgeExisting(edge)) { + return; + } + const srcNode = edge.getSrcNode(); + const dstNode = edge.getDstNode(); + srcNode.addOutgoingEdge(edge); + dstNode.addIncomingEdge(edge); + this.edgeNum++; + } + // 留下 UIAibility 和 page 节点 + removeNonUiabilityAndNonPageNodes() { + for (let it of this.pageToUIFuncNodeMap.values()) { + let arkclass = it.getClass(); + if (arkclass.checkIsUIAbility() == false && arkclass.getDeclaringArkFile().checkIsPage() == false) { + console.log("remove : ", arkclass.getName()); + this.removeNode(it.getID()); + } + } + } + removeTestNode() { + for (let it of this.pageToUIFuncNodeMap.values()) { + let arkclass = it.getClass(); + if (arkclass.getName().includes("Test")) { + //console.log("remove : ", arkfile.getName()); + this.removeNode(it.getID()); + } + } + } + // 展示page内的关系 -- 删除与page无关的节点 + // public showMapByArkFile(arkfile: ArkFile): void { + // console.log("begin: -----------------------------------"); + // let import_list: ArkFile[] = this.scene.getImportFiles(arkfile); + // // 对目前的 边 进行 BFS 遍历 + // let queue: Array = []; + // let vis: Map = new Map(); + // for (const file of import_list) { + // queue.push(file); + // } + // // BFS + // while (queue.length > 0) { + // const topfile = queue.shift() as ArkFile; + // if (vis.has(topfile)) { + // continue; + // } + // console.log("top :", topfile.getName()); + // vis.set(topfile, true); + // let import_list2 = this.scene.getImportFiles(topfile); + // for (const file of import_list2) { + // console.log("file: ", file.getName()); + // if (file) { + // if (!vis.has(file)) { + // import_list.push(file); + // queue.push(file); + // } + // } + // } + // } + // // 删除不在 stay 里面的 节点 + // let import_node: Map = new Map(); + // for (const arkfile of import_list) { + // let node = this.getNodeByArkClass(arkfile); + // if (node) { + // import_node.set(node, true); + // console.log("stay :", node.getFile().getName()); + // } + // } + // for (let it of this.pageToUIFuncNodeMap.values()) { + // if (!import_node.has(it)) { + // this.removeNode(it.getID()); + // } + // } + // } + // 获取与指定Page 相关的跳转关系 + getUiabilitiesFromPage(pageId) { + const node = this.pageToUIFuncNodeMap.get(pageId); + if (!node) { + return []; + } + const edges = []; + for (const edge of this.getEd()) { + if (edge.getSrcNode() === node) { + edges.push(edge); + } + } + return edges; + } + getEntries() { + console.log(this.scene); + return this.entries; + } + setEntries(n) { + this.entries = n; + } + dump(name, entry) { + let printer = new GraphPrinter(this); + if (entry) { + printer.setStartID(entry); + } + PrinterBuilder.dump(printer, name); + let jsonPrinter = new GraphJsonPrinter(this); + if (entry) { + jsonPrinter.setStartID(entry); + } + PrinterBuilder.dump(jsonPrinter, name + ".json"); + } + // https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-system-router-V5#routerpush + solveSystemRouterMethod(arkclass, arkfile, arkmethod, threeAddresStmt, api_name, attribute, node, type) { + let expr = threeAddresStmt.getInvokeExpr(); + let args = expr?.getArgs(); + if (args && args.length > 0) { // 里面有参数的情况:push,replace,back + const firstarg = args[0]; + console.log("firstarg : ", firstarg); + // 如果第一个参数是 Local 类型 (局部变量) + if (firstarg instanceof Local) { + if (firstarg.getType() instanceof ClassType) { //参数的类型是类 类型 + let target_class = this.GetTargetClassFormClassTypeWithFieldName(arkclass, arkfile, arkmethod, threeAddresStmt, "uri", firstarg, api_name); + if (target_class) { + // 使用 createEdgeFromFiles 函数创建边 + let edge = this.createEdgeFromClass(arkclass, target_class, api_name, arkmethod, attribute, node, threeAddresStmt, type); + if (edge) { + this.addEdge(edge); + } + } + else { + console.log(threeAddresStmt + ' target_file is undefined'); + console.log("找不到的:", firstarg); + } + } + // 包装一下 + } + else if (firstarg instanceof StringConstant) { // 如果第一个参数是 StringConstan 字符串常量 + let load_class = this.StringConstantFindTargetClass(arkfile, arkclass, firstarg, api_name); + if (load_class) { + let edge = this.createEdgeFromClass(arkclass, load_class, api_name, arkmethod, attribute, node, threeAddresStmt, type); + if (edge) { + this.addEdge(edge); + } + } + else { + console.error('load page is undefined'); + } + } + } + else { // 没有参数的情况: .back() .clear() + console.log("no arg stmt: ", expr?.toString()); + let uifunc_node = this.getNodeByArkClass(arkclass); + if (uifunc_node) { + if (api_name == "back") { + uifunc_node.backtop_edges.push(new UIFuncBackEdge(uifunc_node, "back", arkclass, arkmethod, attribute, node, null, threeAddresStmt)); + } + else if (api_name == "clear") { + uifunc_node.clear_edges.push(new UIFuncBackEdge(uifunc_node, "clear", arkclass, arkmethod, attribute, node, null, threeAddresStmt)); + } + } + } + } + // https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-router-V5#routerback + solveOhosRouterMethod(arkclass, arkfile, arkmethod, threeAddresStmt, api_name, attribute, node, type) { + let expr = threeAddresStmt.getInvokeExpr(); + let args = expr?.getArgs(); + if (args && args.length > 0) { // 里面有参数的情况:pushUrl,replaceUrl,pushNamedRoute,replaceNamedRoute,back,push,replace + const firstarg = args[0]; + console.log("firstarg : ", firstarg); + // 如果第一个参数是 Local 类型 (局部变量) + if (firstarg instanceof Local) { + if (firstarg.getType() instanceof ClassType) { //参数的类型是类 类型 + let filedname; + if (api_name == "pushNamedRoute" || api_name == "replaceNamedRoute") { + filedname = "name;"; + } + else { + filedname = "url"; + } + let target_class = this.GetTargetClassFormClassTypeWithFieldName(arkclass, arkfile, arkmethod, threeAddresStmt, filedname, firstarg, api_name); + console.log("arkclass : ", target_class?.getName()); + if (target_class) { + // 使用 createEdgeFromFiles 函数创建边 + let edge = this.createEdgeFromClass(arkclass, target_class, api_name, arkmethod, attribute, node, threeAddresStmt, type); + if (edge) { + this.addEdge(edge); + } + } + else { + console.log(threeAddresStmt + ' target_file is undefined'); + console.log("找不到的:", firstarg); + } + } + // 包装一下 + } + else if (firstarg instanceof StringConstant) { // 如果第一个参数是 StringConstan 字符串常量 + let load_class = this.StringConstantFindTargetClass(arkfile, arkclass, firstarg, api_name); + if (load_class) { + let edge = this.createEdgeFromClass(arkclass, load_class, api_name, arkmethod, attribute, node, threeAddresStmt, type); + if (edge) { + this.addEdge(edge); + } + } + else { + console.error('load page is undefined'); + } + } + else if (firstarg instanceof NumberConstant) { // router.back(1); + let index = Number(firstarg.getValue()); + let uifunc_node = this.getNodeByArkClass(arkclass); + if (api_name == "back" && index && uifunc_node) { + uifunc_node.backindex_edges.push(new UIFuncBackEdge(uifunc_node, "back", arkclass, arkmethod, attribute, node, index, threeAddresStmt)); + } + } + } + else { // 没有参数的情况: clear, + console.log("no arg stmt: ", expr?.toString()); + let uifunc_node = this.getNodeByArkClass(arkclass); + if (uifunc_node) { + if (api_name == "back") { + uifunc_node.backtop_edges.push(new UIFuncBackEdge(uifunc_node, "back", arkclass, arkmethod, attribute, node, null, threeAddresStmt)); + } + else if (api_name == "clear") { + uifunc_node?.clear_edges.push(new UIFuncBackEdge(uifunc_node, "clear", arkclass, arkmethod, attribute, node, null, threeAddresStmt)); + } + } + } + } + // https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-window-V5#setuicontent9 + solveOhosWindowMethod(arkclass, arkfile, arkmethod, threeAddresStmt, api_name, attribute, node, type) { + let expr = threeAddresStmt.getInvokeExpr(); + let args = expr?.getArgs(); + if (args && args.length > 0) { // 里面有参数的情况:pushUrl,replaceUrl,pushNamedRoute,replaceNamedRoute,back,push,replace + const firstarg = args[0]; + console.log("firstarg : ", firstarg); + console.log("firstarg type: ", firstarg.getType().toString()); + // 如果第一个参数是 Local 类型 (局部变量) + if (firstarg instanceof Local) { + if (firstarg.getType() instanceof ClassType) { //solveOhosWindowMethod 一般不是 ClassType + let filedname; + if (api_name == "pushNamedRoute" || api_name == "replaceNamedRoute") { + filedname = "name;"; + } + else { + filedname = "url"; + } + let target_class = this.GetTargetClassFormClassTypeWithFieldName(arkclass, arkfile, arkmethod, threeAddresStmt, filedname, firstarg, api_name); + if (target_class) { + // 使用 createEdgeFromFiles 函数创建边 + let edge = this.createEdgeFromClass(arkclass, target_class, api_name, arkmethod, attribute, node, threeAddresStmt, type); + if (edge) { + this.addEdge(edge); + } + } + else { + console.error(threeAddresStmt.toString() + 'target_file is undefined'); + } + } + // 包装一下 + } + else if (firstarg instanceof StringConstant) { // 如果第一个参数是 StringConstan 字符串常量 : setUIContent,loadContent,loadContentByName + let load_page = this.StringConstantFindTargetClass(arkfile, arkclass, firstarg, api_name); + if (load_page) { + let edge = this.createEdgeFromClass(arkclass, load_page, api_name, arkmethod, attribute, node, threeAddresStmt, type); + if (edge) { + this.addEdge(edge); + } + } + else { + console.error('load page is undefined'); + } + } + if (args.length > 1 && args[1] instanceof StringConstant) { + let secondarg = args[1]; + console.log("second value : ", secondarg.getValue()); + let load_page = this.StringConstantFindTargetClass(arkfile, arkclass, secondarg, api_name); + if (load_page) { + let edge = this.createEdgeFromClass(arkclass, load_page, api_name, arkmethod, attribute, node, threeAddresStmt, type); + if (edge) { + this.addEdge(edge); + } + } + else { + console.error('load page is undefined'); + } + } + } + else { // 没有参数的情况: clear, + console.log("no arg stmt: ", expr?.toString()); + } + } + solveNavigationMethod(arkclass, arkfile, arkmethod, threeAddresStmt, api_name, attribute, node, type) { + let expr = threeAddresStmt.getInvokeExpr(); + let args = expr?.getArgs(); + if (args && args.length > 0) { // 里面有参数的情况:pushUrl,replaceUrl,pushNamedRoute,replaceNamedRoute,back,push,replace + const firstarg = args[0]; + console.log("firstarg : ", firstarg); + console.log("firstarg type: ", firstarg.getType().toString()); + // 如果第一个参数是 Local 类型 (局部变量) + if (firstarg instanceof Local) { + if (firstarg.getType() instanceof ClassType) { //solveOhosWindowMethod 一般不是 ClassType + let filedname; + filedname = "name"; + let target_class = this.GetTargetClassFormClassTypeWithFieldName(arkclass, arkfile, arkmethod, threeAddresStmt, filedname, firstarg, api_name); + if (target_class) { + // 使用 createEdgeFromFiles 函数创建边 + let edge = this.createEdgeFromClass(arkclass, target_class, api_name, arkmethod, attribute, node, threeAddresStmt, type); + if (edge) { + this.addEdge(edge); + } + } + else { + console.error(threeAddresStmt.toString() + 'target_file is undefined'); + } + } + // 包装一下 + } + else if (firstarg instanceof StringConstant) { // 如果第一个参数是 StringConstan 字符串常量 : setUIContent,loadContent,loadContentByName + let load_page = this.StringConstantFindTargetClass(arkfile, arkclass, firstarg, api_name); + if (load_page) { + let edge = this.createEdgeFromClass(arkclass, load_page, api_name, arkmethod, attribute, node, threeAddresStmt, type); + if (edge) { + this.addEdge(edge); + } + } + else { + console.error('load page is undefined'); + } + } + if (args.length > 1 && args[1] instanceof StringConstant) { + let secondarg = args[1]; + console.log("second value : ", secondarg.getValue()); + let load_page = this.StringConstantFindTargetClass(arkfile, arkclass, secondarg, api_name); + if (load_page) { + let edge = this.createEdgeFromClass(arkclass, load_page, api_name, arkmethod, attribute, node, threeAddresStmt, type); + if (edge) { + this.addEdge(edge); + } + } + else { + console.error('load page is undefined'); + } + } + } + else { // 没有参数的情况: clear, + console.log("no arg stmt: ", expr?.toString()); + } + } + GetTargetClassFormClassTypeWithFieldName(arkclass, arkfile, arkmethod, threeAddresStmt, filedname, firstarg, api_name) { + let classSignature = firstarg.getType().getClassSignature(); + console.log("classSignature: ", classSignature); + let classname = classSignature.getClassName(); + let clazz = arkfile.getClassWithName(classname); + if (clazz) { + console.log("clazz: ", clazz); + // replaceUrl pushUrl router.back() like .... = router.replaceUrl({url: 'pages/Profile' } + let url = clazz.getFieldWithName(filedname); // https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-system-router-V5#routerpush + let initstmt = url?.getInitializer(); + console.log("filed name: ", filedname); + console.log("url: ", url); + console.log("initstmt: ", initstmt?.toString()); + if (initstmt && initstmt.length > 0 && initstmt[0] instanceof ArkAssignStmt) { + let rightop_type = initstmt[0].getRightOp(); + if (rightop_type instanceof StringConstant) { // 字符串常量 + return this.StringConstantFindTargetClass(arkfile, arkclass, rightop_type, api_name); + } + } + } + return null; + } + StringConstantFindTargetClass(srcfile, arkclass, rightop_type, api_name) { + let target_url = rightop_type.getValue(); + if (target_url == '') + return null; + // 1.命令路由 + let target_class; + console.log("debug api_name :", api_name); + if (api_name == "pushNamedRoute" || api_name == "replaceNamedRoute") { + // 去Scene的 routNameMap里面去查 @Entry("routerName:mypage") + let mp = this.scene.getRouteNameMap(); + target_class = mp.get(target_url); + } + else if (api_name == "pushPathByName" || api_name == "pushDestinationByName" || api_name == "replacePathByName" || api_name == "pushPath" || api_name == "replacePath") // https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V14/ts-basic-components-navigation-V14#pushpathbyname10 + { + // 1. PageMap 查表 要把起始页面的view tree node传过来? 找nav所引用的 对象 ? + const func_node = this.getNodeByArkClass(arkclass); + console.log("func_node: ", func_node?.getClass().getName()); + console.log("target_url: ", target_url); + const router_map = func_node?.router_map; + console.log("router_map: ", router_map); + if (router_map) { + router_map.forEach((innerMap, fieldSignature) => { + for (const [key, arkfile] of innerMap) { + if (key == target_url) { + console.log("Page map debug :", key, "------->", arkfile.getName()); + if (arkfile) { + return arkfile; + } + } + } + }); + } + // 去系统路由表里面去查 module + const module_scene = srcfile.getModuleScene(); + const mp = module_scene?.returnRouteMap(); + if (mp) { + target_class = mp.get(target_url); + if (target_class) { + console.log("gobal map debug :", target_url, "------->", target_class.getName()); + return target_class; + } + } + } + console.log("taget_url: ", target_url); + console.log("begin to find class by path substring"); + target_class = this.scene.getClassByPathSubstring(target_url); + if (target_class) { + return target_class; + } + else { + return null; + } + } + createEdgeFromClass(arkclass, target_class, api_name, arkmethod, attribute, node, stmt, type) { + let src_node = this.getNodeByArkClass(arkclass); + let dst_node = this.getNodeByArkClass(target_class); + if (src_node !== undefined && dst_node !== undefined) { + let edge = new UIFuncEdge(src_node, dst_node, api_name, arkclass, arkmethod, attribute, node, stmt, type); + console.log("add edge from " + src_node.getClass().getName() + " to " + dst_node.getClass().getName() + " api: " + api_name + " attribute: " + attribute + " xpath: ", node?.xpath); + return edge; + } + else { + console.error("src_class: ", arkclass.getSignature().toString()); + console.error("target_class: ", target_class.getSignature().toString()); + console.error("api: ", api_name); + console.error('Either src_node or dst_node is undefined'); + return null; + } + } + // @todo : 需要去完善 + ifEdgeExisting(edge) { + const srcNode = edge.getSrcNode(); + const dstNode = edge.getDstNode(); + for (let existingEdge of srcNode.getOutgoingEdges()) { + // 如果目标节点、转移方法、类和方法相同 + if (existingEdge.getDstNode() === dstNode && + existingEdge.getTransitionMethod() === edge.getTransitionMethod()) { + // 如果node.xpath相同,则返回true,表示已有边存在 + if (existingEdge.getNode()?.xpath === edge.getNode()?.xpath) { + return true; + } + } + } + return false; + } + getExistingEdge(edge) { + const srcNode = edge.getSrcNode(); + const dstNode = edge.getDstNode(); + for (let existingEdge of srcNode.getOutgoingEdges()) { + // 如果目标节点、转移方法、类和方法相同 + if (existingEdge.getDstNode() === dstNode && + existingEdge.getTransitionMethod() === edge.getTransitionMethod() && + existingEdge.getApiClass() === edge.getApiClass() && + existingEdge.getApiMethod() === edge.getApiMethod() && + existingEdge.getStmt() === edge.getStmt()) { + const existingNode = existingEdge.getNode(); + const newNode = edge.getNode(); + // 如果node.xpath相同,则返回true,表示已有边存在 + if (existingNode?.xpath && newNode?.xpath && existingNode.xpath.length <= newNode.xpath.length) { + return existingEdge; + } + } + } + return null; + } + // 接受一个 Value 类型 ,然后去追踪定位 api使用的地方 + AnalyzeValue(value, node, arkclass, arkmethod, attribute, stmt) { + console.log("vlaue type: ", value.constructor.name); + if (value instanceof Local) { + const valueType = value.getType(); + console.log("Local value type: ", valueType.constructor.name); + if (valueType instanceof FunctionType) { + //console.log("function: ", value); + this.AnalyzeFouctionTypeValue(valueType, node, arkclass, attribute); + } + else if (valueType instanceof ClassType) { + this.AnalyzeClassTypeValue(valueType, node, arkclass, attribute); + } + } + else if (value instanceof ArkInstanceInvokeExpr) { + this.AnalyzeInvokeExpr(value, node, arkclass, arkmethod, attribute, stmt); + } + else if (value instanceof ArkPtrInvokeExpr) { + this.AnalyzePtrInvokeExpr(value, node, arkclass, arkmethod, attribute, stmt); + } + else ; + console.log("Value 分析结束"); + } + AnalyzePtrInvokeExpr(value, node, arkclass, arkmethod, attribute, stmt) { + console.log("AnalyzePtrInvokeExpr: ", value); + } + // 如果是赋值语句,那么一定不是 API 调用点 目前来看,所要做的就是 分析 左右两边,追踪api的地点 + AnalyzeArkAssignStmt(stmt, node, arkclass, arkmethod, attribute) { + let leftop = stmt.getLeftOp(); + let rightop = stmt.getRightOp(); + // 跳过 this = this: ... 的赋值 + // console.log("right: ",rightop); + // console.log("left: ",leftop); + if (leftop instanceof Local && + leftop.getName() === "this" && + leftop.getType() instanceof ClassType && + leftop.getType().getClassSignature().getClassName() === arkclass.getSignature().getClassName() && + rightop instanceof ArkThisRef && + rightop.getType() instanceof ClassType && + rightop.getType().getClassSignature().getClassName() === arkclass.getSignature().getClassName()) { + return; + } + console.log("分析右边"); + this.AnalyzeValue(leftop, node, arkclass, arkmethod, attribute, stmt); + //console.log("右边 = ",rightop); + console.log("分析左边"); + this.AnalyzeValue(rightop, node, arkclass, arkmethod, attribute, stmt); + } + // 两个任务 1.看有没有 api ,2. 能否继续 追踪 + AnalyzeArkInvokeStmt(stmt, node, arkclass, arkmethod, attribute) { + //console.log("AnalyzeArkInvokeStmt: ",stmt.toString()); + const invokeExpr = stmt.getInvokeExpr(); + this.AnalyzeInvokeExpr(invokeExpr, node, arkclass, arkmethod, attribute, stmt); // + let uses = stmt.getUses(); + for (const use of uses) { + this.AnalyzeValue(use, node, arkclass, arkmethod, attribute, stmt); + } + } + AnalyzeFouctionTypeValue(value, node, arkclass, attribute) { + let method_signature = value.getMethodSignature(); + let method = this.getMethod(method_signature); + if (method) { + this.AnalyzeArkMethod(method, node, arkclass, attribute); + } + } + // 追踪这个类的 Stmt + AnalyzeClassTypeValue(vlaue, node, arkclass, attribute) { + let class_signature = vlaue.getClassSignature(); + let clazz = this.getClass(class_signature); + if (clazz) { + console.log("分析类: ", clazz.getSignature()); + this.AnalyzeArkClass(clazz, node, arkclass, attribute); + } + } + getMethod(method_signature) { + let class_signature = method_signature.getDeclaringClassSignature(); + let file_signature = class_signature.getDeclaringFileSignature(); + let file = this.scene.getFile(file_signature); + let clazz = file?.getClass(class_signature); + let method = clazz?.getMethod(method_signature); + return method; + } + getClass(class_signature) { + let file_signature = class_signature.getDeclaringFileSignature(); + let file = this.scene.getFile(file_signature); + let clazz = file?.getClass(class_signature); + return clazz; + } + //追踪这个类的 Stmt + AnalyzeArkMethod(method, node, arkclass, attribute) { + if (method.isConstructor()) + return; + console.log("分析方法:", arkclass.getName() + ";" + method.getName() + " attribute: " + attribute); + let body = method?.getBody(); + const cfg = body?.getCfg(); + if (cfg) { + this.AnalyzerArkCfg(cfg, node, arkclass, method, attribute); + } + } + AnalyzeArkClass(clazz, node, arkclass, attribute) { + // 那就是 分析这个类的所有方法 + for (const arkmethod of clazz.getMethods()) { // 要不要加true ? + this.AnalyzeArkMethod(arkmethod, node, arkclass, attribute); + } + } + AnalyzerArkCfg(cfg, node, arkclass, arkmethod, attribute) { + if (cfg.getDeclaringMethod().isConstructor()) + return; + let hash = ""; + for (const stmt of cfg.getStmts()) { + hash += stmt.toString(); + } + if (this.analyzedCfg2.has(hash) && !node) { + console.log("跳过1"); + return; + } + this.analyzedCfg2.add(hash); + hash += node?.xpath; + hash += arkclass.getSignature().toString(); + if (this.analyzedCfg.has(hash)) { + console.log("跳过2 xpath: ", node?.xpath); + return; + } + console.log("第一次分析: ", arkmethod.getName() + " xpath: ", node?.xpath); + this.analyzedCfg.add(hash); + //console.log("分析的这个cfg属于:",cfg.getDeclaringMethod().getName()); + for (const stmt of cfg.getStmts()) { + //console.log("AnalyzerArkCfg stmt: ",stmt.toString()); + //console.log("stmt constructor name: ", stmt.constructor.name); + if (stmt instanceof ArkInvokeStmt) { // 方法调用语句--- api 最有可能在语句 + console.log("方法调用stmt: ", stmt.toString() + " attribute: " + attribute); + // if(stmt.toString() == "instanceinvoke this.<@Healthy_life/entry/src/main/ets/view/HomeComponent.ets: HomeIndex.editTaskAction()>()"){ + // console.log("right :",(stmt as ArkInvokeStmt).getInvokeExpr()); + // } + // console.log("方法调用处的node: xpath:",node?.xpath); + this.AnalyzeArkInvokeStmt(stmt, node, arkclass, arkmethod, attribute); + } + else if (stmt instanceof ArkAssignStmt) { + console.log("赋值stmt: ", stmt.toString()); + this.AnalyzeArkAssignStmt(stmt, node, arkclass, arkmethod, attribute); + } + else { + console.log("都不是"); + } + } + } + // 分析一个 方法调用的表达式, 有方法体,有参数 + // 先追踪 后分析 + AnalyzeInvokeExpr(invokeExpr, node, arkclass, arkmethod, attribute, stmt) { + console.log("stmt: ", stmt.toString()); + // 1.追踪参数 + //console.log("AnalyzeInvokeExpr: ",stmt.toString()); + const args = invokeExpr.getArgs(); + // 要追踪就追踪好了,栈的信息并不会传过去 + for (let arg of args) { + this.AnalyzeValue(arg, node, arkclass, arkmethod, attribute, stmt); + } + // 2.追踪方法体 + // 分析别的方法,栈的信息不会共享 + const method = this.getMethod(invokeExpr.getMethodSignature()); + if (method) { + console.log("method: ", method.getName()); + this.AnalyzeArkMethod(method, node, arkclass, attribute); + } + // current_stack 在下面进行更新 + // 分析本身 先处理 高频的 实例方法调用 + if (invokeExpr instanceof ArkInstanceInvokeExpr) { + let base = invokeExpr.getBase(); //调用方法的实例 + const methodSubSignature = invokeExpr.getMethodSignature()?.getMethodSubSignature(); + const api_name = methodSubSignature?.getMethodName() ?? ""; + if ((base.getName() == "router" || base.getName() == "Router") && (SystemRouterMethodsSet.has(api_name) || OhosRouterMethodsSet.has(api_name))) { + // this.solveSystemRouterMethod // 去找到起点,终点,找到一条边 + // this.solveOhosRouterMethod + let edge = this.getEdgeFromApiExpr(invokeExpr, node, arkclass, arkmethod, api_name, attribute, stmt, 'Router'); + //console.log("find api :",api_name); + //console.log("xpath:",node?.xpath); + if (edge) { + // console.log("准备加边!:", (edge?.getSrcNode() as UIFuncNode).getFile().getName() + "----------->", (edge?.getDstNode() as UIFuncNode).getFile().getName()); + // console.log("node.xpath:", node?.xpath); + this.addEdge(edge); + } + if (node) { + let handler = new Handler(attribute || "", null, // @todo 把edge的dst改成ArkUIViewTreeNode + exports.UITransitionType.Router, undefined, 'Router API 调用', getNavigationType(api_name) // 假设使用 Push 跳转方式 + ); + node.handlers.push(handler); + } + console.log("找到了1: ", stmt.toString() + " method: " + arkmethod.getName()); + // console.log("node ",node); + } + if (OhosWindowMethodsSet.has(api_name)) { + console.log("找到了2: ", stmt.toString() + " method: " + arkmethod.getName()); + // this.solveOhosWindowMethod + // console.log("find api :",api_name); + // console.log("xpath:",node?.xpath); + let edge = this.getEdgeFromApiExpr(invokeExpr, node, arkclass, arkmethod, api_name, attribute, stmt, 'UIAibility'); + if (edge) { + // console.log("准备加边!:", (edge?.getSrcNode() as UIFuncNode).getFile().getName() + "----------->", (edge?.getDstNode() as UIFuncNode).getFile().getName()); + // console.log("node.xpath:", node?.xpath); + this.addEdge(edge); + } + if (node) { + let handler = new Handler(attribute || "", null, // @todo 把edge的dst改成ArkUIViewTreeNode + exports.UITransitionType.UIAibility, undefined, 'UIAibility API 调用', exports.NavigationType.None // 假设使用 Push 跳转方式 + ); + node.handlers.push(handler); + } + // console.log("node: ",node?.name); + // console.log("node xpath: ",node?.xpath); + } + if (NavigationMethodsSet.has(api_name)) { + // this.solveNavigationMethod + console.log("找到了: ", api_name); + let edge = this.getEdgeFromApiExpr(invokeExpr, node, arkclass, arkmethod, api_name, attribute, stmt, 'Navigation'); + if (edge) { + // console.log("准备加边!:", (edge?.getSrcNode() as UIFuncNode).getFile().getName() + "----------->", (edge?.getDstNode() as UIFuncNode).getFile().getName()); + // console.log("node.xpath:", node?.xpath); + this.addEdge(edge); + } + else { + console.log("边不存在"); + } + if (node) { + let handler = new Handler(attribute || "", null, // @todo 把edge的dst改成ArkUIViewTreeNode + exports.UITransitionType.Navigation, undefined, 'Navigation API 调用', getNavigationType(api_name) // 假设使用 Push 跳转方式 + ); + node.handlers.push(handler); + } + console.log("找到了3: ", stmt.toString() + " method: " + arkmethod.getName()); + // console.log("find api :", api_name); + // console.log("xpath:", node?.xpath); + // console.log("Handler: ", node?.handler); + // console.log("找到了: ",invokeExpr); + // console.log("node: ",node?.name); + // console.log("node xpath: ",node?.xpath); + } + } + } + // 分析参数,然后返回一条边,也有可能没有参数, + getEdgeFromApiExpr(invokeExpr, node, arkclass, arkmethod, api_name, attribute, stmt, type) { + const args = invokeExpr.getArgs(); + console.log("begin getEdgeFromApiExpr"); + console.log("api name:", api_name); + // console.log("args number: ",args.length); + if (args.length > 0 && args) { // 有参数的情况下 + const arg = invokeExpr.getArg(0); // 通常都是第一个 + const src_file = arkclass.getDeclaringArkFile(); + const target_class = this.getTargetClassFromValue(node, src_file, arkclass, arg, api_name); // 分析这个Value的值,得到一个ArkFile + console.log("target class :", target_class?.getName()); + if (target_class) { + const edge = this.createEdgeFromClass(arkclass, target_class, api_name, arkmethod, attribute, node, stmt, type); + return edge; + } + else { + console.log(api_name + "dont have target file"); + } + // setUIContent 也有可能是第二个 + if (args.length > 1) { + const arg = invokeExpr.getArg(1); // 通常都是第一个 + const src_class = arkclass; + const target_class = this.getTargetClassFromValue(node, src_file, arkclass, arg, api_name); // 分析这个Value的值,得到一个ArkFile + if (target_class) { + const edge = this.createEdgeFromClass(src_class, target_class, api_name, arkmethod, attribute, node, stmt, type); + return edge; + } + } + // 如果是 router.back(1) 上面是得不到边的 + if (arg instanceof NumberConstant) { + let uifunc_node = this.getNodeByArkClass(arkclass); + if (uifunc_node && (api_name == "back" || api_name == "pop")) { + console.log("find back(" + Number(arg.getValue()) + ") in " + uifunc_node.getClass().getName()); + console.log("xpath:", node?.xpath); + uifunc_node.backindex_edges.push(new UIFuncBackEdge(uifunc_node, api_name, arkclass, arkmethod, attribute, node, Number(arg.getValue()), stmt)); + } + } + } + else { + // 没有参数的情况 router.back(); + let uifunc_node = this.getNodeByArkClass(arkclass); + if (uifunc_node) { + if (api_name == "back" || api_name == "pop") { + console.log("find back/pop() in " + uifunc_node.getClass().getName()); + console.log("xpath:", node?.xpath); + uifunc_node.backtop_edges.push(new UIFuncBackEdge(uifunc_node, api_name, arkclass, arkmethod, attribute, node, null, stmt)); + console.log(uifunc_node.backtop_edges.length); + for (const edges of uifunc_node.backtop_edges) { + console.log("edge xpath: ", edges.getViewTreeNode()?.xpath); + } + } + else if (api_name == "clear") { + uifunc_node.clear_edges.push(new UIFuncBackEdge(uifunc_node, "clear", arkclass, arkmethod, attribute, node, null, stmt)); + } + } + return null; + } + return null; + } + getTargetClassFromValue(node, srcfile, arkclass, arg, api_name) { + //console.log("jxx datest: ",api_name + " " + arg); + ///console.log("arg: ",arg); + if (arg instanceof Local) { + // console.log("begin get target class from Local"); + // let result = backtraceLocalInitValue(arg); + // console.log("result: ", result); + return this.getTargetClassFromLocal(node, srcfile, arkclass, arg, api_name); + } + else if (arg instanceof StringConstant) { + // console.log("begin get target class from string constant"); + return this.getTargetClassFromStringConstant(node, srcfile, arkclass, arg, api_name); + } + return null; + } + getTargetClassFromLocal(node, srcfile, arkclass, arg, api_name) { + let arg_type = arg.getType(); + if (arg_type instanceof ClassType) { + console.log("getTargetClassFromLocal -> getTargetClassromClassType"); + return this.getTargetClassromClassType(node, srcfile, arkclass, arg_type, api_name); + } + else if (arg_type instanceof StringType) { + console.log("getTargetClassFromLocal -> getTargetClassFromStringType"); + return this.getTargetClassFromStringType(node, srcfile, arkclass, arg, api_name); + } + return null; + } + getTargetClassFromStringType(node, srcfile, arkclass, arg, api_name) { + const stmts = arg.getDeclaringStmt(); + if (stmts && stmts instanceof ArkAssignStmt) { + console.log("string type: ", stmts.toString()); + const rightop = stmts.getRightOp(); + if (rightop instanceof ArkStaticFieldRef) { + return this.getTargetClassFromArkStaticFieldRef(node, srcfile, arkclass, rightop, api_name); + } + } + return null; + } + getTargetClassFromArkStaticFieldRef(node, srcfile, arkclass, rightop_type, api_name) { + const type = rightop_type.getFieldSignature(); + const filedname = type.getFieldName(); + const it = type.getDeclaringSignature(); + const it2 = this.scene.getClass(it); + const mp = it2?.getStaticFieldWithName(filedname); + const inists = mp?.getInitializer(); + if (inists) { + if (inists[0] instanceof ArkAssignStmt) { + const rightop = inists[0].getRightOp(); + if (rightop instanceof StringConstant) { + return this.StringConstantFindTargetClass(srcfile, arkclass, rightop, api_name); + } + } + // for(const ist of inists){ + // console.log("jxx init:",ist); + // } + } + return null; + } + parseUrlFromArkField(field, scene, url) { + const initStmts = field.getInitializer(); + const newLocals = []; + for (const stmt of initStmts) { + if (stmt instanceof ArkAssignStmt) { + console.log("stmt: ", stmt.toString()); + let rightOp = stmt.getRightOp(); + if (rightOp instanceof ArkNewExpr) { + newLocals.push(stmt.getLeftOp()); + } + } + } + console.log("newLocals size = ", newLocals.length); + for (const local of newLocals) { + const initValue = backtraceLocalInitValue(local); + console.log("initValue: ", initValue); + if (initValue instanceof ArkNewExpr) { + const arkclass = ModelUtils.getArkClassInBuild(scene, initValue.getClassType()); + const fieldValueMap = parseObjectLiteral(arkclass, scene); + for (let pair of fieldValueMap) { + let field_name = pair[0].getName(); + let value = pair[1]; + //console.log("pair: ", pair); + if (field_name == url) { + console.log("field value: ", value); + } + } + } + } + return "1"; + } + getTargetClassromClassType(node, srcfile, arkclass, arg, api_name) { + let classSignature = arg.getClassSignature(); + let file_signature = classSignature.getDeclaringFileSignature(); + let file = this.scene.getFile(file_signature); + let clazz = file?.getClass(classSignature); + if (clazz) { + //console.log("clazz: ",clazz); + let url = clazz.getFieldWithName("uri"); + if (!url) + url = clazz.getFieldWithName("url"); + if (!url) + url = clazz.getFieldWithName("name"); + //console.log("jxx test url:",url); + //console.log("jxx api:",api_name); + let initstmt = url?.getInitializer(); + if (url) { + console.log("begin to parse arkfield"); + const fieldValueMap = parseObjectLiteral(clazz, srcfile.getScene()); + //console.log("fieldValueMap: ", fieldValueMap); + for (let pair of fieldValueMap) { + let field_name = pair[0].getName(); + let value = pair[1]; + //console.log("pair: ", pair); + if (field_name == "url") { + console.log("field value: ", value); + if (value instanceof StringConstant) { + return this.StringConstantFindTargetClass(srcfile, arkclass, value, api_name); + } + } + } + } + // if(initstmt && initstmt.length>1 && initstmt[0] instanceof ArkAssignStmt){ + // const rightop = initstmt[0].getRightOp(); + // console.log("jxxright: ",rightop); + // if(rightop instanceof ArkStaticFieldRef){ + // const fieldSignature = rightop.getFieldSignature(); + // const filedname = fieldSignature.getFieldName(); + // const declaringsig = fieldSignature.getDeclaringSignature(); + // if(declaringsig instanceof ClassSignature){ + // const FileSignature = declaringsig.getDeclaringFileSignature(); + // const file = this.scene.getFile(FileSignature); + // const arkclass = file?.getClass(declaringsig); + // if(arkclass){ + // console.log("jxx filename:",filedname); + // console.log("jxxarkclass: ",arkclass); + // const filed = arkclass.getStaticFieldWithName(filedname); + // console.log("jxxfiled:",filed); + // const initer = filed?.getInitializer(); + // if(initer){ + // for(const x of initer){ + // console.log("jxx x:",x.toString()); + // } + // } + // } + // } + // } + // for(const st of initstmt){ + // console.log("st: ",st.toString()); + // } + // } + // if(initstmt && initstmt.length>1){ + // //console.log("jxx initstmt:",ist.toString()); + // const rightop = (initstmt[1] as ArkAssignStmt).getRightOp(); + // if(rightop instanceof ArkArrayRef) + // { + // console.log("jxx: rightop:",rightop); + // const declearingStmt = rightop.getBase().getDeclaringStmt(); + // console.log("jxx:declear: ",declearingStmt); + // } + // } + // 还需要完善 + if (initstmt && initstmt.length > 0 && initstmt[0] instanceof ArkAssignStmt) { + let rightop_type = initstmt[0].getRightOp(); + if (rightop_type instanceof StringConstant) { + return this.StringConstantFindTargetClass(srcfile, arkclass, rightop_type, api_name); + } + else if (rightop_type instanceof ArkStaticFieldRef) { + // 拿到这个字段 + const type = rightop_type.getFieldSignature(); + const filedname = type.getFieldName(); + const it = type.getDeclaringSignature(); + const it2 = this.scene.getClass(it); + const mp = it2?.getStaticFieldWithName(filedname); + const inists = mp?.getInitializer(); + if (inists) { + if (inists[0] instanceof ArkAssignStmt) { + const rightop = inists[0].getRightOp(); + if (rightop instanceof StringConstant) { + return this.StringConstantFindTargetClass(srcfile, arkclass, rightop, api_name); + } + } + // for(const ist of inists){ + // console.log("jxx init:",ist); + // } + } + //console.log("jxx uses:",mp); + } + } + } + return null; + } + getTargetClassFromStringConstant(node, srcfile, arkclas, arg, api_name) { + let target_class = this.StringConstantFindTargetClass(srcfile, arkclas, arg, api_name); + console.log("debug api_name :", api_name); + if (target_class) ; + else { + console.log("target file is null"); + } + console.log("target_file:name: ", target_class?.getName()); + return target_class; + } + buildRouterMapFromNav() { + for (const arkfile of this.scene.getFiles()) { + for (const arkclass of arkfile.getClasses()) { + let viewTree = arkclass.getArkUIViewTree(); + if (viewTree) { + const walkTree = (node) => { + //console.log("node name:", node.name); + if (node.name == "Navigation") { + //console.log("have navDestination:"); + node.attributes.forEach((value, key) => { + if (key != "navDestination") + return; + const [stmt, details] = value; + details.forEach((detail, index) => { + if (detail instanceof ArkInstanceFieldRef) { + let it = detail.getFieldSignature(); + //console.log("field: ", it); + const func = it.getType(); + if (func instanceof FunctionType) { + const method_signature = func.getMethodSignature(); + const method = this.getMethod(method_signature); + //console.log("method: ", method?.getName()); + if (method) { + let cfg = method.getCfg(); + if (cfg) { + let func_node = this.getNodeByArkClass(arkclass); + if (func_node) { + this.buildRouterMapFromCfg(func_node, it, cfg); + } + // for (const st of cfg?.getStmts()) { + // console.log("st1: ", st.toString()); + // console.log("st2: ", st.getOriginalText()); + // } + } + } + } + } + else { + console.log("debugdetail: ", detail); + } + }); + }); + } + node.children.forEach(walkTree); + }; + const rootNode = viewTree.getRoot(); + if (rootNode) { + walkTree(rootNode); + } + } + } + } + } + buildRouterMapFromCfg(node, fieldSignature, cfg) { + let mp = node.router_map.get(fieldSignature); + if (!mp) { + mp = new Map(); + node.router_map.set(fieldSignature, mp); + } + let target_file_string; + let target_akrfile; + for (const stmt of cfg.getStmts()) { + // console.log("nav st: ",stmt.toString()); + // console.log("taget_file_string:",target_file_string); + // console.log("target_arkfile: ",target_akrfile?.getName()); + if (stmt instanceof ArkAssignStmt) { + let rightop = stmt.getRightOp(); + //console.log("right op:",rightop); + // if + if (rightop instanceof ArkConditionExpr && stmt.getOriginalText()?.includes("if") && target_file_string == undefined) { + const op2 = rightop.getOp2(); + //console.log("op2 :",op2); + if (op2 instanceof StringConstant) { + //console.log("debug value:",op2.getValue()); + target_file_string = op2.getValue(); + } + else if (op2 instanceof Local && (op2.getType() instanceof StringType || op2.getType() instanceof UnknownType)) { + target_file_string = this.getStringFormLocal(op2); + } + } + // view.create() + if (rightop instanceof ArkStaticInvokeExpr) { + let method_signature = rightop.getMethodSignature(); + let declaringClassSignature = method_signature.getDeclaringClassSignature(); + let method_subsignature = method_signature.getMethodSubSignature(); + if (declaringClassSignature.getClassName() == "View" && method_subsignature.getMethodName() == "create") { + const arg = rightop.getArg(0); + if (arg && arg instanceof Local) { + //console.log("DEC: ",arg.getDeclaringStmt()); + const declarstmt = arg.getDeclaringStmt(); + if (declarstmt instanceof ArkAssignStmt && declarstmt.getRightOp() instanceof ArkNewExpr && declarstmt.getRightOp().getClassType() instanceof ClassType) { + const classSignature = declarstmt.getRightOp().getType().getClassSignature(); + //console.log("DEBUGclassSignature : ",classSignature); + const declaringFileSignature = classSignature.getDeclaringFileSignature(); + target_akrfile = this.scene.getFile(declaringFileSignature); + //console.log("debugFile: ",target_akrfile?.getName()); + } + } + } + } + } + // console.log("name1: ",target_file_string); + // console.log("name2: ",target_akrfile?.getName()); + if (target_file_string && target_akrfile) { + console.log("DEBUG :", target_file_string + "----->", target_akrfile.getName()); + mp?.set(target_file_string, target_akrfile); + // console.log("map:",node.router_map.get(fieldSignature)); + target_file_string = undefined; + target_akrfile = undefined; + } + } + } + getStringFormLocal(local) { + if (local.getType() instanceof StringType || local.getType() instanceof UnknownType) { + const declaringstmt = local.getDeclaringStmt(); + console.log("declaringstmt: ", declaringstmt); + if (declaringstmt instanceof ArkAssignStmt) { + const rightop = declaringstmt.getRightOp(); + if (rightop instanceof ArkStaticFieldRef) { + //console.log("string from Local debug: ",this.getStringFromArkStaticFieldRef(rihtop)); + return this.getStringFromArkStaticFieldRef(rightop); + } + else if (rightop instanceof ArkInstanceFieldRef) { + return this.getStringFromArkInstanceFieldRef(rightop); + } + } + } + return null; + } + getStringFromArkInstanceFieldRef(rightop) { + const type = rightop.getFieldSignature(); + console.log("type: ", type); + return null; + } + getStringFromArkStaticFieldRef(rightop_type) { + const type = rightop_type.getFieldSignature(); + const filedname = type.getFieldName(); + const it = type.getDeclaringSignature(); + const it2 = this.scene.getClass(it); + const mp = it2?.getStaticFieldWithName(filedname); + const inists = mp?.getInitializer(); + if (inists) { + if (inists[0] instanceof ArkAssignStmt) { + const rightop = inists[0].getRightOp(); + if (rightop instanceof StringConstant) { + let target_url = rightop.getValue(); + return target_url; + } + } + // for(const ist of inists){ + // console.log("jxx init:",ist); + // } + } + return null; + } + doSolveBackEdge() { + for (const node of this.nodesItor()) { + console.log("jxxtest: ", node.getClass().getName() + " " + node.backtop_edges.length); + if (node.backtop_edges.length > 0) { + //let backedg = (node as UIFuncNode).backtop_edges[0]; + for (const backedg of node.backtop_edges) { + console.log("ready to solve back:", backedg.getViewTreeNode()?.xpath); + for (const inedge of node.getIncomingEdge()) { + const srcnode = inedge.getSrcNode(); + let edge = new UIFuncEdge(node, srcnode, backedg.getTransistionMethod(), backedg.getArkClass(), backedg.getArkMethod(), backedg.getAttribute(), backedg.getViewTreeNode(), backedg.getStmt(), "unkown"); + this.addEdge(edge); + } + } + } + } + } + MatchStaticAndDynamicComponentTrees(uifuncgraph) { + const layoutDir = path.resolve(__dirname, "./layout"); + const dynamicTrees = ArkUIViewTreeImpl.prototype.initArkUIViewTreeFromLayout.call(null, layoutDir); + const results = []; + const useMap = new Map(); + for (const arkfile of this.scene.getFiles()) { + const importFiles = this.scene.getImportFiles(arkfile); + useMap.set(arkfile, []); + for (const importFile of importFiles) { + if (importFile != arkfile) { + if (!useMap.has(importFile)) { + useMap.set(importFile, []); + } + useMap.get(importFile)?.push(arkfile); + } + } + } + console.log("layout size = ", dynamicTrees.length); + for (const dynamicTree of dynamicTrees) { + const root = dynamicTree.getRoot(); + if (!root) { + console.error("Dynamic tree root is null"); + results.push(dynamicTree); + continue; + } + if (root.dynamic_attributes && root.dynamic_attributes.hasOwnProperty("pagePath")) { + const staticTrees = this.findStaticTreeByDynamicTree(dynamicTree); + let bestMatchRatio = -1; + let bestHandlerXpath = []; + let bestMatchNodes = []; + let bestStaticTree = undefined; + let bestDynamicTree = undefined; + for (let staticTree of staticTrees || []) { + if(!staticTree) { + continue; + } + const staticRoot = staticTree.getRoot(); + TextMatching(staticTree, dynamicTree); + if (staticRoot) { + DFSMatching(staticTree, dynamicTree, staticRoot); + let not_matched = 0; + let xpath_sum = dynamicTree.nodeSet.size; + let handler_xpath = []; + let match_nodes = []; + for (const node of dynamicTree.nodeSet) { + let is_handler = false; + const clallback_methods = []; + const match_xpath = []; + if (!dynamicTree.nodes2possiblesnode.has(node.unique_xpath)) { + not_matched++; + } + else { + for (const possiblenode of dynamicTree.nodes2possiblesnode.get(node.unique_xpath)) { + for (let attribute of CALLBACK_METHOD_NAME) { + if (possiblenode.attributes.has(attribute)) { + clallback_methods.push(attribute); + is_handler = true; + } + } + match_xpath.push(possiblenode.unique_xpath); + } + match_nodes.push(node); + } + if (is_handler == true) { + node.dynamic_attributes["xpath"] = node.xpath; + node.dynamic_attributes["unique_xpath"] = node.unique_xpath; + const mathXpathJson = JSON.stringify(match_xpath); + const callbackJson = clallback_methods.toString(); + node.dynamic_attributes["call_back_method"] = callbackJson; + node.dynamic_attributes["static_node"] = mathXpathJson; + handler_xpath.push(node.dynamic_attributes); + } + } + const match_ratio = (xpath_sum - not_matched) / xpath_sum; + if (match_ratio > bestMatchRatio) { + bestMatchRatio = match_ratio; + bestHandlerXpath = JSON.parse(JSON.stringify(handler_xpath)); // 深拷贝 + bestMatchNodes = [...match_nodes]; // 浅拷贝节点数组 + bestStaticTree = staticTree; + bestDynamicTree = dynamicTree; // 记录当前 dynamicTree + } + // console.log(`匹配率: ${(match_ratio * 100).toFixed(2)}% (${xpath_sum - not_matched}/${xpath_sum})`); + // for (const click_xpath of handler_xpath) { + // console.log("please click: ", click_xpath); + // } + // let page_guide_json_printer = new PageGuidePrinter(this); + // let page_node = uifuncgraph.getNodeByArkClass(staticTree.getDeclaringArkClass()); + // if (page_node) { + // page_guide_json_printer.setPageID(page_node.getID()); + // console.log("set page: ", page_node.getClass().getSignature()); + // page_guide_json_printer.setDynamicTree(dynamicTree); + // page_guide_json_printer.setMatchedNodes(match_nodes); + // PrinterBuilder.dump( + // page_guide_json_printer, + // `out/${dynamicTree.getLayoutFileName()}_guide.json` + // ); + // } + dynamicTree.nodes2possiblesnode.clear(); + } + } + if (bestStaticTree && bestDynamicTree) { + console.log(`最佳匹配率: ${(bestMatchRatio * 100).toFixed(2)}%`); + for (const click_xpath of bestHandlerXpath) { + } + let page_guide_json_printer = new PageGuidePrinter(this); + let page_node = uifuncgraph.getNodeByArkClass(bestStaticTree.getDeclaringArkClass()); + if (root.dynamic_attributes.hasOwnProperty("pagePath") && root.dynamic_attributes.pagePath == "overlay") { + const info = bestStaticTree.getRoot()?.signature?.toString(); + page_node = uifuncgraph.getNodeByInfo(info || bestStaticTree.getRoot()?.name || ""); + } + if (page_node) { + TextMatching(bestStaticTree, dynamicTree); + DFSMatching(bestStaticTree, dynamicTree, bestStaticTree.getRoot()); + page_guide_json_printer.setPageID(page_node.getID()); + console.log("set page: ", page_node.getClass().getSignature()); + page_guide_json_printer.setStaticTree(bestStaticTree); + page_guide_json_printer.setDynamicTree(bestDynamicTree); // 用备份的 dynamicTree + page_guide_json_printer.setMatchedNodes(bestMatchNodes); + console.log("first time"); + //const outputString = page_guide_json_printer.dump(); + //console.log("输出的 JSON 字符串: ", outputString); + console.log("second time"); + const page_guided_path = path.resolve(__dirname, `out/${bestDynamicTree.getLayoutFileName()}_guided.json`); + PrinterBuilder.dump(page_guide_json_printer, page_guided_path); + console.log("输出完成: ", `out/${bestDynamicTree.getLayoutFileName()}_guided.json`); + dynamicTree.nodes2possiblesnode.clear(); + } + else { + console.log("page_node is null for ", bestStaticTree.getDeclaringArkClass()?.getName()); + } + } + } + results.push(dynamicTree); + } + return results; + } + findStaticTreeByDynamicTree(dynamicTree) { + const root = dynamicTree.getRoot(); + let results = []; + if (!root || !root.dynamic_attributes || !root.dynamic_attributes.pagePath) { + console.log("dynamic tree pagePath is null"); + return undefined; + } + const pagePath = root.dynamic_attributes.pagePath; + for (const arkclass of this.scene.getClasses()) { + if (arkclass.getSignature().getDeclaringFileSignature().getFileName().includes(pagePath) && + !arkclass.isAnonymousClass() && + !arkclass.isDefaultArkClass()) { + console.log("find static tree for dynamic tree:", pagePath); + results.push(arkclass.getArkUIViewTree()); + } + } + if (pagePath == "overlay") { + for (const tree of GlobalOverlayTree) { + const dummyMethod = new ArkMethod(); + const fakeTreeImpl = new ArkUIViewTreeImpl(dummyMethod); + fakeTreeImpl.setRoot(tree); + fakeTreeImpl.buildXpath(tree, tree, 0); + fakeTreeImpl.buildXpath2Nodes(tree, tree); + results.push(fakeTreeImpl); + } + console.log("result size for overlay:", results.length); + } + return results; + } + AnalyzeArkMethod_NEW(method, node, arkclass, attribute) { + let body = method?.getBody(); + const cfg = body?.getCfg(); + if (cfg) { + this.AnalyzerArkCfg_NEW(cfg, node, arkclass, method, attribute); + } + } + AnalyzerArkCfg_NEW(cfg, node, arkclass, arkmethod, attribute) { + for (const stmt of cfg.getStmts()) { + if (stmt instanceof ArkInvokeStmt) { // 方法调用语句--- api 最有可能在语句 + //console.log("jxx test stmt: ", stmt.toString()); + this.AnalyzeArkInvokeStmt_NEW(stmt, node, arkclass, arkmethod, attribute); + } + else if (stmt instanceof ArkAssignStmt) { + const invokeExpr = stmt.getInvokeExpr(); + if (invokeExpr) { + this.AnalyzeInvokeExpr_NEW(invokeExpr, node, arkclass, arkmethod, attribute, stmt); + } + } + } + } + AnalyzeArkInvokeStmt_NEW(stmt, node, arkclass, arkmethod, attribute) { + const invokeExpr = stmt.getInvokeExpr(); + this.AnalyzeInvokeExpr_NEW(invokeExpr, node, arkclass, arkmethod, attribute, stmt); // + } + AnalyzeInvokeExpr_NEW(invokeExpr, node, arkclass, arkmethod, attribute, stmt) { + // 分析本身 先处理 高频的 实例方法调用 + if (invokeExpr instanceof ArkStaticInvokeExpr || invokeExpr instanceof ArkInstanceInvokeExpr) { + const methodSignature = invokeExpr.getMethodSignature(); + let base_name = "unknown"; + let api_name = "unknown"; + if (invokeExpr instanceof ArkStaticInvokeExpr) { + base_name = methodSignature.getDeclaringClassSignature().getDeclaringNamespaceSignature()?.getNamespaceName() || ""; + api_name = methodSignature.getMethodSubSignature()?.getMethodName() || ""; + } + else { + let base = invokeExpr.getBase(); //调用方法的实例 + const methodSubSignature = invokeExpr.getMethodSignature()?.getMethodSubSignature(); + api_name = methodSubSignature?.getMethodName() ?? ""; + base_name = base.getName(); + } + if ((base_name == "router" || base_name == "Router") && (SystemRouterMethodsSet.has(api_name) || OhosRouterMethodsSet.has(api_name))) { + // this.solveSystemRouterMethod // 去找到起点,终点,找到一条边 + // this.solveOhosRouterMethod + let edge = this.getEdgeFromApiExpr(invokeExpr, node, arkclass, arkmethod, api_name, attribute, stmt, "Router"); + //console.log("find api :",api_name); + //console.log("xpath:",node?.xpath); + if (edge) { + // console.log("准备加边!:", (edge?.getSrcNode() as UIFuncNode).getFile().getName() + "----------->", (edge?.getDstNode() as UIFuncNode).getFile().getName()); + // console.log("node.xpath:", node?.xpath); + this.addEdge(edge); + } + if (node) { + let handler = new Handler(attribute || "", null, // @todo 把edge的dst改成ArkUIViewTreeNode + exports.UITransitionType.Router, undefined, 'Router API 调用', getNavigationType(api_name) // 假设使用 Push 跳转方式 + ); + node.handlers.push(handler); + } + console.log("找到了1: ", stmt.toString() + " method: " + arkmethod.getName()); + // console.log("node ",node); + } + if (OhosWindowMethodsSet.has(api_name)) { + console.log("找到了2: ", stmt.toString() + " method: " + arkmethod.getName()); + // this.solveOhosWindowMethod + // console.log("find api :",api_name); + // console.log("xpath:",node?.xpath); + let edge = this.getEdgeFromApiExpr(invokeExpr, node, arkclass, arkmethod, api_name, attribute, stmt, "UIAibility"); + if (edge) { + // console.log("准备加边!:", (edge?.getSrcNode() as UIFuncNode).getFile().getName() + "----------->", (edge?.getDstNode() as UIFuncNode).getFile().getName()); + // console.log("node.xpath:", node?.xpath); + this.addEdge(edge); + } + if (node) { + let handler = new Handler(attribute || "", null, // @todo 把edge的dst改成ArkUIViewTreeNode + exports.UITransitionType.UIAibility, undefined, 'UIAibility API 调用', exports.NavigationType.None // 假设使用 Push 跳转方式 + ); + node.handlers.push(handler); + } + // console.log("node: ",node?.name); + // console.log("node xpath: ",node?.xpath); + } + if (NavigationMethodsSet.has(api_name)) { + // this.solveNavigationMethod + console.log("找到了3: ", api_name); + let edge = this.getEdgeFromApiExpr(invokeExpr, node, arkclass, arkmethod, api_name, attribute, stmt, 'Navigation'); + if (edge) { + // console.log("准备加边!:", (edge?.getSrcNode() as UIFuncNode).getFile().getName() + "----------->", (edge?.getDstNode() as UIFuncNode).getFile().getName()); + // console.log("node.xpath:", node?.xpath); + this.addEdge(edge); + } + else { + console.log("边不存在"); + } + if (node) { + let handler = new Handler(attribute || "", null, // @todo 把edge的dst改成ArkUIViewTreeNode + exports.UITransitionType.Navigation, undefined, 'Navigation API 调用', getNavigationType(api_name) // 假设使用 Push 跳转方式 + ); + node.handlers.push(handler); + } + console.log("找到了3: ", stmt.toString() + " method: " + arkmethod.getName()); + // console.log("find api :", api_name); + // console.log("xpath:", node?.xpath); + // console.log("Handler: ", node?.handler); + // console.log("找到了: ",invokeExpr); + // console.log("node: ",node?.name); + // console.log("node xpath: ",node?.xpath); + } + } + } +} + +function cloneArkUIViewTreeNode(node) { + // 创建新节点,逐个拷贝字段 + const clonedNode = { + name: node.name, + stmts: new Map(node.stmts), // 浅拷贝 stmts + attributes: new Map(node.attributes), // 浅拷贝 attributes + stateValues: new Set(node.stateValues), // 浅拷贝 stateValues + parent: null, // parent 在递归过程中会被单独处理 + children: [], // 初始化空 children,稍后递归填充 + stateValuesTransfer: node.stateValuesTransfer ? new Map(node.stateValuesTransfer) : undefined, // 浅拷贝 stateValuesTransfer + xpath: node.xpath, // 直接拷贝 xpath + unique_xpath: node.unique_xpath, // 直接拷贝 unique_xpath + walk: node.walk, // 函数引用不需要深拷贝 + isBuilder: node.isBuilder, // 函数引用不需要深拷贝 + isCustomComponent: node.isCustomComponent, // 函数引用不需要深拷贝 + handlers: node.handlers ? node.handlers.map(h => new Handler(h.type, h.target, h.transitionType, h.method, h.info, h.navigationType)) : [], + }; + // 递归克隆子节点 + node.children.forEach(child => { + const clonedChild = cloneArkUIViewTreeNode(child); // 克隆子节点 + clonedChild.parent = clonedNode; // 设置父节点为当前克隆节点 + clonedNode.children.push(clonedChild); // 将克隆的子节点添加到 children 中 + }); + return clonedNode; +} +exports.UITransitionType = void 0; +(function (UITransitionType) { + UITransitionType["Navigation"] = "Navigation"; + UITransitionType["Router"] = "Router"; + UITransitionType["TabContent"] = "TabContent"; + UITransitionType["Dialog"] = "dialog"; + UITransitionType["Back"] = "back"; + UITransitionType["None"] = "none"; + UITransitionType["UIAibility"] = "UIAibility"; +})(exports.UITransitionType || (exports.UITransitionType = {})); +// 细分 Navigation 类型 +exports.NavigationType = void 0; +(function (NavigationType) { + NavigationType["Push"] = "push"; + NavigationType["Back"] = "back"; + NavigationType["Forward"] = "forward"; + NavigationType["None"] = "none"; +})(exports.NavigationType || (exports.NavigationType = {})); +class Handler { + /** 触发类型,例如 click、longPress 等 */ + type; + /** 跳转目标页面或组件名 */ + target; // target 可以是 ArkUIViewTreeNode 或 null + /** 触发事件所调用的方法签名(可选) */ + method; + /** 附加描述信息,比如参数说明、数据绑定等 */ + info; + /** 跳转类型:push、modal、replace 等 */ + transitionType; + /** 如果是 Navigation 类型,进一步定义具体跳转方式 */ + navigationType; + constructor(type, target = null, // 默认值为 null + transitionType, method, info, navigationType) { + this.type = type; + this.target = target; + this.transitionType = transitionType; + this.method = method; + this.info = info; + this.navigationType = navigationType; + } +} +function getNavigationType(apiName) { + // 处理 SystemRouterMethodsSet 中的方法 + if (SystemRouterMethodsSet.has(apiName)) { + switch (apiName) { + case 'push': + case 'replace': + return exports.NavigationType.Push; // 对应 Push 类型 + case 'back': + return exports.NavigationType.Back; // 对应 Back 类型 + case 'clear': + return exports.NavigationType.None; // 对应 None 类型,通常是清除操作 + default: + return exports.NavigationType.None; // 默认返回 None + } + } + // 处理 OhosRouterMethodsSet 中的方法 + if (OhosRouterMethodsSet.has(apiName)) { + switch (apiName) { + case 'pushUrl': + case 'pushNamedRoute': + return exports.NavigationType.Push; // 对应 Push 类型 + case 'replaceUrl': + case 'replaceNamedRoute': + return exports.NavigationType.Push; // 替换也是 Push 类型 + case 'back': + return exports.NavigationType.Back; // 对应 Back 类型 + case 'clear': + return exports.NavigationType.None; // 对应 None 类型 + default: + return exports.NavigationType.None; // 默认返回 None + } + } + // 根据不同的 api_name 返回对应的 NavigationType + switch (apiName) { + case 'pushPath': + case 'pushPathByName': + case 'pushDestination': + case 'pushDestinationByName': + return exports.NavigationType.Push; // 所有 push 相关的方法都返回 Push + case 'pop': + case 'popToName': + case 'popToIndex': + return exports.NavigationType.Back; // 所有 pop 相关的方法都返回 Back + case 'replacePath': + case 'replacePathByName': + case 'replaceDestination': + return exports.NavigationType.Push; // 替换相关的也返回 Push(可以根据具体需求调整) + case 'clear': + return exports.NavigationType.None; // 清除操作可能不涉及导航 + case 'removeByName': + case 'removeByIndexes': + case 'removeByNavDestinationId': + return exports.NavigationType.None; // 删除操作不涉及导航 + case 'moveToTop': + case 'moveIndexToTop': + return exports.NavigationType.None; // 移动操作不涉及导航 + default: + return exports.NavigationType.None; // 如果没有匹配的,返回默认值 + } +} + +const logger$k = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'ViewTreeBuilder'); +const COMPONENT_CREATE_FUNCTIONS = new Set([COMPONENT_CREATE_FUNCTION, COMPONENT_BRANCH_FUNCTION]); +const GlobalOverlayTree = []; +function backtraceLocalInitValue(value) { + let stmt = value.getDeclaringStmt(); + if (stmt instanceof ArkAssignStmt) { + let rightOp = stmt.getRightOp(); + if (rightOp instanceof Local) { + return backtraceLocalInitValue(rightOp); + } + else if (rightOp instanceof ArkInstanceFieldRef && rightOp.getBase().getName().startsWith(TEMP_LOCAL_PREFIX)) { + return backtraceLocalInitValue(rightOp.getBase()); + } + else if (rightOp instanceof ArkArrayRef) { + return backtraceLocalInitValue(rightOp.getBase()); + } + return rightOp; + } + return value; +} +function parseObjectLiteral(objectLiteralCls, scene) { + let map = new Map(); + if (objectLiteralCls?.getCategory() !== ClassCategory.OBJECT) { + return map; + } + objectLiteralCls?.getFields().forEach(field => { + let stmts = field.getInitializer(); + if (stmts.length === 0) { + return; + } + let assignStmt = stmts[stmts.length - 1]; + if (!(assignStmt instanceof ArkAssignStmt)) { + return; + } + let value = assignStmt.getRightOp(); + if (value instanceof Local) { + value = backtraceLocalInitValue(value); + } + map.set(field, value); + if (value instanceof ArkNewExpr) { + let subCls = ModelUtils.getArkClassInBuild(scene, value.getClassType()); + let childMap = parseObjectLiteral(subCls, scene); + if (childMap) { + map.set(field, childMap); + } + } + }); + return map; +} +// function parseObjectFromArkField(field: ArkField, scene: Scene, api_name: string): ArkUIViewTreeNodeImpl | null | undefined { +// const initStmts = field.getInitializer(); +// const newLocals: Local[] = []; +// // 提取所有 ArkNewExpr 左值 +// for (const stmt of initStmts) { +// if (stmt instanceof ArkAssignStmt) { +// let rightOp = stmt.getRightOp(); +// if (rightOp instanceof ArkNewExpr) { +// newLocals.push(stmt.getLeftOp() as Local); +// } else if (rightOp instanceof ArkInstanceFieldRef) { +// console.log("field: ", rightOp); +// if (field.getType() instanceof FunctionType) { +// const methodSignature = (field.getType() as FunctionType).getMethodSignature(); +// const method = scene.getMethod(methodSignature); +// if (method?.hasBuilderDecorator()) { +// const builder_node = this.analyzeBuilderNode +// } +// } +// } else { +// console.log("type : ", rightOp.getType().toString()); +// } +// } +// } +// for (const local of newLocals) { +// const initValue = backtraceLocalInitValue(local); +// if (initValue instanceof ArkNewExpr) { +// const arkClass = ModelUtils.getArkClassInBuild(scene, initValue.getClassType()); +// const fieldValueMap = parseObjectLiteral(arkClass, scene); // Map +// // console.log("fieldValueMap: ", fieldValueMap); +// let node; +// let action; +// for (let pair of fieldValueMap) { +// let field_name = pair[0].getName(); +// let value = pair[1]; +// if (field_name == "value") { +// // @todo(jxianxiao):根据 action 回调函数 来实现与 node 的绑定(UI迁移行为) +// // if(api_name == "bindPopup"){ +// // node = new ViewTreeNodeImpl("Button"); +// // }else{ +// // node = new ViewTreeNodeImpl("Text"); +// // } +// node = new ArkUIViewTreeNodeImpl("Text"); +// if (value instanceof StringConstant) { +// node.text_content = (value as StringConstant).getValue(); +// } +// } +// if (field_name == "action") { +// action = value +// } +// } +// let type = (action as Local).getType(); +// if (type instanceof FunctionType) { +// const method_signature = type.getMethodSignature(); +// let method = scene.getMethod(method_signature); +// if (method && method.getCfg()) { +// // @todo(jxianxiao):根据 action 回调函数 来实现与 node 的绑定(UI迁移行为) +// } +// } +// return node; +// } +// } +// return null; +// } +class StateValuesUtils { + declaringArkClass; + constructor(declaringArkClass) { + this.declaringArkClass = declaringArkClass; + } + static getInstance(declaringArkClass) { + return new StateValuesUtils(declaringArkClass); + } + parseStmtUsesStateValues(stmt, uses = new Set(), wholeMethod = false, visitor = new Set()) { + if (visitor.has(stmt)) { + return uses; + } + visitor.add(stmt); + let values = stmt.getUses(); + if (stmt instanceof ArkAssignStmt) { + values.push(stmt.getLeftOp()); + } + for (const v of values) { + this.parseValueUsesStateValues(v, uses, wholeMethod, visitor); + } + return uses; + } + objectLiteralMapUsedStateValues(uses, map) { + for (const [_, value] of map) { + if (value instanceof ArkInstanceFieldRef) { + let srcField = this.declaringArkClass.getFieldWithName(value.getFieldName()); + let decorators = srcField?.getStateDecorators(); + if (srcField && decorators && decorators.length > 0) { + uses.add(srcField); + } + } + else if (value instanceof Map) { + this.objectLiteralMapUsedStateValues(uses, value); + } + else if (value instanceof ArkNormalBinopExpr || value instanceof ArkConditionExpr) { + this.parseValueUsesStateValues(value.getOp1(), uses); + this.parseValueUsesStateValues(value.getOp2(), uses); + } + } + } + parseObjectUsedStateValues(type, uses = new Set()) { + if (!(type instanceof ClassType)) { + return uses; + } + let cls = ModelUtils.getArkClassInBuild(this.declaringArkClass.getDeclaringArkFile().getScene(), type); + let map = parseObjectLiteral(cls, this.declaringArkClass.getDeclaringArkFile().getScene()); + this.objectLiteralMapUsedStateValues(uses, map); + return uses; + } + parseMethodUsesStateValues(methodSignature, uses, visitor = new Set()) { + if (visitor.has(methodSignature)) { + return; + } + visitor.add(methodSignature); + let method = this.declaringArkClass.getDeclaringArkFile().getScene().getMethod(methodSignature); + if (!method) { + return; + } + let stmts = method.getCfg()?.getStmts(); + if (!stmts) { + return; + } + for (const stmt of stmts) { + this.parseStmtUsesStateValues(stmt, uses, true, visitor); + } + } + parseValueUsesStateValues(v, uses = new Set(), wholeMethod = false, visitor = new Set()) { + if (v instanceof ArkInstanceFieldRef) { + let field = this.declaringArkClass.getField(v.getFieldSignature()); + let decorators = field?.getStateDecorators(); + if (field && decorators && decorators.length > 0) { + uses.add(field); + } + } + else if (v instanceof ArkInstanceInvokeExpr) { + this.parseMethodUsesStateValues(v.getMethodSignature(), uses, visitor); + } + else if (v instanceof Local) { + if (v.getName() === 'this') { + return uses; + } + let type = v.getType(); + if (type instanceof FunctionType) { + this.parseMethodUsesStateValues(type.getMethodSignature(), uses, visitor); + return uses; + } + this.parseObjectUsedStateValues(type, uses); + let declaringStmt = v.getDeclaringStmt(); + if (!wholeMethod && declaringStmt) { + this.parseStmtUsesStateValues(declaringStmt, uses, wholeMethod, visitor); + } + } + return uses; + } +} +var ArkUIViewTreeNodeType; +(function (ArkUIViewTreeNodeType) { + ArkUIViewTreeNodeType[ArkUIViewTreeNodeType["SystemComponent"] = 0] = "SystemComponent"; + ArkUIViewTreeNodeType[ArkUIViewTreeNodeType["CustomComponent"] = 1] = "CustomComponent"; + ArkUIViewTreeNodeType[ArkUIViewTreeNodeType["Builder"] = 2] = "Builder"; + ArkUIViewTreeNodeType[ArkUIViewTreeNodeType["BuilderParam"] = 3] = "BuilderParam"; + ArkUIViewTreeNodeType[ArkUIViewTreeNodeType["Dialog"] = 4] = "Dialog"; + ArkUIViewTreeNodeType[ArkUIViewTreeNodeType["Menu"] = 5] = "Menu"; + ArkUIViewTreeNodeType[ArkUIViewTreeNodeType["Toast"] = 6] = "Toast"; + ArkUIViewTreeNodeType[ArkUIViewTreeNodeType["TabBar"] = 7] = "TabBar"; + ArkUIViewTreeNodeType[ArkUIViewTreeNodeType["PageMap"] = 8] = "PageMap"; + ArkUIViewTreeNodeType[ArkUIViewTreeNodeType["BindContent"] = 9] = "BindContent"; + ArkUIViewTreeNodeType[ArkUIViewTreeNodeType["BindSheet"] = 10] = "BindSheet"; + ArkUIViewTreeNodeType[ArkUIViewTreeNodeType["MenuWrapper"] = 11] = "MenuWrapper"; + ArkUIViewTreeNodeType[ArkUIViewTreeNodeType["Popup"] = 12] = "Popup"; + ArkUIViewTreeNodeType[ArkUIViewTreeNodeType["AlertDialog"] = 13] = "AlertDialog"; + ArkUIViewTreeNodeType[ArkUIViewTreeNodeType["ActionSheet"] = 14] = "ActionSheet"; + ArkUIViewTreeNodeType[ArkUIViewTreeNodeType["TipsDialog"] = 15] = "TipsDialog"; + ArkUIViewTreeNodeType[ArkUIViewTreeNodeType["SelectDialog"] = 16] = "SelectDialog"; + ArkUIViewTreeNodeType[ArkUIViewTreeNodeType["LoadingDialog"] = 17] = "LoadingDialog"; + ArkUIViewTreeNodeType[ArkUIViewTreeNodeType["CustomContentDialog"] = 18] = "CustomContentDialog"; + ArkUIViewTreeNodeType[ArkUIViewTreeNodeType["PopoverDialog"] = 19] = "PopoverDialog"; + ArkUIViewTreeNodeType[ArkUIViewTreeNodeType["ConfirmDialog"] = 20] = "ConfirmDialog"; +})(ArkUIViewTreeNodeType || (ArkUIViewTreeNodeType = {})); +class ArkUIViewTreeNodeImpl { + name; + stmts; + attributes; + stateValues; + parent; + children; + classSignature; + signature; + stateValuesTransfer; + builderParam; + builder; + text_content; + type; + xpath; + unique_xpath; + handlers; + dynamic_attributes; // 新增字段,用于存储动态属性 + nearest_type_node_map_upward = new Map(); // 新增字段,用于存储向上查找的最近类型节点 + nearest_type_node_map_downward = new Map(); // 新增字段,用于存储向下查找的最近类型节点 + constructor(name) { + this.name = name; + this.stmts = new Map(); + this.attributes = new Map(); + this.stateValues = new Set(); + this.parent = null; + this.children = []; + this.xpath = ''; + this.unique_xpath = ''; + this.handlers = []; + this.text_content = ""; + this.type = ArkUIViewTreeNodeType.SystemComponent; + this.dynamic_attributes = {}; + } + setDynamicAttributes(attributes) { + this.dynamic_attributes = attributes; + } + isBuilder() { + return this.type === ArkUIViewTreeNodeType.Builder; + } + isBuilderParam() { + return this.type === ArkUIViewTreeNodeType.BuilderParam; + } + isCustomComponent() { + return this.type === ArkUIViewTreeNodeType.CustomComponent; + } + walk(selector, visitor = new Set()) { + if (visitor.has(this)) { + return false; + } + let ret = selector(this); + visitor.add(this); + for (const child of this.children) { + ret = ret || child.walk(selector, visitor); + if (ret) { + break; + } + } + return ret; + } + parseAttributes(stmt) { + let expr; + if (stmt instanceof ArkAssignStmt) { + let op = stmt.getRightOp(); + if (op instanceof ArkInstanceInvokeExpr) { + expr = op; + } + else if (op instanceof ArkStaticInvokeExpr) { + expr = op; + } + } + else if (stmt instanceof ArkInvokeStmt) { + let invoke = stmt.getInvokeExpr(); + if (invoke instanceof ArkInstanceInvokeExpr) { + expr = invoke; + } + else if (invoke instanceof ArkStaticInvokeExpr) { + expr = invoke; + } + } + if (expr) { + let key = expr.getMethodSignature().getMethodSubSignature().getMethodName(); + let relationValues = []; + for (const arg of expr.getArgs()) { + if (arg instanceof Local) { + this.getBindValues(arg, relationValues); + } + else if (arg instanceof Constant) { + relationValues.push(arg); + } + } + this.attributes.set(key, [stmt, relationValues]); + } + } + addStmt(tree, stmt) { + this.parseAttributes(stmt); + if (this.name !== COMPONENT_FOR_EACH && this.name !== COMPONENT_LAZY_FOR_EACH) { + this.parseStateValues(tree, stmt); + } + } + getBindValues(local, relationValues, visitor = new Set()) { + if (visitor.has(local)) { + return; + } + visitor.add(local); + const stmt = local.getDeclaringStmt(); + if (!stmt) { + let type = local.getType(); + if (type instanceof FunctionType) { + relationValues.push(type.getMethodSignature()); + } + return; + } + for (const v of stmt.getUses()) { + if (v instanceof Constant) { + relationValues.push(v); + } + else if (v instanceof ArkInstanceFieldRef) { + relationValues.push(v); + } + else if (v instanceof Local) { + this.getBindValues(v, relationValues, visitor); + } + } + } + parseStateValues(tree, stmt) { + let stateValues = StateValuesUtils.getInstance(tree.getDeclaringArkClass()).parseStmtUsesStateValues(stmt); + stateValues.forEach(field => { + this.stateValues.add(field); + tree.addStateValue(field, this); + }, this); + } + static createCustomComponent() { + let instance = new ArkUIViewTreeNodeImpl(COMPONENT_CUSTOMVIEW); + instance.type = ArkUIViewTreeNodeType.CustomComponent; + return instance; + } + static createTabBarNode() { + let instance = new ArkUIViewTreeNodeImpl(COMPONENT_TABBAR); + instance.type = ArkUIViewTreeNodeType.TabBar; + return instance; + } + static createBindContentNode() { + let instance = new ArkUIViewTreeNodeImpl(COMPONENT_BINDCONTENT); + instance.type = ArkUIViewTreeNodeType.BindContent; + return instance; + } + static createAlertDialogNode() { + let instance = new ArkUIViewTreeNodeImpl("AlertDialog"); + instance.type = ArkUIViewTreeNodeType.AlertDialog; + return instance; + } + static createTipsDialogNode() { + let instance = new ArkUIViewTreeNodeImpl("TipsDialog"); + instance.type = ArkUIViewTreeNodeType.TipsDialog; + return instance; + } + static createConfirmDialogNode() { + let instance = new ArkUIViewTreeNodeImpl("ConfirmDialog"); + instance.type = ArkUIViewTreeNodeType.ConfirmDialog; + return instance; + } + static createSelectDialogNode() { + let instance = new ArkUIViewTreeNodeImpl("SelectDialog"); + instance.type = ArkUIViewTreeNodeType.SelectDialog; + return instance; + } + static createLodaingDialogNode() { + let instance = new ArkUIViewTreeNodeImpl("LoadingDialog"); + instance.type = ArkUIViewTreeNodeType.LoadingDialog; + return instance; + } + static createCustomContentDialogNode() { + let instance = new ArkUIViewTreeNodeImpl("CustomContentDialog"); + instance.type = ArkUIViewTreeNodeType.CustomContentDialog; + return instance; + } + static createToastgNode() { + let instance = new ArkUIViewTreeNodeImpl("Toast"); + instance.type = ArkUIViewTreeNodeType.Toast; + return instance; + } + static createPopoverDialogNode() { + let instance = new ArkUIViewTreeNodeImpl("PopoverDialog"); + instance.type = ArkUIViewTreeNodeType.PopoverDialog; + return instance; + } + static createActionSheetNode() { + let instance = new ArkUIViewTreeNodeImpl("ActionSheet"); + instance.type = ArkUIViewTreeNodeType.ActionSheet; + return instance; + } + static createBindSheetNode() { + let instance = new ArkUIViewTreeNodeImpl(COMPONENT_BINDSHEET); + instance.type = ArkUIViewTreeNodeType.BindSheet; + return instance; + } + static createMenuWrapperNode() { + let instance = new ArkUIViewTreeNodeImpl(COMPONENT_MENUWRAPPER); + instance.type = ArkUIViewTreeNodeType.MenuWrapper; + return instance; + } + static createPageMapNode() { + let instance = new ArkUIViewTreeNodeImpl("PageMap"); + instance.type = ArkUIViewTreeNodeType.PageMap; + return instance; + } + static createBuilderNode() { + let instance = new ArkUIViewTreeNodeImpl(BUILDER_DECORATOR); + instance.type = ArkUIViewTreeNodeType.Builder; + return instance; + } + static createBuilderParamNode() { + let instance = new ArkUIViewTreeNodeImpl(BUILDER_PARAM_DECORATOR); + instance.type = ArkUIViewTreeNodeType.BuilderParam; + return instance; + } + static createDialogNode() { + let instance = new ArkUIViewTreeNodeImpl(COMPONENT_DIALOG); + instance.type = ArkUIViewTreeNodeType.Dialog; + return instance; + } + static createPopupNode() { + let instance = new ArkUIViewTreeNodeImpl(COMPONENT_POPUP); + instance.type = ArkUIViewTreeNodeType.Popup; + return instance; + } + static createMenuNode() { + let instance = new ArkUIViewTreeNodeImpl(COMPONENT_MENU); + instance.type = ArkUIViewTreeNodeType.Menu; + return instance; + } + static createToastNode() { + let instance = new ArkUIViewTreeNodeImpl(COMPONENT_TOAST); + instance.type = ArkUIViewTreeNodeType.Toast; + return instance; + } + changeBuilderParam2BuilderNode(builder) { + this.name = BUILDER_DECORATOR; + this.type = ArkUIViewTreeNodeType.Builder; + this.signature = builder.getSignature(); + this.classSignature = this.signature; + const root = builder.getArkUIViewTree()?.getRoot(); + if (root) { + for (let child of root.children) { + this.children.push(child); + } + } + else { + console.log(`ViewTree->changeBuilderParam2BuilderNode ${builder.getSignature().toString()} @Builder viewtree fail.`); + } + } + hasBuilderParam() { + return this.walk(item => { + return item.isBuilderParam(); + }); + } + clone(parent, map = new Map()) { + let newNode = new ArkUIViewTreeNodeImpl(this.name); + newNode.attributes = this.attributes; + newNode.stmts = newNode.attributes; + newNode.stateValues = this.stateValues; + newNode.parent = parent; + newNode.type = this.type; + newNode.signature = this.signature; + newNode.classSignature = newNode.signature; + newNode.builderParam = this.builderParam; + newNode.builder = this.builder; + map.set(this, newNode); + for (const child of this.children) { + if (map.has(child)) { + newNode.children.push(map.get(child)); + } + else { + newNode.children.push(child.clone(newNode, map)); + } + } + return newNode; + } +} +class ArkUITreeNodeStack { + root = null; + stack; + virtualRoot = null; + constructor() { + this.stack = []; + } + /** + * @internal + */ + push(node) { + let parent = this.getParent(); + node.parent = parent; + this.stack.push(node); + if (parent === null || parent === undefined) { + this.root = node; + } + else { + parent.children.push(node); + } + } + /** + * @internal + */ + pop() { + this.stack.pop(); + } + /** + * @internal + */ + top() { + return this.isEmpty() ? null : this.stack[this.stack.length - 1]; + } + /** + * @internal + */ + isEmpty() { + return this.stack.length === 0; + } + /** + * @internal + */ + popAutomicComponent(name) { + if (this.isEmpty()) { + return; + } + let node = this.stack[this.stack.length - 1]; + if (name !== node.name && !this.isContainer(node.name)) { + this.stack.pop(); + } + } + getBottom() { + return this.isEmpty() ? null : this.stack[0]; + } + getParent() { + if (this.stack.length === 0) { + return null; + } + let node = this.stack[this.stack.length - 1]; + //@todo(jxianxiao):为什么需要这一步? + if (!this.isContainer(node.name)) { + this.stack.pop(); + } + return this.stack[this.stack.length - 1]; + } + //@todo(jxianxiao):这是干什么用的? + isContainer(name) { + return isEtsContainerComponent(name) || SPECIAL_CONTAINER_COMPONENT.has(name) || name === BUILDER_DECORATOR; + } + /** + * @internal + */ + popComponentExpect(name) { + for (let i = this.stack.length - 1; i >= 0; i--) { + if (this.stack[i].name !== name) { + this.stack.pop(); + } + else { + break; + } + } + return this; + } + findNearestNodeByName(name) { + for (let i = this.stack.length - 1; i >= 0; i--) { + const node = this.stack[i]; + if (node.name === name) { + return node; + } + } + return undefined; + } + printStack() { + console.log("ArkUITreeNodeStack 当前栈内容(底 → 顶):"); + this.stack.forEach((node, index) => { + console.log(`[${index}] name: ${node.name}`); + }); + } +} +class ArkUIViewTreeImpl extends ArkUITreeNodeStack { + render; + buildViewStatus; + //@todo(jxianxiao):为什么是这个key-value + stateValues; + fieldTypes; + layout_file_name; + unique_xpath_idx = 0; + xpath2nodes; + xpathVector; + child2parent; + nodeSet; + nodes2possiblesnode; + constructor(render) { + super(); + this.render = render; + this.buildViewStatus = false; + this.stateValues = new Map(); + this.fieldTypes = new Map(); + this.xpath2nodes = new Map(); + this.xpathVector = []; + this.child2parent = new Map(); + this.nodeSet = new Set(); + this.nodes2possiblesnode = new Map(); + this.layout_file_name = "unkown"; + } + /** + * ViewTree root node. + * @returns root node + */ + getRoot() { + this.buildViewTree(); + return this.root; + } + setRoot(root) { + this.root = root; + } + setLayoutFileName(name) { + this.layout_file_name = name.replace(/[\/\\:\*\?"<>\|]/g, "_"); + } + getLayoutFileName() { + return this.layout_file_name; + } + getStateValues() { + this.buildViewTree(); + return this.stateValues; + } + /** + * @deprecated Use {@link getStateValues} instead. + */ + isClassField(name) { + return this.fieldTypes.has(name); + } + /** + * @deprecated Use {@link getStateValues} instead. + */ + getClassFieldType(name) { + return this.fieldTypes.get(name); + } + /** + * @internal + */ + buildViewTree() { + if (!this.render || this.isInitialized()) { + return; + } + // console.log("Building view tree: ", this.render.getDeclaringArkClass().getName()); + const rootNode = new ArkUIViewTreeNodeImpl("root"); + this.virtualRoot = rootNode; + this.buildViewStatus = true; + this.loadClasssFieldTypes(); + if (this.render.hasBuilderDecorator()) { + // console.log("has builder decorator"); + let node = ArkUIViewTreeNodeImpl.createBuilderNode(); + node.signature = this.render.getSignature(); + node.classSignature = node.signature; + this.push(node); + } + if (this.render.getCfg()) { + // console.log("has cfg"); + this.buildViewTreeFromCfg(this.render.getCfg()); + } + if (this.root) { + const oldRoot = this.root; + rootNode.children.push(oldRoot); + oldRoot.parent = rootNode; + this.root = rootNode; + } + else { + console.log("root is null"); + } + { + this.analyzeDialogFromArkClass(this.render.getDeclaringArkClass(), this.render.getDeclaringArkFile().getScene()); + } + if (this.root) { + this.root.unique_xpath = this.render.getSignature().toString(); + this.buildXpath(this.root, this.root, 0); + this.buildXpath2Nodes(this.root, this.root); + } + } + /** + * @internal + */ + isInitialized() { + return this.root != null || this.buildViewStatus; + } + /** + * @internal + */ + addStateValue(field, node) { + if (!this.stateValues.has(field)) { + this.stateValues.set(field, new Set()); + } + let sets = this.stateValues.get(field); + sets?.add(node); + } + /** + * @internal + */ + isCreateFunc(name) { + return COMPONENT_CREATE_FUNCTIONS.has(name); + } + loadClasssFieldTypes() { + for (const field of this.render.getDeclaringArkClass().getFields()) { + let decorators = field.getStateDecorators(); + if (decorators.length > 0) { + if (decorators.length === 1) { + this.fieldTypes.set(field.getName(), decorators[0]); + } + else { + this.fieldTypes.set(field.getName(), decorators[0]); + } + } + else { + this.fieldTypes.set(field.getName(), field.getSignature().getType()); + } + } + } + /** + * @internal + */ + getDeclaringArkClass() { + return this.render.getDeclaringArkClass(); + } + /** + * @internal + */ + findMethod(methodSignature) { + let method = this.render.getDeclaringArkFile().getScene().getMethod(methodSignature, true); + if (method) { + return method; + } + // class + method = this.getDeclaringArkClass().getMethod(methodSignature); + if (method) { + return method; + } + return this.findMethodWithName(methodSignature.getMethodSubSignature().getMethodName()); + } + /** + * @internal + */ + findMethodWithName(name) { + let method = this.getDeclaringArkClass().getMethodWithName(name); + if (method) { + return method; + } + // namespace + this.getDeclaringArkClass() + .getDeclaringArkNamespace() + ?.getAllMethodsUnderThisNamespace() + .forEach(value => { + if (value.getName() === name) { + method = value; + } + }); + if (method) { + return method; + } + this.getDeclaringArkClass() + .getDeclaringArkFile() + .getAllNamespacesUnderThisFile() + .forEach(namespace => { + namespace.getAllMethodsUnderThisNamespace().forEach(value => { + if (value.getName() === name) { + method = value; + } + }); + }); + return method; + } + /** + * @internal + */ + findClass(classSignature) { + return ModelUtils.getClass(this.render, classSignature); + } + findBuilderMethod(value) { + let method; + if (value instanceof ArkInstanceFieldRef) { + method = this.findMethodWithName(value.getFieldName()); + } + else if (value instanceof ArkStaticInvokeExpr) { + method = this.findMethod(value.getMethodSignature()); + } + else if (value instanceof Local && value.getType() instanceof FunctionType) { + method = this.findMethod(value.getType().getMethodSignature()); + } + else if (value instanceof Local) { + method = this.findMethodWithName(value.getName()); + } + if (method && !method.hasBuilderDecorator()) { + method = this.findMethodInvokeBuilderMethod(method); + } + return method; + } + /** + * @internal + */ + addBuilderNode(method) { + let builderViewTree = method.getArkUIViewTree(); + if (!builderViewTree || !builderViewTree.getRoot()) { + console.log(`ViewTree->addBuilderNode ${method.getSignature().toString()} build viewtree fail.`); + // add empty node + let node = ArkUIViewTreeNodeImpl.createBuilderNode(); + node.signature = method.getSignature(); + node.classSignature = node.signature; + this.push(node); + this.pop(); + return node; + } + let root = builderViewTree.getRoot(); + this.push(root); + if (method.getDeclaringArkClass() === this.render.getDeclaringArkClass()) { + for (const [field, nodes] of builderViewTree.getStateValues()) { + for (const node of nodes) { + this.addStateValue(field, node); + } + } + } + this.pop(); + return root; + } + /** + * @internal + */ + addCustomComponentNode(cls, arg, builder, flag = "push") { + let node = ArkUIViewTreeNodeImpl.createCustomComponent(); + node.signature = cls.getSignature(); + node.classSignature = node.signature; + node.stateValuesTransfer = this.parseObjectLiteralExpr(cls, arg, builder); + if (arg instanceof Local && arg.getType()) { + let stateValues = StateValuesUtils.getInstance(this.getDeclaringArkClass()).parseObjectUsedStateValues(arg.getType()); + stateValues.forEach(field => { + node.stateValues.add(field); + this.addStateValue(field, node); + }); + } + // jxx + if (flag == "push") { + this.push(node); + } + //this.printStack(); + let componentViewTree = cls.getArkUIViewTree(); + if (!componentViewTree || !componentViewTree.getRoot()) { + console.log(`ViewTree->addCustomComponentNode ${cls.getSignature().toString()} build viewtree fail.`); + return node; + } + let root = componentViewTree.getRoot(); + //console.log("View Tree: ", componentViewTree); + if (root.hasBuilderParam()) { + root = this.cloneBuilderParamNode(node, root); + } + node.children.push(root); + return node; + } + cloneBuilderParamNode(node, root) { + root = root.clone(node); + if (node.stateValuesTransfer) { + root.walk(item => { + let child = item; + if (!child.isBuilderParam() || !child.builderParam) { + return false; + } + let method = node.stateValuesTransfer?.get(child.builderParam); + if (method) { + child.changeBuilderParam2BuilderNode(method); + } + return false; + }); + } + return root; + } + /** + * @internal + */ + addBuilderParamNode(field) { + let node = ArkUIViewTreeNodeImpl.createBuilderParamNode(); + node.builderParam = field; + this.push(node); + this.pop(); + return node; + } + /** + * @internal + */ + addSystemComponentNode(name) { + let node = new ArkUIViewTreeNodeImpl(name); + this.push(node); + return node; + } + //@note(jxianxiao): 为什么只返回一个? + findMethodInvokeBuilderMethod(method) { + let stmts = method.getCfg()?.getStmts(); + if (!stmts) { + return undefined; + } + for (const stmt of stmts) { + let expr; + if (stmt instanceof ArkInvokeStmt) { + expr = stmt.getInvokeExpr(); + } + else if (stmt instanceof ArkAssignStmt) { + let rightOp = stmt.getRightOp(); + if (rightOp instanceof ArkInstanceInvokeExpr || rightOp instanceof ArkStaticInvokeExpr) { + expr = rightOp; + } + } + if (expr === undefined) { + continue; + } + let method = this.findMethod(expr.getMethodSignature()); + if (method?.hasBuilderDecorator()) { + return method; + } + } + return undefined; + } + parseFieldInObjectLiteral(field, cls, transferMap) { + let dstField = cls.getFieldWithName(field.getName()); + if (dstField?.getStateDecorators().length === 0 && !dstField?.hasBuilderParamDecorator()) { + return; + } + let stmts = field.getInitializer(); + if (stmts.length === 0) { + return; + } + let assignStmt = stmts[stmts.length - 1]; + if (!(assignStmt instanceof ArkAssignStmt)) { + return; + } + let value = assignStmt.getRightOp(); + if (value instanceof Local) { + value = backtraceLocalInitValue(value); + } + if (dstField?.hasBuilderParamDecorator()) { + let method = this.findBuilderMethod(value); + if (method) { + transferMap.set(dstField, method); + } + } + else { + let srcField; + if (value instanceof ArkInstanceFieldRef) { + srcField = this.getDeclaringArkClass().getFieldWithName(value.getFieldName()); + } + if (srcField && dstField) { + transferMap.set(dstField, srcField); + } + } + } + parseObjectLiteralExpr(cls, object, builder) { + let transferMap = new Map(); + if (object instanceof Local && object.getType() instanceof ClassType) { + let anonymousSig = object.getType().getClassSignature(); + let anonymous = this.findClass(anonymousSig); + anonymous?.getFields().forEach(field => { + this.parseFieldInObjectLiteral(field, cls, transferMap); + }); + } + // If the builder exists, there will be a unique BuilderParam + if (builder) { + cls.getFields().forEach(value => { + if (value.hasBuilderParamDecorator()) { + transferMap.set(value, builder); + } + }); + } + if (transferMap.size === 0) { + return undefined; + } + return transferMap; + } + viewComponentCreationParser(name, stmt, expr, flag = "push") { + let temp = expr.getArg(0); + let arg; + temp.getUsedStmts().forEach(value => { + if (value instanceof ArkAssignStmt && value.getRightOp() instanceof ArkInstanceInvokeExpr) { + const rightOp = value.getRightOp(); + const methodName = rightOp.getMethodSignature().getMethodSubSignature().getMethodName(); + if (methodName === 'constructor') { + arg = rightOp.getArg(0); + } + } + }); + let builderMethod; + let builder = expr.getArg(1); + if (builder) { + let method = this.findMethod(builder.getType().getMethodSignature()); + if (!method?.hasBuilderDecorator()) { + method?.addDecorator(new Decorator(BUILDER_DECORATOR)); + } + if (!method?.hasViewTree()) { + method?.setViewTree(new ArkUIViewTreeImpl(method)); + } + if (method) { + builderMethod = method; + } + } + let initValue = backtraceLocalInitValue(temp); + if (!(initValue instanceof ArkNewExpr)) { + return undefined; + } + const initValueType = initValue.getType(); + if (!(initValueType instanceof ClassType)) { + return undefined; + } + let clsSignature = initValueType.getClassSignature(); + // console.log("view debug clsSignature: " + clsSignature); + if (clsSignature) { + let cls = this.findClass(clsSignature); + if (cls && cls.hasComponentDecorator()) { + return this.addCustomComponentNode(cls, arg, builderMethod, flag); + } + else { + logger$k.error(`ViewTree->viewComponentCreationParser not found class ${clsSignature.toString()}. ${stmt.toString()}`); + } + } + return undefined; + } + waterFlowCreationParser(name, stmt, expr) { + let node = this.addSystemComponentNode(name); + let object = expr.getArg(0); + if (object instanceof Local && object.getType() instanceof ClassType) { + let anonymousSig = object.getType().getClassSignature(); + let anonymous = this.findClass(anonymousSig); + let footer = anonymous?.getFieldWithName('footer'); + if (!footer) { + return node; + } + let stmts = footer.getInitializer(); + let assignStmt = stmts[stmts.length - 1]; + if (!(assignStmt instanceof ArkAssignStmt)) { + return node; + } + let value = assignStmt.getRightOp(); + let method = this.findBuilderMethod(value); + if (method?.hasBuilderDecorator()) { + return this.addBuilderNode(method); + } + } + return node; + } + forEachCreationParser(name, stmt, expr) { + let node = this.addSystemComponentNode(name); + let values = expr.getArg(0); + let declaringStmt = values?.getDeclaringStmt(); + if (declaringStmt) { + let stateValues = StateValuesUtils.getInstance(this.getDeclaringArkClass()).parseStmtUsesStateValues(declaringStmt); + stateValues.forEach(field => { + node.stateValues.add(field); + this.addStateValue(field, node); + }); + } + let type = expr.getArg(1).getType(); + let method = this.findMethod(type.getMethodSignature()); + if (method && method.getCfg()) { + this.buildViewTreeFromCfg(method.getCfg()); + } + return node; + } + repeatCreationParser(name, stmt, expr) { + let node = this.addSystemComponentNode(name); + let arg = expr.getArg(0); + let declaringStmt = arg?.getDeclaringStmt(); + if (declaringStmt) { + let stateValues = StateValuesUtils.getInstance(this.getDeclaringArkClass()).parseStmtUsesStateValues(declaringStmt); + stateValues.forEach(field => { + node.stateValues.add(field); + this.addStateValue(field, node); + }); + } + return node; + } + ifBranchCreationParser(name, stmt, expr) { + this.popComponentExpect(COMPONENT_IF); + return this.addSystemComponentNode(COMPONENT_IF_BRANCH); + } + COMPONENT_CREATE_PARSERS = new Map([ + ['ForEach.create', this.forEachCreationParser.bind(this)], + ['LazyForEach.create', this.forEachCreationParser.bind(this)], + ['Repeat.create', this.repeatCreationParser.bind(this)], + ['View.create', this.viewComponentCreationParser.bind(this)], + ['If.branch', this.ifBranchCreationParser.bind(this)], + ['WaterFlow.create', this.waterFlowCreationParser.bind(this)], + ]); + INSTANCE_INVOKE_PARSERS = new Map([ + ['tabBar', this.tabBarComponentParser.bind(this)], + ['navDestination', this.navDestinationComponentParser.bind(this)], + ['bindContentCover', this.bindContentCoverComponentParser.bind(this)], + ['bindSheet', this.bindSheetComponentParser.bind(this)], + ['bindContextMenu', this.bindContextMenuComponentParser.bind(this)], + ['bindMenu', this.bindMenuComponentParser.bind(this)], + ['bindPopup', this.bindPopupComponentParser.bind(this)], + ]); + // + DIALOG_SHOW_PARSERS = new Map([ + ['showAlertDialog', this.AlertDialogShowParser.bind(this)], + ['showActionSheet', this.ActionSheetShowParser.bind(this)], + ['CalendarPickerDialog', this.CalendarPickerDialogShowParser.bind(this)], + ['showDatePickerDialog', this.DatePickerDialogShowParser.bind(this)], + ['showTimePickerDialog', this.TimePickerDialogShowParser.bind(this)], + ['showTextPickerDialog', this.TextPickerDialogShowParser.bind(this)], + ['showToast', this.ToastShowParser.bind(this)], + ['showDialog,', this.DialogShowParser.bind(this)], + ['showActionMenu', this.ActionMenuShowParser.bind(this)], + ]); + componentCreateParse(componentName, methodName, stmt, expr, flag = "push") { + let parserFn = this.COMPONENT_CREATE_PARSERS.get(`${componentName}.${methodName}`); + if (parserFn) { + let node = parserFn(componentName, stmt, expr, flag); + node?.addStmt(this, stmt); + return node; + } + this.popAutomicComponent(componentName); + let node = this.addSystemComponentNode(componentName); + node.addStmt(this, stmt); + if (componentName == "MenuItem") { + const args = expr.getArgs(); + for (let arg of args) { + //console.log("MenuItem arg", arg); + const type = arg.getType(); + if (type instanceof ClassType) { + if (this.findClass(type.getClassSignature())?.getFieldWithName("builder")) { + //console.log("Menu item builder: ", this.findClass(type.getClassSignature())?.getFieldWithName("builder")); + const node = this.handleBindMenuItemClass(new Map(), arg); + //this.printStack(); + const Menu_node = this.getBottom(); + //console.log("Buttom: ",Menu_node); + if (node && Menu_node) { + Menu_node?.children.push(node); + } + } + } + } + } + return node; + } + parseStaticInvokeExpr(local2Node, stmt, expr, flag = "push") { + let methodSignature = expr.getMethodSignature(); + let method = this.findMethod(methodSignature); + if (method?.hasBuilderDecorator()) { + let node = this.addBuilderNode(method); + node.parseStateValues(this, stmt); + return node; + } + let name = methodSignature.getDeclaringClassSignature().getClassName(); + let methodName = methodSignature.getMethodSubSignature().getMethodName(); + if (this.isCreateFunc(methodName)) { + return this.componentCreateParse(name, methodName, stmt, expr, flag); + } + let currentNode = this.top(); + if (name === currentNode?.name) { + currentNode.addStmt(this, stmt); + if (methodName === COMPONENT_POP_FUNCTION) { + this.pop(); + } + return currentNode; + } + else if (name === COMPONENT_IF && methodName === COMPONENT_POP_FUNCTION) { + this.popComponentExpect(COMPONENT_IF); + this.pop(); + } + return undefined; + } + /** + * $temp4.margin({ top: 20 }); + * @param viewTree + * @param local2Node + * @param expr + */ + parseInstanceInvokeExpr(local2Node, stmt, expr) { + let temp = expr.getBase(); + if (local2Node.has(temp)) { + let component = local2Node.get(temp); + if (component?.name === COMPONENT_REPEAT && expr.getMethodSignature().getMethodSubSignature().getMethodName() === 'each') { + let arg = expr.getArg(0); + let type = arg.getType(); + if (type instanceof FunctionType) { + let method = this.findMethod(type.getMethodSignature()); + this.buildViewTreeFromCfg(method?.getCfg()); + } + this.pop(); + } + else { + component?.addStmt(this, stmt); + } + let methodName = expr.getMethodSignature().getMethodSubSignature().getMethodName(); + //console.log("methodName: ", methodName); + { + let parserFn = this.INSTANCE_INVOKE_PARSERS.get(methodName); + if (parserFn) { + parserFn(local2Node, stmt, expr); + } + } + return component; + } + let name = expr.getBase().getName(); + if (name.startsWith(TEMP_LOCAL_PREFIX)) { + let initValue = backtraceLocalInitValue(expr.getBase()); + if (initValue instanceof ArkThisRef) { + name = 'this'; + } + } + let methodName = expr.getMethodSignature().getMethodSubSignature().getMethodName(); + let field = this.getDeclaringArkClass().getFieldWithName(methodName); + if (name === 'this' && field?.hasBuilderParamDecorator()) { + return this.addBuilderParamNode(field); + } + let method = this.findMethod(expr.getMethodSignature()); + if (name === 'this' && method?.hasBuilderDecorator()) { + // console.log("builder method: ", method.getName()); + return this.analyzeBuilderNode(method); + // return this.addBuilderNode(method); + } + return undefined; + } + parsePtrInvokeExpr(local2Node, stmt, expr) { + let temp = expr.getFuncPtrLocal(); + if (temp instanceof Local && local2Node.has(temp)) { + let component = local2Node.get(temp); + if (component?.name === COMPONENT_REPEAT && expr.getMethodSignature().getMethodSubSignature().getMethodName() === 'each') { + let arg = expr.getArg(0); + let type = arg.getType(); + if (type instanceof FunctionType) { + let method = this.findMethod(type.getMethodSignature()); + this.buildViewTreeFromCfg(method?.getCfg()); + } + this.pop(); + } + else { + component?.addStmt(this, stmt); + } + return component; + } + else if (temp instanceof ArkInstanceFieldRef) { + let name = temp.getBase().getName(); + if (name.startsWith(TEMP_LOCAL_PREFIX)) { + let initValue = backtraceLocalInitValue(temp.getBase()); + if (initValue instanceof ArkThisRef) { + name = 'this'; + } + } + let methodName = temp.getFieldName(); + let field = this.getDeclaringArkClass().getFieldWithName(methodName); + if (name === 'this' && field?.hasBuilderParamDecorator()) { + return this.addBuilderParamNode(field); + } + let method = this.findMethod(expr.getMethodSignature()); + if (name === 'this' && method?.hasBuilderDecorator()) { + return this.addBuilderNode(method); + } + } + return undefined; + } + /** + * $temp3 = View.create($temp2); + * $temp4 = View.pop(); + * $temp4.margin({ top: 20 }); + * + * $temp2 = List.create(); + * $temp5 = $temp2.width('100%'); + * $temp6 = $temp5.height('100%'); + * $temp6.backgroundColor('#FFDCDCDC'); + * @param viewTree + * @param local2Node + * @param stmt + * @returns + */ + parseAssignStmt(local2Node, stmt) { + let left = stmt.getLeftOp(); + let right = stmt.getRightOp(); + if (!(left instanceof Local)) { + return; + } + let component; + if (right instanceof ArkStaticInvokeExpr) { + component = this.parseStaticInvokeExpr(local2Node, stmt, right); + } + else if (right instanceof ArkInstanceInvokeExpr) { + component = this.parseInstanceInvokeExpr(local2Node, stmt, right); + } + else if (right instanceof ArkPtrInvokeExpr) { + component = this.parsePtrInvokeExpr(local2Node, stmt, right); + } + if (component) { + local2Node.set(left, component); + } + } + parseInvokeStmt(local2Node, stmt) { + let expr = stmt.getInvokeExpr(); + if (expr instanceof ArkStaticInvokeExpr) { + this.parseStaticInvokeExpr(local2Node, stmt, expr); + } + else if (expr instanceof ArkInstanceInvokeExpr) { + this.parseInstanceInvokeExpr(local2Node, stmt, expr); + } + else if (expr instanceof ArkPtrInvokeExpr) { + this.parsePtrInvokeExpr(local2Node, stmt, expr); + } + } + buildViewTreeFromCfg(cfg, local2Node = new Map()) { + if (!cfg) { + return; + } + let blocks = cfg.getBlocks(); + for (const block of blocks) { + for (const stmt of block.getStmts()) { + if (!(stmt instanceof ArkInvokeStmt || stmt instanceof ArkAssignStmt)) { + continue; + } + if (stmt instanceof ArkAssignStmt) { + this.parseAssignStmt(local2Node, stmt); + } + else if (stmt instanceof ArkInvokeStmt) { + this.parseInvokeStmt(local2Node, stmt); + } + } + } + } + analayzeOverlayArg(local2Node, arg, stmt, apiType) { + const type = arg.getType(); + console.log("type: ", type.constructor.name); + if (type instanceof FunctionType) { + return this.hanldeFunctionType(local2Node, arg, type, stmt); + } + else if (type instanceof ArrayType && apiType == "bindMenu") { + console.log("执行 bind Menu"); + let menu_node = new ArkUIViewTreeNodeImpl("Menu"); + const res = this.handleBindMenuArray(local2Node, arg); + for (const node of res) { + menu_node.children.push(node); + } + return [menu_node]; + } + else if (type instanceof ClassType && apiType == "bindPopup") { + let popup_node = new ArkUIViewTreeNodeImpl("Popup"); + const res = this.handleBindPopupClass(local2Node, arg); + for (const node of res) { + popup_node.children.push(node); + } + return [popup_node]; + } + else ; + return []; + } + hanldeFunctionType(local2Node, arg, type, stmt) { + console.log("执行hanldeFunctionType"); + const methodSignature = type.getMethodSignature(); + const method = this.findMethod(methodSignature); + if (method?.hasBuilderDecorator()) { + const node = this.analyzeBuilderNode(method); + local2Node.set(arg, node); + return [node]; + } + return []; + } + handleBindMenuArray(local2Node, arg) { + // console.log("执行handleBindMenuArray"); + const result = []; + const local = arg; + const stmt = local.getDeclaringStmt(); + if (!(stmt instanceof ArkAssignStmt)) + return result; + const right = stmt.getRightOp(); + if (!(right instanceof ArkInstanceFieldRef)) + return result; + const fieldSig = right.getFieldSignature(); + const scene = this.render.getDeclaringArkFile().getScene(); + const clazz = this.findClass(fieldSig.getDeclaringSignature()); + const field = clazz?.getField(fieldSig); + const inits = field?.getInitializer(); + if (!inits) + return result; + for (const initStmt of inits) { + if (initStmt instanceof ArkAssignStmt && initStmt.getRightOp() instanceof ArkNewExpr) { + ///console.log("满足: stmt: ", initStmt.toString()); + const initValue = backtraceLocalInitValue(initStmt.getLeftOp()); + if (initValue instanceof ArkNewExpr) { + const cls = ModelUtils.getArkClassInBuild(scene, initValue.getClassType()); + const map = parseObjectLiteral(cls, scene); + const node = this.buildNodeFromMenuLiteral(local2Node, map, initStmt); + if (node) + result.push(node); + } + } + } + return result; + } + buildNodeFromMenuLiteral(local2Node, map, stmt) { + let textNode = null; + let action = null; + for (const [field, value] of map) { + if (field.getName() === "value" || field.getName() == "title") { + textNode = new ArkUIViewTreeNodeImpl("Text"); + textNode.text_content = value.getValue(); + } + else if (field.getName() === "action" && !(value instanceof Map)) { + action = value; + } + } + if (action instanceof Local) { + const type = action.getType(); + if (type instanceof FunctionType) { + const method = this.findMethod(type.getMethodSignature()); + if (method && textNode) { + //if(this.currentPage) addOverlaynode(textNode); + local2Node.set(action, textNode); + } + } + } + if (textNode && stmt) { + textNode.addStmt(this, stmt); + } + return textNode; + } + handleBindPopupClass(local2Node, arg) { + console.log("执行handleBindPopupClass"); + const result = []; + const calssSig = arg.getType().getClassSignature(); + const clazz = this.findClass(calssSig); + if (!clazz) + return []; + const scene = this.render.getDeclaringArkFile().getScene(); + const builder = clazz.getFieldWithName("builder"); + const primary = clazz.getFieldWithName("primaryButton"); + const secondary = clazz.getFieldWithName("secondaryButton"); + // @note(jxianxiao):一定同时存在么? + //if (!primary || !secondary) return result; + if (builder) { + console.log("begin to parse builder: "); + const builder_node = this.parseObjectFromArkField(builder, scene, "bindPopup"); + if (builder_node) { + local2Node.set(arg, builder_node); + result.push(builder_node); + } + } + if (primary) { + const node = this.parseObjectFromArkField(primary, scene, "bindPopup"); + if (node) { + local2Node.set(arg, node); + result.push(node); + } // @note(jxianxiao):这里可能会有问题,对应的arg是同一个,插入2个会导致覆盖 + } + if (secondary) { + const secNode = this.parseObjectFromArkField(secondary, scene, "bindPopup"); + if (secNode) { + local2Node.set(arg, secNode); // @note(jxianxiao):这里可能会有问题,对应的arg是同一个,插入2个会导致覆盖 + result.push(secNode); + } + } + return result; + } + handleBindMenuItemClass(local2Node, arg) { + const Menu_node = ArkUIViewTreeNodeImpl.createMenuNode(); + const calssSig = arg.getType().getClassSignature(); + const clazz = this.findClass(calssSig); + if (!clazz) + return undefined; + const scene = this.render.getDeclaringArkFile().getScene(); + const builder = clazz.getFieldWithName("builder"); + // @note(jxianxiao):一定同时存在么? + //if (!primary || !secondary) return result; + if (builder) { + console.log("begin to parse builder: "); + const builder_node = this.parseObjectFromArkField(builder, scene, "MenuItem"); + if (builder_node) { + local2Node.set(arg, builder_node); + Menu_node.children.push(builder_node); + return Menu_node; + } + } + return undefined; + } + analyzeBuilderNode(method) { + let builderViewTree = method.getArkUIViewTree(); + if (!builderViewTree || !builderViewTree.getRoot()) { + console.log(`ViewTree->addBuilderNode ${method.getSignature().toString()} build viewtree fail.`); + let node = ArkUIViewTreeNodeImpl.createBuilderNode(); + node.signature = method.getSignature(); + node.classSignature = node.signature; + return node; + } + let root = builderViewTree.getRoot(); + if (method.getDeclaringArkClass() === this.render.getDeclaringArkClass()) { + for (const [field, nodes] of builderViewTree.getStateValues()) { + for (const node of nodes) { + this.addStateValue(field, node); + } + } + } + //console.log("it's overlay nodes size: ",builderViewTree.getOverlayRoot()); + //这里加进去以后又弹出来了 + return root; + } + tabBarComponentParser(local2Node, stmt, expr) { + const args = expr.getArgs(); + for (const arg of args) { + const local = arg; + if (local2Node.has(local)) { + let node = local2Node.get(local); + this.printStack(); + let tabs_node = this.findNearestNodeByName("Tabs"); + let tabBarNode; + if (tabs_node && node) { + // 创建虚拟父节点 TabBar + tabBarNode = ArkUIViewTreeNodeImpl.createTabBarNode(); + tabBarNode.children.push(node); + node.parent = tabBarNode; + // 挂到 Tabs 节点下 + tabs_node.children.push(tabBarNode); + tabBarNode.parent = tabs_node; + } + // 创建一个 tabBar -> TabContent 的 一个关系 tabBar_2_TabContent + let base = expr.getBase(); + if (local2Node.has(base)) { + let tabContent_node = local2Node.get(base); + if (tabContent_node && tabBarNode) { + tabBar_2_TabContent.set(tabContent_node, tabBarNode); + // 创建 Handler 实例 + let handler = new Handler('onClick', // 触发类型 + tabContent_node, // 跳转目标 + exports.UITransitionType.TabContent, // 跳转类型 + undefined, // 方法签名 + '切换到对应的 TabContent 内容', // 描述 @todo 这个是根据下面的ViewPU来确定的 + undefined // 跳转方式 + ); + if (node) { + while (!ETS_COMPILER_OPTIONS.ets.components.includes(node.name)) { + node = node?.children[0]; + } + console.log("create one TabContent"); + node.handlers.push(handler); + } + } + } + } + } + return undefined; + } + navDestinationComponentParser(local2Node, stmt, expr) { + const args = expr.getArgs(); + for (const arg of args) { + const local = arg; + let type = arg.getType(); + if (local2Node.has(local)) { + const node = local2Node.get(local); + let navigation_node = this.findNearestNodeByName("Navigation"); + if (navigation_node && node) { + // 创建虚拟父节点 NavDestination + const navDestinationNode = ArkUIViewTreeNodeImpl.createPageMapNode(); + navDestinationNode.children.push(node); + node.parent = navDestinationNode; + // 挂到 Tabs 节点下 + navigation_node.children.push(navDestinationNode); + navDestinationNode.parent = navigation_node; + } + } + else if (type instanceof FunctionType) { + const method = this.findMethod(type.getMethodSignature()); + if (method && method.hasBuilderDecorator()) { + const builder_node = this.analyzeBuilderNode(method); + local2Node.set(arg, builder_node); + const base = expr.getBase(); + if (base instanceof Local && local2Node.has(base)) { + const nav_node = local2Node.get(base); + if (nav_node) { + const pagemap_node = ArkUIViewTreeNodeImpl.createPageMapNode(); + pagemap_node.children.push(builder_node); + builder_node.parent = pagemap_node; + // 挂到 Navigation 节点下 + nav_node.children.push(pagemap_node); + pagemap_node.parent = nav_node; + } + } + } + } + else ; + } + return undefined; + } + bindContentCoverComponentParser(local2Node, stmt, expr) { + const args = expr.getArgs(); + for (const arg of args) { + //const type = arg.getType(); + const local = arg; + if (local2Node.has(local)) { + const node = local2Node.get(local); + let root = this.virtualRoot; + if (root && node) { + // 创建虚拟父节点 TabBar + const bindContentNode = ArkUIViewTreeNodeImpl.createBindContentNode(); + bindContentNode.children.push(node); + node.parent = bindContentNode; + // 收集到全局 Overlay Tree + GlobalOverlayTree.push(bindContentNode); + } + if (node && !root) { + console.warn("bindContentCoverComponentParser: root is undefined, node: ", node.toString()); + } + } + } + return undefined; + } + bindSheetComponentParser(local2Node, stmt, expr) { + const args = expr.getArgs(); + for (const arg of args) { + const type = arg.getType(); + console.log("type: ", type.constructor.name); + const local = arg; + if (local2Node.has(local)) { + console.log("yes local2node has local"); + const node = local2Node.get(local); + let root = this.virtualRoot; + if (root && node) { + // 创建虚拟父节点 TabBar + const bindContentNode = ArkUIViewTreeNodeImpl.createBindSheetNode(); + bindContentNode.children.push(node); + node.parent = bindContentNode; + // 收集到全局 Overlay Tree + GlobalOverlayTree.push(bindContentNode); + } + if (node && !root) { + console.warn("bindSheetComponentParser: root is undefined, node: ", node.toString()); + } + } + } + return undefined; + } + bindContextMenuComponentParser(local2Node, stmt, expr) { + const args = expr.getArgs(); + for (const arg of args) { + const type = arg.getType(); + console.log("type: ", type.constructor.name); + const local = arg; + if (local2Node.has(local)) { + console.log("yes local2node has local"); + const node = local2Node.get(local); + let root = this.virtualRoot; + if (root && node) { + // 创建虚拟父节点 TabBar + const bindContentNode = ArkUIViewTreeNodeImpl.createMenuNode(); + bindContentNode.children.push(node); + node.parent = bindContentNode; + // 收集到全局 Overlay Tree + GlobalOverlayTree.push(bindContentNode); + } + if (node && !root) { + console.warn("bindContextMenuComponentParser: root is undefined, node: ", node.toString()); + } + } + else if (type instanceof FunctionType) { + const method = this.findMethod(type.getMethodSignature()); + if (method && method.hasBuilderDecorator()) { + const builder_node = this.analyzeBuilderNode(method); + local2Node.set(arg, builder_node); + const MenuWrapperNode = ArkUIViewTreeNodeImpl.createMenuNode(); + MenuWrapperNode.children.push(builder_node); + builder_node.parent = MenuWrapperNode; + GlobalOverlayTree.push(MenuWrapperNode); + } + } + } + return undefined; + } + // bindMenu(isShow: boolean, content: Array | CustomBuilder, options?: MenuOptions): T + bindMenuComponentParser(local2Node, stmt, expr) { + const args = expr.getArgs(); + for (const arg of args) { + const type = arg.getType(); + const local = arg; + // 1. 如果参数是数组类型(常见于 bindMenu 场景) + if (type instanceof ArrayType) { + const menuNode = ArkUIViewTreeNodeImpl.createMenuNode(); + const menuItems = this.handleBindMenuArray(local2Node, arg); + for (const item of menuItems) { + menuNode.children.push(item); + item.parent = menuNode; + } + GlobalOverlayTree.push(menuNode); + } + // 2. 如果 local2Node 里有节点,直接挂到 Menu + else if (local2Node.has(local)) { + const node = local2Node.get(local); + if (node) { + const menuNode = ArkUIViewTreeNodeImpl.createMenuNode(); + menuNode.children.push(node); + node.parent = menuNode; + GlobalOverlayTree.push(menuNode); + } + } + // 3. 如果参数是 FunctionType,递归分析 + else if (type instanceof FunctionType) { + const method = this.findMethod(type.getMethodSignature()); + if (method && method.hasBuilderDecorator()) { + const menuNode = ArkUIViewTreeNodeImpl.createMenuWrapperNode(); + this.stack.push(menuNode); + const builderNode = this.analyzeBuilderNode(method); + this.pop(); + local2Node.set(arg, builderNode); + menuNode.children.push(builderNode); + builderNode.parent = menuNode; + GlobalOverlayTree.push(menuNode); + } + } + } + return undefined; + } + bindPopupComponentParser(local2Node, stmt, expr) { + const args = expr.getArgs(); + for (const arg of args) { + const type = arg.getType(); + const local = arg; + // 1. 如果 local2Node 里有节点,直接挂到 Popup + if (local2Node.has(local)) { + const node = local2Node.get(local); + if (node) { + const popupNode = ArkUIViewTreeNodeImpl.createPopupNode(); + popupNode.children.push(node); + node.parent = popupNode; + GlobalOverlayTree.push(popupNode); + } + } + // 2. 如果参数是 ClassType,调用 handleBindPopupClass + else if (type instanceof ClassType) { + const popupNode = ArkUIViewTreeNodeImpl.createPopupNode(); // 或 createPopupNode() + const popupItems = this.handleBindPopupClass(local2Node, arg); + for (const item of popupItems) { + popupNode.children.push(item); + item.parent = popupNode; + } + GlobalOverlayTree.push(popupNode); + } + // 3. 如果参数是 FunctionType,递归分析 + else if (type instanceof FunctionType) { + const method = this.findMethod(type.getMethodSignature()); + if (method && method.hasBuilderDecorator()) { + const builderNode = this.analyzeBuilderNode(method); + local2Node.set(arg, builderNode); + const popupNode = ArkUIViewTreeNodeImpl.createPopupNode(); // 或 createPopupNode() + popupNode.children.push(builderNode); + builderNode.parent = popupNode; + GlobalOverlayTree.push(popupNode); + } + } + } + return undefined; + } + findBuilderMethodDeep(method) { + if (method.hasBuilderDecorator()) { + return method; + } + const cfg = method.getCfg(); + if (!cfg) + return undefined; + for (const stmt of cfg.getStmts()) { + // 1. 直接 instanceinvoke 语句 + if (stmt instanceof ArkInvokeStmt) { + const expr = stmt.getInvokeExpr(); + if (expr instanceof ArkInstanceInvokeExpr) { + const base = expr.getBase(); + //const methodName = expr.getMethodSignature().getMethodSubSignature().getMethodName(); + if (base.getName() === "this") { + const subMethod = this.findMethod(expr.getMethodSignature()); + if (subMethod && subMethod.hasBuilderDecorator()) { + return subMethod; + } + } + } + } + // 2. 赋值语句,右侧是 instanceinvoke + else if (stmt instanceof ArkAssignStmt) { + const rightOp = stmt.getRightOp(); + if (rightOp instanceof ArkInstanceInvokeExpr) { + const base = rightOp.getBase(); + //const methodName = rightOp.getMethodSignature().getMethodSubSignature().getMethodName(); + if (base.getName() === "this") { + const subMethod = this.findMethod(rightOp.getMethodSignature()); + if (subMethod && subMethod.hasBuilderDecorator()) { + return subMethod; + } + } + } + } + // 3. 可以递归处理更深层的表达式(如右侧是 Lambda/Block/CallChain) + // 可根据实际IR结构扩展 + } + return undefined; + } + parseObjectFromArkField(field, scene, api_name) { + console.log("field: ", field); + const initStmts = field.getInitializer(); + const newLocals = []; + if (api_name === 'open') { + for (const stmt of initStmts) { + console.log("open init: ", stmt.toString()); + } + } + // 提取所有 ArkNewExpr 左值 + for (const stmt of initStmts) { + if (stmt instanceof ArkAssignStmt) { + let rightOp = stmt.getRightOp(); + let leftOp = stmt.getLeftOp(); + if (rightOp instanceof ArkNewExpr) { + newLocals.push(stmt.getLeftOp()); + } + else if (rightOp instanceof ArkInstanceFieldRef) { + console.log("field: ", rightOp); + if (field.getType() instanceof FunctionType) { + const methodSignature = field.getType().getMethodSignature(); + const method = scene.getMethod(methodSignature); + if (method?.hasBuilderDecorator()) { + const builder_node = this.analyzeBuilderNode(method); + return builder_node; + } + } + } + else if (rightOp instanceof Local && rightOp.getType() instanceof FunctionType && leftOp instanceof ArkInstanceFieldRef && leftOp.getFieldName() == "builder") { + const methodSignature = rightOp.getType().getMethodSignature(); + const method = scene.getMethod(methodSignature); + const builder_method = method ? this.findBuilderMethodDeep(method) : undefined; + if (builder_method) { + const builder_node = this.analyzeBuilderNode(builder_method); + return builder_node; + } + } + else ; + } + } + for (const local of newLocals) { + const initValue = backtraceLocalInitValue(local); + //console.log("initValue: ", initValue); + if (initValue instanceof ArkNewExpr) { + const arkClass = ModelUtils.getArkClassInBuild(scene, initValue.getClassType()); + const fieldValueMap = parseObjectLiteral(arkClass, scene); // Map + console.log("fieldValueMap: ", fieldValueMap); + let node; + let action; + for (let pair of fieldValueMap) { + let field_name = pair[0].getName(); + let value = pair[1]; + if (field_name == "value") { + // @todo(jxianxiao):根据 action 回调函数 来实现与 node 的绑定(UI迁移行为) + // if(api_name == "bindPopup"){ + // node = new ViewTreeNodeImpl("Button"); + // }else{ + // node = new ViewTreeNodeImpl("Text"); + // } + node = new ArkUIViewTreeNodeImpl("Text"); + if (value instanceof StringConstant) { + node.text_content = value.getValue(); + } + } + else if (field_name == "action") { + action = value; + } + else if (field_name == "builder") { + console.log("value: ", value); + if (value instanceof Local) { + const type = value.getType(); + if (type instanceof FunctionType) { + const method = this.findMethod(type.getMethodSignature()); + if (method && method.hasBuilderDecorator()) { + // 分析 builder 方法,生成 Dialog 内容树 + const builderNode = this.analyzeBuilderNode(method); + // ...将 builderNode 挂到 Dialog 节点下... + GlobalOverlayTree.push(builderNode); + } + } + } + else if (value instanceof ArkStaticInvokeExpr) { + const fakeStmt = new ArkInvokeStmt(value); // 或者用你已有的 stmt + const expr = value; + const methodSignature = expr.getMethodSignature(); + const classSignature = methodSignature.getDeclaringClassSignature(); + const clazz = this.findClass(classSignature); + const view_node = clazz?.getArkUIViewTree()?.getRoot(); + // console.log("view_node: ", view_node); + if (!clazz) + continue; + view_node.signature = clazz?.getSignature(); + // console.log("builder_class: ", clazz); + // console.log("builder_sig: ", clazz?.getSignature()); + view_node.classSignature = view_node.signature; + if (view_node) { + GlobalOverlayTree.push(view_node); + } + const agrs = value.getArgs(); + for (const arg of agrs) { + const type = arg.getType(); + if (type instanceof ClassType) { + // 递归分析自定义组件 + // ... + this.handleDialogClass(new Map(), arg, type, fakeStmt, value.getMethodSignature().getMethodSubSignature().getMethodName()); + } + } + } + } + } + if (!action) { + continue; + } + let type = action.getType(); + if (type instanceof FunctionType) { + const method_signature = type.getMethodSignature(); + let method = scene.getMethod(method_signature); + if (method && method.getCfg()) ; + } + return node; + } + } + return null; + } + analyzeDialogFromArkClass(arkclass, scene, local2Node = new Map()) { + const methods = arkclass.getMethods(); + for (const mehtod of methods) { + const cfg = mehtod.getCfg(); + const stmts = cfg?.getStmts(); + if (stmts) { + for (const stmt of stmts) { + this.analayzeDialogFromArkStmt(local2Node, stmt, scene); + } + } + } + } + analayzeDialogFromArkStmt(local2Node, stmt, scene) { + if (stmt instanceof ArkInvokeStmt) { + const expr = stmt.getInvokeExpr(); + if (expr instanceof ArkInstanceInvokeExpr) { + const base = expr.getBase(); + const method_name = expr.getMethodSignature().getMethodSubSignature().getMethodName(); + if (method_name == "showAlertDialog") { + let parserFn = this.DIALOG_SHOW_PARSERS.get(method_name); + if (parserFn) { + parserFn(local2Node, stmt, expr); + } + } + else if (method_name == "showActionSheet") { + let parserFn = this.DIALOG_SHOW_PARSERS.get(method_name); + if (parserFn) { + parserFn(local2Node, stmt, expr); + } + } + else if (method_name == "open") { + console.log("open stmt: ", stmt); + const base = expr.getBase(); + const initValue = backtraceLocalInitValue(base); + console.log("base: ", base); + console.log("initValue: ", initValue); + if (initValue instanceof ArkInstanceFieldRef) { + const field_name = initValue.getFieldSignature().getFieldName(); + const declaring_sig = initValue.getFieldSignature().getDeclaringSignature(); + if (declaring_sig instanceof ClassSignature) { + const clazz = this.findClass(declaring_sig); + let field = clazz?.getFieldWithName(field_name); + if (field instanceof ArkField) { + this.parseObjectFromArkField(field, scene, "open"); + } + } + } + //console.log("initValue: ",initValue); + } + else if (base.getName() == "CalendarPickerDialog" && method_name == "show") { + let parserFn = this.DIALOG_SHOW_PARSERS.get(base.getName()); + if (parserFn) { + parserFn(local2Node, stmt, expr); + } + } + else if (method_name == "showDatePickerDialog" || method_name == "showTimePickerDialog" || method_name == "showTextPickerDialog") { + let parserFn = this.DIALOG_SHOW_PARSERS.get(method_name); + if (parserFn) { + parserFn(local2Node, stmt, expr); + } + } + } + } + } + // ref:https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-methods-alert-dialog-box#alertdialogparam%E5%AF%B9%E8%B1%A1%E8%AF%B4%E6%98%8E + AlertDialogShowParser(local2Node, stmt, expr) { + if (!(expr instanceof ArkInstanceInvokeExpr)) + return; + const args = expr.getArgs(); + for (const arg of args) { + const type = arg.getType(); + if (type instanceof ClassType) { + this.handleDialogClass(local2Node, arg, type, stmt, "AlterDialog"); + } + } + } + ToastShowParser(local2Node, stmt, expr) { + if (!(expr instanceof ArkInstanceInvokeExpr)) + return; + const args = expr.getArgs(); + for (const arg of args) { + const type = arg.getType(); + if (type instanceof ClassType) { + this.handleDialogClass(local2Node, arg, type, stmt, "Toast"); + } + } + } + DialogShowParser(local2Node, stmt, expr) { + if (!(expr instanceof ArkInstanceInvokeExpr)) + return; + const args = expr.getArgs(); + for (const arg of args) { + const type = arg.getType(); + if (type instanceof ClassType) { + this.handleDialogClass(local2Node, arg, type, stmt, "Dialog"); + } + } + } + ActionMenuShowParser(local2Node, stmt, expr) { + if (!(expr instanceof ArkInstanceInvokeExpr)) + return; + const args = expr.getArgs(); + for (const arg of args) { + const type = arg.getType(); + if (type instanceof ClassType) { + this.handleDialogClass(local2Node, arg, type, stmt, "ActionMenu"); + } + } + } + handleDialogClass(local2Node, arg, type, stmt, dialog_type) { + const calssSig = arg.getType().getClassSignature(); + // console.log("calssSig: ", calssSig); + const clazz = this.findClass(calssSig); + if (!clazz) + return; + const scene = this.render.getDeclaringArkFile().getScene(); + // console.log("handleDialogClass clazz: ", clazz); + const primary = clazz.getFieldWithName("primaryButton"); + const secondary = clazz.getFieldWithName("secondaryButton"); + const confirm = clazz.getFieldWithName("confirm"); + const buttons = clazz.getFieldWithName("buttons"); + const radioContent = clazz.getFieldWithName("radioContent"); + // @note(jxianxiao):一定同时存在么? + //if (!primary || !secondary) return result; + let dialog_node = ArkUIViewTreeNodeImpl.createAlertDialogNode(); + if (dialog_type == "AlertDialog") { + dialog_node = ArkUIViewTreeNodeImpl.createAlertDialogNode(); + } + else if (dialog_type == "TipsDialog") { + dialog_node = ArkUIViewTreeNodeImpl.createTipsDialogNode(); + } + else if (dialog_type == "ConfirmDialog") { + dialog_node = ArkUIViewTreeNodeImpl.createConfirmDialogNode(); + } + else if (dialog_type == "LodaingDialog") { + dialog_node = ArkUIViewTreeNodeImpl.createLodaingDialogNode(); + } + else if (dialog_type == "CustomContentDialog") { + dialog_node = ArkUIViewTreeNodeImpl.createCustomContentDialogNode(); + } + else if (dialog_type == "Toast") { + dialog_node = ArkUIViewTreeNodeImpl.createToastNode(); + } + else if (dialog_type == "Dialog") { + dialog_node = ArkUIViewTreeNodeImpl.createDialogNode(); + } + else if (dialog_type == "ActionMenu") { + dialog_node = ArkUIViewTreeNodeImpl.createMenuNode(); + } + if (primary) { + const node = this.parseObjectFromArkField(primary, scene, "AlertDialog"); + if (node) { + local2Node.set(arg, node); + dialog_node.children.push(node); + node.parent = dialog_node; + } // @note(jxianxiao):这里可能会有问题,对应的arg是同一个,插入2个会导致覆盖 + } + if (secondary) { + const secNode = this.parseObjectFromArkField(secondary, scene, "AlertDialog"); + if (secNode) { + local2Node.set(arg, secNode); // @note(jxianxiao):这里可能会有问题,对应的arg是同一个,插入2个会导致覆盖 + dialog_node.children.push(secNode); + secNode.parent = dialog_node; + } + } + if (confirm) { + const confirm_node = this.parseObjectFromArkField(confirm, scene, "AlertDialog"); + if (confirm_node) { + local2Node.set(arg, confirm_node); // @note(jxianxiao):这里可能会有问题,对应的arg是同一个,插入2个会导致覆盖 + dialog_node.children.push(confirm_node); + confirm_node.parent = dialog_node; + } + } + if (buttons) { + const type = buttons.getType(); + if (type instanceof ArrayType) { + const inits = buttons.getInitializer(); + for (const initStmt of inits) { + if (initStmt instanceof ArkAssignStmt && initStmt.getRightOp() instanceof ArkNewExpr) { + const initValue = backtraceLocalInitValue(initStmt.getLeftOp()); + if (initValue instanceof ArkNewExpr) { + const cls = ModelUtils.getArkClassInBuild(scene, initValue.getClassType()); + const map = parseObjectLiteral(cls, scene); + const node = this.buildNodeFromMenuLiteral(local2Node, map, initStmt); + if (node) { + dialog_node.children.push(node); + node.parent = dialog_node; + } + } + } + } + } + //parseObjectFromArkField(buttons, scene, "AlertDialog"); + } + if (radioContent) { + const type = radioContent.getType(); + if (type instanceof ArrayType) { + const inits = radioContent.getInitializer(); + for (const initStmt of inits) { + if (initStmt instanceof ArkAssignStmt && initStmt.getRightOp() instanceof ArkNewExpr) { + const initValue = backtraceLocalInitValue(initStmt.getLeftOp()); + if (initValue instanceof ArkNewExpr) { + const cls = ModelUtils.getArkClassInBuild(scene, initValue.getClassType()); + const map = parseObjectLiteral(cls, scene); + const node = this.buildNodeFromMenuLiteral(local2Node, map, initStmt); + if (node) { + dialog_node.children.push(node); + node.parent = dialog_node; + } + } + } + } + } + //parseObjectFromArkField(buttons, scene, "AlertDialog"); + } + GlobalOverlayTree.push(dialog_node); + //return result; + } + ActionSheetShowParser(local2Node, stmt, expr) { + if (!(expr instanceof ArkInstanceInvokeExpr)) + return; + const args = expr.getArgs(); + for (const arg of args) { + const type = arg.getType(); + if (type instanceof ClassType) { + this.handleSheetClass(local2Node, arg, type, stmt); + } + } + } + handleSheetClass(local2Node, arg, type, stmt) { + const calssSig = arg.getType().getClassSignature(); + const clazz = this.findClass(calssSig); + if (!clazz) + return; + const scene = this.render.getDeclaringArkFile().getScene(); + const primary = clazz.getFieldWithName("primaryButton"); + const secondary = clazz.getFieldWithName("secondaryButton"); + const confirm = clazz.getFieldWithName("confirm"); + const sheets = clazz.getFieldWithName("sheets"); + // @note(jxianxiao):一定同时存在么? + //if (!primary || !secondary) return result; + const alter_dialog_node = ArkUIViewTreeNodeImpl.createActionSheetNode(); + if (primary) { + const node = this.parseObjectFromArkField(primary, scene, "ActionSheet"); + if (node) { + local2Node.set(arg, node); + alter_dialog_node.children.push(node); + node.parent = alter_dialog_node; + } // @note(jxianxiao):这里可能会有问题,对应的arg是同一个,插入2个会导致覆盖 + } + if (secondary) { + const secNode = this.parseObjectFromArkField(secondary, scene, "ActionSheet"); + if (secNode) { + local2Node.set(arg, secNode); // @note(jxianxiao):这里可能会有问题,对应的arg是同一个,插入2个会导致覆盖 + alter_dialog_node.children.push(secNode); + secNode.parent = alter_dialog_node; + } + } + if (confirm) { + const confirm_node = this.parseObjectFromArkField(confirm, scene, "ActionSheet"); + if (confirm_node) { + local2Node.set(arg, confirm_node); // @note(jxianxiao):这里可能会有问题,对应的arg是同一个,插入2个会导致覆盖 + alter_dialog_node.children.push(confirm_node); + confirm_node.parent = alter_dialog_node; + } + } + if (sheets) { + const type = sheets.getType(); + if (type instanceof ArrayType) { + const inits = sheets.getInitializer(); + for (const initStmt of inits) { + if (initStmt instanceof ArkAssignStmt && initStmt.getRightOp() instanceof ArkNewExpr) { + const initValue = backtraceLocalInitValue(initStmt.getLeftOp()); + if (initValue instanceof ArkNewExpr) { + const cls = ModelUtils.getArkClassInBuild(scene, initValue.getClassType()); + const map = parseObjectLiteral(cls, scene); + const node = this.buildNodeFromMenuLiteral(local2Node, map, initStmt); + if (node) { + alter_dialog_node.children.push(node); + node.parent = alter_dialog_node; + } + } + } + } + } + //parseObjectFromArkField(buttons, scene, "AlertDialog"); + } + GlobalOverlayTree.push(alter_dialog_node); + //return result; + } + CalendarPickerDialogShowParser(local2Node, stmt, expr) { + const Dialog = ArkUIViewTreeNodeImpl.createDialogNode(); + const node1 = new ArkUIViewTreeNodeImpl("Text"); + node1.text_content = "确定"; + const node2 = new ArkUIViewTreeNodeImpl("Text"); + node2.text_content = "取消"; + const Calendar_node = new ArkUIViewTreeNodeImpl("Calendar"); + Dialog.children.push(node1); + Dialog.children.push(node2); + Dialog.children.push(Calendar_node); + GlobalOverlayTree.push(Dialog); + return Dialog; + } + DatePickerDialogShowParser(local2Node, stmt, expr) { + const Dialog = ArkUIViewTreeNodeImpl.createDialogNode(); + const node1 = new ArkUIViewTreeNodeImpl("Text"); + node1.text_content = "确定"; + const node2 = new ArkUIViewTreeNodeImpl("Text"); + node2.text_content = "取消"; + const DatePicker_node = new ArkUIViewTreeNodeImpl("DatePicker"); + Dialog.children.push(node1); + Dialog.children.push(node2); + Dialog.children.push(DatePicker_node); + GlobalOverlayTree.push(Dialog); + return Dialog; + } + TimePickerDialogShowParser(local2Node, stmt, expr) { + const Dialog = ArkUIViewTreeNodeImpl.createDialogNode(); + const node1 = new ArkUIViewTreeNodeImpl("Text"); + node1.text_content = "确定"; + const node2 = new ArkUIViewTreeNodeImpl("Text"); + node2.text_content = "取消"; + const TimePicker_node = new ArkUIViewTreeNodeImpl("TimePicker"); + Dialog.children.push(node1); + Dialog.children.push(node2); + Dialog.children.push(TimePicker_node); + GlobalOverlayTree.push(Dialog); + return Dialog; + } + TextPickerDialogShowParser(local2Node, stmt, expr) { + const Dialog = ArkUIViewTreeNodeImpl.createDialogNode(); + const node1 = new ArkUIViewTreeNodeImpl("Text"); + node1.text_content = "确定"; + const node2 = new ArkUIViewTreeNodeImpl("Text"); + node2.text_content = "取消"; + const TextPicker_node = new ArkUIViewTreeNodeImpl("TextPicker"); + Dialog.children.push(node1); + Dialog.children.push(node2); + Dialog.children.push(TextPicker_node); + GlobalOverlayTree.push(Dialog); + return Dialog; + } + initArkUIViewTreeFromLayout(layoutPath) { + const layoutDir = layoutPath; + const fs = require('fs'); + const path = require('path'); + const dynamicTrees = []; + if (!fs.existsSync(layoutDir) || !fs.lstatSync(layoutDir).isDirectory()) { + console.error(`Layout directory does not exist: ${layoutDir}`); + return dynamicTrees; + } + const entries = fs.readdirSync(layoutDir); + for (const entry of entries) { + const entryPath = path.join(layoutDir, entry); + if (path.extname(entryPath) === '.json') { + let jsonData; + try { + const inputFile = fs.readFileSync(entryPath, 'utf-8'); + if (inputFile.trim() === '') { + console.warn(`[WARNING] File is empty: ${path.basename(entryPath)}`); + continue; + } + jsonData = JSON.parse(inputFile); + } + catch (error) { + console.error(`\nerror: failed to open \`${entryPath}\`\n`); + continue; + } + const dummyMethod = new ArkMethod(); + const dynamicTree = new ArkUIViewTreeImpl(dummyMethod); + const rootNode = dynamicTree.buildDynamicTreeFromLayout(jsonData, ''); + dynamicTree.root = rootNode.children[0]; + if (dynamicTree.root.children.length > 1) { + console.log("this is overlay tree"); + dynamicTree.root.children.splice(0, 1); // 删除第一个孩子 + dynamicTree.root.dynamic_attributes["pagePath"] = "overlay"; + } + console.log("begin to build xpath"); + dynamicTree.buildXpath(dynamicTree.root, dynamicTree.root, 0, false); + dynamicTree.UpdateNearestTypeNodeMap(dynamicTree.root, dynamicTree.root); + dynamicTree.buildXpath2Nodes(dynamicTree.root, dynamicTree.root); + dynamicTrees.push(dynamicTree); + const baseName = path.basename(entryPath, '.json'); // 去掉 .json 扩展名 + dynamicTree.setLayoutFileName(baseName); + //console.log("dynamic root: ", rootNode); + } + } + return dynamicTrees; + } + buildDynamicTreeFromLayout(jsonData, xpath) { + const rootNode = new ArkUIViewTreeNodeImpl("root"); + if (jsonData.hasOwnProperty("attributes")) { + const dynamicAttributes = jsonData["attributes"]; + // 如果是 WindowsSence 节点,直接返回空节点 + if (dynamicAttributes.hasOwnProperty("abilityName") && + dynamicAttributes.hasOwnProperty("type") && + dynamicAttributes["type"] && + dynamicAttributes["type"] !== "root") { + return rootNode; // 空节点 + } + //设置动态属性 + rootNode.setDynamicAttributes(dynamicAttributes); + rootNode.name = dynamicAttributes["type"] || "unknown"; // "type" 即为组件名称 + rootNode.text_content = dynamicAttributes["text"] || ""; + rootNode.xpath = xpath; + let handler = new Handler("unknown", null, exports.UITransitionType.None, undefined, dynamicAttributes["accessibilityId"] || ""); + rootNode.handlers.push(handler); + if (jsonData.hasOwnProperty("children") && Array.isArray(jsonData["children"])) { + for (const childData of jsonData["children"]) { + const childNode = this.buildDynamicTreeFromLayout(childData, xpath); + if (childNode.name !== "none") { + rootNode.children.push(childNode); + childNode.parent = rootNode; + } + } + } + } + return rootNode; + } + buildXpath2Nodes(current, parent) { + // 初始化字段 + if (!this.xpath2nodes) { + this.xpath2nodes = new Map(); + } + if (!this.child2parent) { + this.child2parent = new Map(); + } + if (!this.nodeSet) { + this.nodeSet = new Set(); + } + // 将当前节点的 XPath 映射到节点集合 + if (!this.xpath2nodes.has(current.xpath)) { + this.xpath2nodes.set(current.xpath, new Set()); + } + this.xpath2nodes.get(current.xpath)?.add(current); + // 将当前节点的 XPath 添加到向量中 + this.xpathVector.push(current.xpath); + //console.log("current xpath: ", current.xpath); + // 将当前节点与父节点的关系存储 + this.child2parent.set(current, parent); + // 将当前节点添加到节点集合 + this.nodeSet.add(current); + // 递归处理子节点 + for (const child of current.children) { + this.buildXpath2Nodes(child, current); + } + } + buildXpath(current, parent, cnt, check = true) { + let has_builder = false; + for (const child of current.children) { + if (child.name == "Builder") { + has_builder = true; + break; + } + } + let current_xpath_copy = current.xpath; + if (current === this.root) { + current.xpath = "root"; + } + else if (parent) { + if (check == false) { + current.xpath = parent.xpath + "/" + current.name; + } + else { + if (ETS_COMPILER_OPTIONS.ets.components.includes(current.name)) { + current.xpath = parent.xpath + "/" + current.name; + } + } + } + if (current.name == "TabContent" && has_builder == true) { + current.xpath = current_xpath_copy; + } + current.unique_xpath = current.unique_xpath + "/" + current.name + (++this.unique_xpath_idx); + for (const child of current.children) { + child.xpath = current.xpath; + child.unique_xpath = current.unique_xpath; + this.buildXpath(child, current, cnt + 1, check); + } + // console.log("current unique_xpath: ", current.unique_xpath); + } + UpdateNearestTypeNodeMap(current, parent) { + //console.log("current : ", current.name + " parent: ", parent.name); + if (current != parent) { + for (const type of ETS_COMPILER_OPTIONS.ets.components) { + if (type == parent.name) { + current.nearest_type_node_map_upward.set(type, parent); + } + else if (parent.nearest_type_node_map_upward.has(type)) { + current.nearest_type_node_map_upward.set(type, parent.nearest_type_node_map_upward.get(type)); + } + } + } + for (const child of current.children) { + this.UpdateNearestTypeNodeMap(child, current); + } + if (current == parent) { + return; + } + if (!current.nearest_type_node_map_downward) { + current.nearest_type_node_map_downward = new Map(); + } + if (!parent.nearest_type_node_map_downward) { + parent.nearest_type_node_map_downward = new Map(); + } + const map = current.nearest_type_node_map_downward; + const parent_map = parent.nearest_type_node_map_downward; + for (const type of ETS_COMPILER_OPTIONS.ets.components) { + const type_set = map.get(type); + if (type == current.name) { + let set = parent_map.get(type); + if (!set) { + set = new Set(); + parent_map.set(type, set); + } + parent_map.get(type)?.add(current); + } + if (type_set) { + if (type == current.name) { + let set = parent_map.get(type); + if (!set) { + set = new Set(); + parent_map.set(type, set); + } + parent_map.get(type)?.add(current); + } + for (const node of type_set) { + if (!parent_map.has(type)) { + parent_map.set(type, new Set()); + } + parent_map.get(type)?.add(node); + } + // @todo 原来版本 是不是不拿最近的对于匹配效果更高? 但是效率呢? + /* + else { + for (const node of type_set) { + if (!parent_map.has(type)) { + parent_map.set(type, new Set()); + } + parent_map.get(type)?.add(node); + } + } + */ + } + } + // console.log(`nearest_type_node_map_downward for node ${current.xpath}:`); + // current.nearest_type_node_map_downward.forEach((nodeSet, type) => { + // const names = Array.from(nodeSet).map(node => node.xpath).join(', '); + // console.log(` type: ${type}, nodes: [${names}]`); + // }); + //console.log("current down: ",current.nearest_type_node_map_downward); + } +} +function buildArkUIViewTree(render) { + return new ArkUIViewTreeImpl(render); +} +function TextMatching(staticTree, dynamicTree) { + for (const dynamic_xpath of dynamicTree.xpathVector) { + const static_set = staticTree.xpath2nodes.get(dynamic_xpath); + const dynamic_set = dynamicTree.xpath2nodes.get(dynamic_xpath); + // console.log("dynamic_xpath: ", dynamic_xpath); + // console.log("static_set: ", static_set); + // console.log("dynamic_set: ", dynamic_set); + if (static_set && dynamic_set) { + //console.log("common xpath: ", dynamic_xpath); + for (const static_node of static_set) { + //console.log("static_node: ", static_node.unique_xpath); + if (!staticTree.nodes2possiblesnode.has(static_node.unique_xpath)) { + staticTree.nodes2possiblesnode.set(static_node.unique_xpath, new Set()); + } + for (const dynamic_node of dynamic_set) { + if (!dynamicTree.nodes2possiblesnode.has(dynamic_node.unique_xpath)) { + dynamicTree.nodes2possiblesnode.set(dynamic_node.unique_xpath, new Set()); + } + staticTree.nodes2possiblesnode.get(static_node.unique_xpath)?.add(dynamic_node); + dynamicTree.nodes2possiblesnode.get(dynamic_node.unique_xpath)?.add(static_node); + } + } + } + } + // 处理结束后输出 staticTree.nodes2possiblesnode 内容 + // console.log("===== staticTree.nodes2possiblesnode 内容 ====="); + // staticTree.nodes2possiblesnode.forEach((valueSet, key) => { + // console.log(`Key: ${key}`); + // console.log("关联的节点:"); + // valueSet.forEach(node => { + // // 假设每个节点有 unique_xpath 属性可以标识 + // console.log(` - ${node.unique_xpath}`); + // }); + // console.log("---"); // 分隔线 + // }); +} +function DFSMatching(staticTree, dynamicTree, static_node) { + const static_node_impl = static_node; + //console.log("current static_node: ", static_node_impl.xpath); + if (!staticTree.nodes2possiblesnode.has(static_node_impl.unique_xpath)) { + // console.log("static_node not in nodes2possiblesnode: ", static_node.xpath); + return; // static_node没有对应的 候选 dynamic_node + } + let possible_nodes = staticTree.nodes2possiblesnode.get(static_node_impl.unique_xpath); + let has_builder = false; + for (const child of static_node.children) { + if (child.name == "Builder") { + has_builder = true; + break; + } + } + for (const child of static_node.children) { + if (!ETS_COMPILER_OPTIONS.ets.components.includes(child.name) || (child.name == "TabContent" && has_builder == true)) // child has buidler 要加进去么? + { + if (possible_nodes) { + if (!staticTree.nodes2possiblesnode.has(child.unique_xpath)) { + staticTree.nodes2possiblesnode.set(child.unique_xpath, new Set()); + } + staticTree.nodes2possiblesnode.set(child.unique_xpath, possible_nodes); + } + DFSMatching(staticTree, dynamicTree, child); + } + else { + UpdatePossibleNodes(dynamicTree, staticTree, static_node_impl, child); + DFSMatching(staticTree, dynamicTree, child); + } + } + if (possible_nodes) { + for (const node of possible_nodes) { + if (!dynamicTree.nodes2possiblesnode.has(node.unique_xpath)) { + dynamicTree.nodes2possiblesnode.set(node.unique_xpath, new Set()); + } + dynamicTree.nodes2possiblesnode.get(node.unique_xpath)?.add(static_node_impl); + //console.log("动态 xpath: ",node.xpath, " ----> 静态 xpath: ", static_node_impl.xpath); + //console.log("uniuqe_xpath: ",node.unique_xpath); + } + } +} +function UpdatePossibleNodes(dynamicTree, staticTree, static_node, child) { + if (!staticTree.nodes2possiblesnode.has(static_node.unique_xpath)) { + return; + } + //console.log("更新静态 xpath:",static_node.xpath+" 下一个的: ",child.name); + const parent_possible_nodes = staticTree.nodes2possiblesnode.get(static_node.unique_xpath); + if (parent_possible_nodes) { + parent_possible_nodes.forEach((node) => { + // console.log("想要查询动态 xpath: ",node.xpath," 下一个的 ",child.name); + // console.log(`[UpdatePossibleNodes] node: (${node.xpath}) nearest_type_node_map_downward:`); + // node.nearest_type_node_map_downward.forEach((nodeSet, type) => { + // const names = Array.from(nodeSet).map(n => n.name + '(' + n.xpath + ')').join(', '); + // console.log(` type: ${type}, nodes: [${names}]`); + // }); + if (node.nearest_type_node_map_downward.has(child.name)) { + const matching_nodes = node.nearest_type_node_map_downward.get(child.name); + if (matching_nodes) { + matching_nodes.forEach((matchedNode) => { + if (!staticTree.nodes2possiblesnode.has(child.unique_xpath)) { + staticTree.nodes2possiblesnode.set(child.unique_xpath, new Set()); + } + staticTree.nodes2possiblesnode.get(child.unique_xpath)?.add(matchedNode); + //console.log("静态xpath: ",child.xpath," 添加候选节点: ",matchedNode.xpath); + }); + } + } + }); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$j = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'ArkMethodBuilder'); +function buildDefaultArkMethodFromArkClass(declaringClass, mtd, sourceFile, node) { + mtd.setDeclaringArkClass(declaringClass); + const methodSubSignature = ArkSignatureBuilder.buildMethodSubSignatureFromMethodName(DEFAULT_ARK_METHOD_NAME, true); + const methodSignature = new MethodSignature(mtd.getDeclaringArkClass().getSignature(), methodSubSignature); + mtd.setImplementationSignature(methodSignature); + mtd.setLineCol(0); + const defaultMethodNode = node ? node : sourceFile; + let bodyBuilder = new BodyBuilder(mtd.getSignature(), defaultMethodNode, mtd, sourceFile); + mtd.setBodyBuilder(bodyBuilder); +} +function buildArkMethodFromArkClass(methodNode, declaringClass, mtd, sourceFile, declaringMethod) { + mtd.setDeclaringArkClass(declaringClass); + declaringMethod !== undefined && mtd.setOuterMethod(declaringMethod); + ts.isFunctionDeclaration(methodNode) && mtd.setAsteriskToken(methodNode.asteriskToken !== undefined); + // All MethodLikeNode except FunctionTypeNode have questionToken. + !ts.isFunctionTypeNode(methodNode) && mtd.setQuestionToken(methodNode.questionToken !== undefined); + mtd.setCode(methodNode.getText(sourceFile)); + mtd.setModifiers(buildModifiers(methodNode)); + mtd.setDecorators(buildDecorators(methodNode, sourceFile)); + if (methodNode.typeParameters) { + mtd.setGenericTypes(buildTypeParameters(methodNode.typeParameters, sourceFile, mtd)); + } + // build methodDeclareSignatures and methodSignature as well as corresponding positions + const methodName = buildMethodName(methodNode, declaringClass, sourceFile, declaringMethod); + const methodParameters = []; + buildParameters(methodNode.parameters, mtd, sourceFile).forEach(parameter => { + buildGenericType(parameter.getType(), mtd); + methodParameters.push(parameter); + }); + let returnType = UnknownType.getInstance(); + if (methodNode.type) { + returnType = buildGenericType(buildReturnType(methodNode.type, sourceFile, mtd), mtd); + } + const methodSubSignature = new MethodSubSignature(methodName, methodParameters, returnType, mtd.isStatic()); + const methodSignature = new MethodSignature(mtd.getDeclaringArkClass().getSignature(), methodSubSignature); + const { line, character } = ts.getLineAndCharacterOfPosition(sourceFile, methodNode.getStart(sourceFile)); + if (isMethodImplementation(methodNode)) { + mtd.setImplementationSignature(methodSignature); + mtd.setLine(line + 1); + mtd.setColumn(character + 1); + } + else { + mtd.setDeclareSignatures(methodSignature); + mtd.setDeclareLinesAndCols([line + 1], [character + 1]); + } + let bodyBuilder = new BodyBuilder(mtd.getSignature(), methodNode, mtd, sourceFile); + mtd.setBodyBuilder(bodyBuilder); + if (mtd.hasBuilderDecorator()) { + mtd.setViewTree(buildViewTree(mtd)); + mtd.setArkUIViewTree(buildArkUIViewTree(mtd)); + } + else if (declaringClass.hasComponentDecorator() && mtd.getSubSignature().toString() === 'build()' && !mtd.isStatic()) { + declaringClass.setViewTree(buildViewTree(mtd)); + declaringClass.setArkUIViewTree(buildArkUIViewTree(mtd)); + } + checkAndUpdateMethod(mtd, declaringClass); + declaringClass.addMethod(mtd); + IRUtils.setComments(mtd, methodNode, sourceFile, mtd.getDeclaringArkFile().getScene().getOptions()); +} +function buildMethodName(node, declaringClass, sourceFile, declaringMethod) { + let name = ''; + if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) { + name = node.name ? node.name.text : buildAnonymousMethodName(node, declaringClass); + } + else if (ts.isFunctionTypeNode(node)) { + //TODO: check name type + name = node.name ? node.name.getText(sourceFile) : buildAnonymousMethodName(node, declaringClass); + } + else if (ts.isMethodDeclaration(node) || ts.isMethodSignature(node)) { + if (ts.isIdentifier(node.name)) { + name = node.name.text; + } + else if (ts.isComputedPropertyName(node.name)) { + if (ts.isIdentifier(node.name.expression)) { + name = node.name.expression.text; + } + else if (ts.isPropertyAccessExpression(node.name.expression)) { + name = handlePropertyAccessExpression(node.name.expression); + } + else { + logger$j.warn('Other method ComputedPropertyName found!'); + } + } + else { + logger$j.warn('Other method declaration type found!'); + } + } + //TODO, hard code + else if (ts.isConstructorDeclaration(node)) { + name = CONSTRUCTOR_NAME; + } + else if (ts.isConstructSignatureDeclaration(node)) { + name = 'construct-signature'; + } + else if (ts.isCallSignatureDeclaration(node)) { + name = CALL_SIGNATURE_NAME; + } + else if (ts.isGetAccessor(node) && ts.isIdentifier(node.name)) { + name = 'Get-' + node.name.text; + } + else if (ts.isSetAccessor(node) && ts.isIdentifier(node.name)) { + name = 'Set-' + node.name.text; + } + else if (ts.isArrowFunction(node)) { + name = buildAnonymousMethodName(node, declaringClass); + } + if (declaringMethod !== undefined && !declaringMethod.isDefaultArkMethod()) { + name = buildNestedMethodName(name, declaringMethod.getName()); + } + return name; +} +function buildAnonymousMethodName(node, declaringClass) { + return `${ANONYMOUS_METHOD_PREFIX}${declaringClass.getAnonymousMethodNumber()}`; +} +function buildNestedMethodName(originName, declaringMethodName) { + if (originName.startsWith(NAME_PREFIX)) { + return `${originName}${NAME_DELIMITER}${declaringMethodName}`; + } + return `${NAME_PREFIX}${originName}${NAME_DELIMITER}${declaringMethodName}`; +} +class ObjectBindingPatternParameter { + propertyName = ''; + name = ''; + optional = false; + constructor() { } + getName() { + return this.name; + } + setName(name) { + this.name = name; + } + getPropertyName() { + return this.propertyName; + } + setPropertyName(propertyName) { + this.propertyName = propertyName; + } + isOptional() { + return this.optional; + } + setOptional(optional) { + this.optional = optional; + } +} +class ArrayBindingPatternParameter { + propertyName = ''; + name = ''; + optional = false; + constructor() { } + getName() { + return this.name; + } + setName(name) { + this.name = name; + } + getPropertyName() { + return this.propertyName; + } + setPropertyName(propertyName) { + this.propertyName = propertyName; + } + isOptional() { + return this.optional; + } + setOptional(optional) { + this.optional = optional; + } +} +class MethodParameter { + name = ''; + type; + optional = false; + dotDotDotToken = false; + objElements = []; + arrayElements = []; + constructor() { } + getName() { + return this.name; + } + setName(name) { + this.name = name; + } + getType() { + return this.type; + } + setType(type) { + this.type = type; + } + isOptional() { + return this.optional; + } + setOptional(optional) { + this.optional = optional; + } + hasDotDotDotToken() { + return this.dotDotDotToken; + } + setDotDotDotToken(dotDotDotToken) { + this.dotDotDotToken = dotDotDotToken; + } + addObjElement(element) { + this.objElements.push(element); + } + getObjElements() { + return this.objElements; + } + setObjElements(objElements) { + this.objElements = objElements; + } + addArrayElement(element) { + this.arrayElements.push(element); + } + getArrayElements() { + return this.arrayElements; + } + setArrayElements(arrayElements) { + this.arrayElements = arrayElements; + } + getUses() { + return []; + } +} +function needDefaultConstructorInClass(arkClass) { + const originClassType = arkClass.getCategory(); + return (arkClass.getMethodWithName(CONSTRUCTOR_NAME) === null && + (originClassType === ClassCategory.CLASS || originClassType === ClassCategory.OBJECT) && + arkClass.getName() !== DEFAULT_ARK_CLASS_NAME && + !arkClass.isDeclare()); +} +function recursivelyCheckAndBuildSuperConstructor(arkClass) { + let superClass = arkClass.getSuperClass(); + while (superClass !== null) { + if (superClass.getMethodWithName(CONSTRUCTOR_NAME) === null) { + buildDefaultConstructor(superClass); + } + superClass = superClass.getSuperClass(); + } +} +function buildDefaultConstructor(arkClass) { + if (!needDefaultConstructorInClass(arkClass)) { + return false; + } + recursivelyCheckAndBuildSuperConstructor(arkClass); + const defaultConstructor = new ArkMethod(); + defaultConstructor.setDeclaringArkClass(arkClass); + defaultConstructor.setCode(''); + defaultConstructor.setIsGeneratedFlag(true); + defaultConstructor.setLineCol(0); + const thisLocal = new Local(THIS_NAME, new ClassType(arkClass.getSignature())); + const locals = new Set([thisLocal]); + const basicBlock = new BasicBlock(); + basicBlock.setId(0); + let parameters = []; + let parameterArgs = []; + const superConstructor = arkClass.getSuperClass()?.getMethodWithName(CONSTRUCTOR_NAME); + if (superConstructor) { + parameters = superConstructor.getParameters(); + for (let index = 0; index < parameters.length; index++) { + const parameterRef = new ArkParameterRef(index, parameters[index].getType()); + const parameterLocal = new Local(parameters[index].getName(), parameterRef.getType()); + locals.add(parameterLocal); + parameterArgs.push(parameterLocal); + basicBlock.addStmt(new ArkAssignStmt(parameterLocal, parameterRef)); + index++; + } + } + basicBlock.addStmt(new ArkAssignStmt(thisLocal, new ArkThisRef(new ClassType(arkClass.getSignature())))); + if (superConstructor) { + const superMethodSubSignature = new MethodSubSignature(SUPER_NAME, parameters, superConstructor.getReturnType()); + const superMethodSignature = new MethodSignature(arkClass.getSignature(), superMethodSubSignature); + const superInvokeExpr = new ArkStaticInvokeExpr(superMethodSignature, parameterArgs); + basicBlock.addStmt(new ArkInvokeStmt(superInvokeExpr)); + } + const methodSubSignature = new MethodSubSignature(CONSTRUCTOR_NAME, parameters, thisLocal.getType(), defaultConstructor.isStatic()); + defaultConstructor.setImplementationSignature(new MethodSignature(arkClass.getSignature(), methodSubSignature)); + basicBlock.addStmt(new ArkReturnStmt(thisLocal)); + const cfg = new Cfg(); + cfg.addBlock(basicBlock); + cfg.setStartingStmt(basicBlock.getHead()); + cfg.setDeclaringMethod(defaultConstructor); + cfg.getStmts().forEach(s => s.setCfg(cfg)); + defaultConstructor.setBody(new ArkBody(locals, cfg)); + checkAndUpdateMethod(defaultConstructor, arkClass); + arkClass.addMethod(defaultConstructor); + return true; +} +function buildInitMethod(initMethod, fieldInitializerStmts, thisLocal) { + const classType = new ClassType(initMethod.getDeclaringArkClass().getSignature()); + const assignStmt = new ArkAssignStmt(thisLocal, new ArkThisRef(classType)); + const block = new BasicBlock(); + block.setId(0); + block.addStmt(assignStmt); + const locals = new Set([thisLocal]); + for (const stmt of fieldInitializerStmts) { + block.addStmt(stmt); + if (stmt.getDef() && stmt.getDef() instanceof Local) { + locals.add(stmt.getDef()); + } + } + block.addStmt(new ArkReturnVoidStmt()); + const cfg = new Cfg(); + cfg.addBlock(block); + for (const stmt of block.getStmts()) { + stmt.setCfg(cfg); + } + cfg.setStartingStmt(assignStmt); + cfg.buildDefUseStmt(locals); + cfg.setDeclaringMethod(initMethod); + initMethod.setBody(new ArkBody(locals, cfg)); +} +function addInitInConstructor(constructor) { + const thisLocal = constructor.getBody()?.getLocals().get(THIS_NAME); + if (!thisLocal) { + return; + } + const cfg = constructor.getCfg(); + if (cfg === undefined) { + return; + } + const blocks = cfg.getBlocks(); + const firstBlockStmts = [...blocks][0].getStmts(); + let index = 0; + for (let i = 0; i < firstBlockStmts.length; i++) { + const stmt = firstBlockStmts[i]; + if (stmt instanceof ArkInvokeStmt && stmt.getInvokeExpr().getMethodSignature().getMethodSubSignature().getMethodName() === SUPER_NAME) { + index++; + continue; + } + if (stmt instanceof ArkAssignStmt) { + const rightOp = stmt.getRightOp(); + if (rightOp instanceof ArkParameterRef || rightOp instanceof ArkThisRef || rightOp instanceof ClosureFieldRef) { + index++; + continue; + } + } + break; + } + const initInvokeStmt = new ArkInvokeStmt(new ArkInstanceInvokeExpr(thisLocal, constructor.getDeclaringArkClass().getInstanceInitMethod().getSignature(), [])); + initInvokeStmt.setCfg(cfg); + firstBlockStmts.splice(index, 0, initInvokeStmt); +} +function isMethodImplementation(node) { + if (ts.isFunctionDeclaration(node) || + ts.isMethodDeclaration(node) || + ts.isConstructorDeclaration(node) || + ts.isGetAccessorDeclaration(node) || + ts.isSetAccessorDeclaration(node) || + ts.isFunctionExpression(node) || + ts.isArrowFunction(node)) { + if (node.body !== undefined) { + return true; + } + } + return false; +} +function checkAndUpdateMethod(method, cls) { + let presentMethod; + if (method.isStatic()) { + presentMethod = cls.getStaticMethodWithName(method.getName()); + } + else { + presentMethod = cls.getMethodWithName(method.getName()); + } + if (presentMethod === null) { + return; + } + if (method.validate().errCode !== ArkErrorCode.OK || presentMethod.validate().errCode !== ArkErrorCode.OK) { + return; + } + const presentDeclareSignatures = presentMethod.getDeclareSignatures(); + const presentDeclareLineCols = presentMethod.getDeclareLineCols(); + const presentImplSignature = presentMethod.getImplementationSignature(); + const newDeclareSignature = method.getDeclareSignatures(); + const newDeclareLineCols = method.getDeclareLineCols(); + const newImplSignature = method.getImplementationSignature(); + if (presentDeclareSignatures !== null && presentImplSignature === null) { + if (newDeclareSignature === null || presentMethod.getDeclareSignatureIndex(newDeclareSignature[0]) >= 0) { + method.setDeclareSignatures(presentDeclareSignatures); + method.setDeclareLineCols(presentDeclareLineCols); + } + else { + method.setDeclareSignatures(presentDeclareSignatures.concat(newDeclareSignature)); + method.setDeclareLineCols(presentDeclareLineCols.concat(newDeclareLineCols)); + } + return; + } + if (presentDeclareSignatures === null && presentImplSignature !== null) { + if (newImplSignature === null) { + method.setImplementationSignature(presentImplSignature); + method.setLineCol(presentMethod.getLineCol()); + } + return; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$i = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'builderUtils'); +function handleQualifiedName(node) { + let right = node.right.text; + let left = ''; + if (node.left.kind === ts.SyntaxKind.Identifier) { + left = node.left.text; + } + else if (node.left.kind === ts.SyntaxKind.QualifiedName) { + left = handleQualifiedName(node.left); + } + let qualifiedName = left + '.' + right; + return qualifiedName; +} +function handlePropertyAccessExpression(node) { + let right = node.name.text; + let left = ''; + if (ts.SyntaxKind[node.expression.kind] === 'Identifier') { + left = node.expression.text; + } + else if (ts.isStringLiteral(node.expression)) { + left = node.expression.text; + } + else if (ts.isPropertyAccessExpression(node.expression)) { + left = handlePropertyAccessExpression(node.expression); + } + let propertyAccessExpressionName = left + '.' + right; + return propertyAccessExpressionName; +} +function buildDecorators(node, sourceFile) { + let decorators = new Set(); + ts.getAllDecorators(node).forEach(decoratorNode => { + let decorator = parseDecorator(decoratorNode); + if (decorator) { + decorator.setContent(decoratorNode.expression.getText(sourceFile)); + decorators.add(decorator); + } + }); + return decorators; +} +function parseDecorator(node) { + if (!node.expression) { + return undefined; + } + let expression = node.expression; + if (ts.isIdentifier(expression)) { + return new Decorator(expression.text); + } + if (!ts.isCallExpression(expression) || !ts.isIdentifier(expression.expression)) { + return undefined; + } + let decorator = new Decorator(expression.expression.text); + if (expression.arguments.length > 0) { + const arg = expression.arguments[0]; + if (ts.isArrowFunction(arg) && ts.isIdentifier(arg.body)) { + decorator.setParam(arg.body.text); + } + } + return decorator; +} +function buildModifiers(node) { + let modifiers = 0; + if (ts.canHaveModifiers(node)) { + ts.getModifiers(node)?.forEach(modifier => { + modifiers |= modifierKind2Enum(modifier.kind); + }); + } + return modifiers; +} +function buildHeritageClauses(heritageClauses) { + let heritageClausesMap = new Map(); + heritageClauses?.forEach(heritageClause => { + heritageClause.types.forEach(type => { + let heritageClauseName = ''; + if (type.typeArguments) { + heritageClauseName = type.getText(); + } + else if (ts.isIdentifier(type.expression)) { + heritageClauseName = type.expression.text; + } + else if (ts.isPropertyAccessExpression(type.expression)) { + heritageClauseName = handlePropertyAccessExpression(type.expression); + } + else { + heritageClauseName = type.getText(); + } + heritageClausesMap.set(heritageClauseName, ts.SyntaxKind[heritageClause.token]); + }); + }); + return heritageClausesMap; +} +function buildTypeParameters(typeParameters, sourceFile, arkInstance) { + const genericTypes = []; + let index = 0; + if (arkInstance instanceof ArkMethod) { + const len = arkInstance.getDeclaringArkClass().getGenericsTypes()?.length; + if (len) { + index = len; + } + } + typeParameters.forEach(typeParameter => { + const genericType = tsNode2Type(typeParameter, sourceFile, arkInstance); + if (genericType instanceof GenericType) { + genericType.setIndex(index++); + genericTypes.push(genericType); + } + if (typeParameter.modifiers) { + logger$i.warn('This typeparameter has modifiers.'); + } + if (typeParameter.expression) { + logger$i.warn('This typeparameter has expression.'); + } + }); + return genericTypes; +} +function buildObjectBindingPatternParam(methodParameter, paramNameNode) { + methodParameter.setName('ObjectBindingPattern'); + let elements = []; + paramNameNode.elements.forEach(element => { + let paraElement = new ObjectBindingPatternParameter(); + if (element.propertyName) { + if (ts.isIdentifier(element.propertyName)) { + paraElement.setPropertyName(element.propertyName.text); + } + else { + logger$i.warn('New propertyName of ObjectBindingPattern found, please contact developers to support this!'); + } + } + if (element.name) { + if (ts.isIdentifier(element.name)) { + paraElement.setName(element.name.text); + } + else { + logger$i.warn('New name of ObjectBindingPattern found, please contact developers to support this!'); + } + } + if (element.initializer) { + logger$i.warn('TODO: support ObjectBindingPattern initializer.'); + } + if (element.dotDotDotToken) { + paraElement.setOptional(true); + } + elements.push(paraElement); + }); + methodParameter.setObjElements(elements); +} +function buildBindingElementOfBindingPatternParam(element, paraElement) { + if (element.propertyName) { + if (ts.isIdentifier(element.propertyName)) { + paraElement.setPropertyName(element.propertyName.text); + } + else { + logger$i.warn('New propertyName of ArrayBindingPattern found, please contact developers to support this!'); + } + } + if (element.name) { + if (ts.isIdentifier(element.name)) { + paraElement.setName(element.name.text); + } + else { + logger$i.warn('New name of ArrayBindingPattern found, please contact developers to support this!'); + } + } + if (element.initializer) { + logger$i.warn('TODO: support ArrayBindingPattern initializer.'); + } + if (element.dotDotDotToken) { + paraElement.setOptional(true); + } +} +function buildArrayBindingPatternParam(methodParameter, paramNameNode) { + methodParameter.setName('ArrayBindingPattern'); + let elements = []; + paramNameNode.elements.forEach(element => { + let paraElement = new ArrayBindingPatternParameter(); + if (ts.isBindingElement(element)) { + buildBindingElementOfBindingPatternParam(element, paraElement); + } + else if (ts.isOmittedExpression(element)) { + logger$i.warn('TODO: support OmittedExpression for ArrayBindingPattern parameter name.'); + } + elements.push(paraElement); + }); + methodParameter.setArrayElements(elements); +} +function buildParameters(params, arkInstance, sourceFile) { + let parameters = []; + params.forEach(parameter => { + let methodParameter = new MethodParameter(); + // name + if (ts.isIdentifier(parameter.name)) { + methodParameter.setName(parameter.name.text); + } + else if (ts.isObjectBindingPattern(parameter.name)) { + buildObjectBindingPatternParam(methodParameter, parameter.name); + } + else if (ts.isArrayBindingPattern(parameter.name)) { + buildArrayBindingPatternParam(methodParameter, parameter.name); + } + else { + logger$i.warn('Parameter name is not identifier, ObjectBindingPattern nor ArrayBindingPattern, please contact developers to support this!'); + } + // questionToken + if (parameter.questionToken) { + methodParameter.setOptional(true); + } + // type + if (parameter.type) { + methodParameter.setType(buildGenericType(tsNode2Type(parameter.type, sourceFile, arkInstance), arkInstance)); + } + else { + methodParameter.setType(UnknownType.getInstance()); + } + // initializer + if (parameter.initializer) ; + // dotDotDotToken + if (parameter.dotDotDotToken) { + methodParameter.setDotDotDotToken(true); + } + // modifiers + if (parameter.modifiers) ; + parameters.push(methodParameter); + }); + return parameters; +} +function buildGenericType(type, arkInstance) { + function replace(urType) { + const typeName = urType.getName(); + let gType; + if (arkInstance instanceof AliasType) { + gType = arkInstance.getGenericTypes()?.find(f => f.getName() === typeName); + } + else { + if (arkInstance instanceof ArkMethod) { + gType = arkInstance.getGenericTypes()?.find(f => f.getName() === typeName); + } + if (!gType) { + gType = arkInstance + .getDeclaringArkClass() + .getGenericsTypes() + ?.find(f => f.getName() === typeName); + } + } + if (gType) { + return gType; + } + const types = urType.getGenericTypes(); + for (let i = 0; i < types.length; i++) { + const mayType = types[i]; + if (mayType instanceof UnclearReferenceType) { + types[i] = replace(mayType); + } + } + return urType; + } + if (type instanceof UnclearReferenceType) { + return replace(type); + } + else if (type instanceof ClassType && arkInstance instanceof AliasType) { + type.setRealGenericTypes(arkInstance.getGenericTypes()); + } + else if (type instanceof UnionType || type instanceof TupleType) { + const types = type.getTypes(); + for (let i = 0; i < types.length; i++) { + const mayType = types[i]; + if (mayType instanceof UnclearReferenceType) { + types[i] = replace(mayType); + } + } + } + else if (type instanceof ArrayType) { + const baseType = type.getBaseType(); + if (baseType instanceof UnclearReferenceType) { + type.setBaseType(replace(baseType)); + } + } + else if (type instanceof FunctionType) { + const returnType = type.getMethodSignature().getType(); + if (returnType instanceof UnclearReferenceType) { + type.getMethodSignature().getMethodSubSignature().setReturnType(replace(returnType)); + } + } + return type; +} +function buildReturnType(node, sourceFile, method) { + if (node) { + return tsNode2Type(node, sourceFile, method); + } + else { + return UnknownType.getInstance(); + } +} +function tsNode2Type(typeNode, sourceFile, arkInstance) { + if (ts.isTypeReferenceNode(typeNode)) { + const genericTypes = []; + if (typeNode.typeArguments) { + for (const typeArgument of typeNode.typeArguments) { + genericTypes.push(tsNode2Type(typeArgument, sourceFile, arkInstance)); + } + } + let referenceNodeName = typeNode.typeName; + if (ts.isQualifiedName(referenceNodeName)) { + let parameterTypeStr = handleQualifiedName(referenceNodeName); + return new UnclearReferenceType(parameterTypeStr, genericTypes); + } + else { + let parameterTypeStr = referenceNodeName.text; + if (parameterTypeStr === Builtin.OBJECT) { + return Builtin.OBJECT_CLASS_TYPE; + } + return new UnclearReferenceType(parameterTypeStr, genericTypes); + } + } + else if (ts.isUnionTypeNode(typeNode) || ts.isIntersectionTypeNode(typeNode)) { + let multipleTypePara = []; + typeNode.types.forEach(tmpType => { + multipleTypePara.push(tsNode2Type(tmpType, sourceFile, arkInstance)); + }); + if (ts.isUnionTypeNode(typeNode)) { + return new UnionType(multipleTypePara); + } + else { + return new IntersectionType(multipleTypePara); + } + } + else if (ts.isLiteralTypeNode(typeNode)) { + return ArkValueTransformer.resolveLiteralTypeNode(typeNode, sourceFile); + } + else if (ts.isTypeLiteralNode(typeNode)) { + let cls = new ArkClass(); + let declaringClass; + if (arkInstance instanceof ArkMethod) { + declaringClass = arkInstance.getDeclaringArkClass(); + } + else if (arkInstance instanceof ArkField) { + declaringClass = arkInstance.getDeclaringArkClass(); + } + else { + declaringClass = arkInstance; + } + if (declaringClass.getDeclaringArkNamespace()) { + cls.setDeclaringArkNamespace(declaringClass.getDeclaringArkNamespace()); + cls.setDeclaringArkFile(declaringClass.getDeclaringArkFile()); + } + else { + cls.setDeclaringArkFile(declaringClass.getDeclaringArkFile()); + } + buildNormalArkClassFromArkMethod(typeNode, cls, sourceFile); + return new ClassType(cls.getSignature()); + } + else if (ts.isFunctionTypeNode(typeNode)) { + let mtd = new ArkMethod(); + let cls; + if (arkInstance instanceof ArkMethod) { + cls = arkInstance.getDeclaringArkClass(); + } + else if (arkInstance instanceof ArkClass) { + cls = arkInstance; + } + else { + cls = arkInstance.getDeclaringArkClass(); + } + buildArkMethodFromArkClass(typeNode, cls, mtd, sourceFile); + return new FunctionType(mtd.getSignature()); + } + else if (ts.isTypeParameterDeclaration(typeNode)) { + const name = typeNode.name.text; + let defaultType; + if (typeNode.default) { + defaultType = tsNode2Type(typeNode.default, sourceFile, arkInstance); + } + let constraint; + if (typeNode.constraint) { + constraint = tsNode2Type(typeNode.constraint, sourceFile, arkInstance); + } + return new GenericType(name, defaultType, constraint); + } + else if (ts.isTupleTypeNode(typeNode)) { + const types = []; + typeNode.elements.forEach(element => { + types.push(tsNode2Type(element, sourceFile, arkInstance)); + }); + return new TupleType(types); + } + else if (ts.isArrayTypeNode(typeNode)) { + return new ArrayType(tsNode2Type(typeNode.elementType, sourceFile, arkInstance), 1); + } + else if (ts.isParenthesizedTypeNode(typeNode)) { + return tsNode2Type(typeNode.type, sourceFile, arkInstance); + } + else if (ts.isTypeOperatorNode(typeNode)) { + return buildTypeFromTypeOperator(typeNode, sourceFile, arkInstance); + } + else if (ts.isTypeQueryNode(typeNode)) { + return buildTypeFromTypeQuery(typeNode, sourceFile, arkInstance); + } + else if (typeNode.kind === ts.SyntaxKind.ObjectKeyword) { + // TODO: type object which is different from Object is needed to support, such as let a: object = {} + return new UnclearReferenceType('object'); + } + else { + return buildTypeFromPreStr(ts.SyntaxKind[typeNode.kind]); + } +} +function buildTypeFromPreStr(preStr) { + let postStr = ''; + switch (preStr) { + case 'BooleanKeyword': + postStr = BOOLEAN_KEYWORD; + break; + case 'FalseKeyword': + postStr = BOOLEAN_KEYWORD; + break; + case 'TrueKeyword': + postStr = BOOLEAN_KEYWORD; + break; + case 'NumberKeyword': + postStr = NUMBER_KEYWORD; + break; + case 'NumericLiteral': + postStr = NUMBER_KEYWORD; + break; + case 'FirstLiteralToken': + postStr = NUMBER_KEYWORD; + break; + case 'StringKeyword': + postStr = STRING_KEYWORD; + break; + case 'StringLiteral': + postStr = STRING_KEYWORD; + break; + case 'UndefinedKeyword': + postStr = UNDEFINED_KEYWORD; + break; + case 'NullKeyword': + postStr = NULL_KEYWORD; + break; + case 'AnyKeyword': + postStr = ANY_KEYWORD; + break; + case 'VoidKeyword': + postStr = VOID_KEYWORD; + break; + case 'NeverKeyword': + postStr = NEVER_KEYWORD; + break; + case 'BigIntKeyword': + postStr = BIGINT_KEYWORD; + break; + default: + postStr = preStr; + } + return TypeInference.buildTypeFromStr(postStr); +} +function buildTypeFromTypeOperator(typeOperatorNode, sourceFile, arkInstance) { + const typeNode = typeOperatorNode.type; + let type = tsNode2Type(typeNode, sourceFile, arkInstance); + switch (typeOperatorNode.operator) { + case ts.SyntaxKind.ReadonlyKeyword: { + if (type instanceof ArrayType || type instanceof TupleType) { + type.setReadonlyFlag(true); + } + return type; + } + case ts.SyntaxKind.KeyOfKeyword: + return new KeyofTypeExpr(type); + case ts.SyntaxKind.UniqueKeyword: + return UnknownType.getInstance(); + default: + return UnknownType.getInstance(); + } +} +function buildTypeFromTypeQuery(typeQueryNode, sourceFile, arkInstance) { + const exprNameNode = typeQueryNode.exprName; + let opValue; + if (ts.isQualifiedName(exprNameNode)) { + if (exprNameNode.left.getText(sourceFile) === THIS_NAME) { + const fieldName = exprNameNode.right.getText(sourceFile); + if (arkInstance instanceof ArkMethod) { + const fieldSignature = arkInstance.getDeclaringArkClass().getFieldWithName(fieldName)?.getSignature() ?? + ArkSignatureBuilder.buildFieldSignatureFromFieldName(fieldName); + const baseLocal = arkInstance.getBody()?.getLocals().get(THIS_NAME) ?? + new Local(THIS_NAME, new ClassType(arkInstance.getDeclaringArkClass().getSignature(), arkInstance.getDeclaringArkClass().getGenericsTypes())); + opValue = new ArkInstanceFieldRef(baseLocal, fieldSignature); + } + else if (arkInstance instanceof ArkClass) { + const fieldSignature = arkInstance.getFieldWithName(fieldName)?.getSignature() ?? ArkSignatureBuilder.buildFieldSignatureFromFieldName(fieldName); + const baseLocal = new Local(THIS_NAME, new ClassType(arkInstance.getSignature(), arkInstance.getGenericsTypes())); + opValue = new ArkInstanceFieldRef(baseLocal, fieldSignature); + } + else { + const fieldSignature = arkInstance.getSignature(); + const baseLocal = new Local(THIS_NAME, new ClassType(arkInstance.getDeclaringArkClass().getSignature(), arkInstance.getDeclaringArkClass().getGenericsTypes())); + opValue = new ArkInstanceFieldRef(baseLocal, fieldSignature); + } + } + else { + const exprName = exprNameNode.getText(sourceFile); + opValue = new Local(exprName, UnknownType.getInstance()); + } + } + else { + const exprName = exprNameNode.escapedText.toString(); + opValue = new Local(exprName, UnknownType.getInstance()); + } + let expr = new TypeQueryExpr(opValue); + if (typeQueryNode.typeArguments) { + for (const typeArgument of typeQueryNode.typeArguments) { + expr.addGenericType(tsNode2Type(typeArgument, sourceFile, arkInstance)); + } + } + return expr; +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +function buildExportInfo(arkInstance, arkFile, line) { + let exportClauseName; + if (arkInstance instanceof ArkBaseModel && arkInstance.isDefault()) { + exportClauseName = DEFAULT; + } + else { + exportClauseName = arkInstance.getName(); + } + return new ExportInfo.Builder() + .exportClauseName(exportClauseName) + .exportClauseType(arkInstance.getExportType()) + .modifiers(arkInstance.getModifiers()) + .arkExport(arkInstance) + .originTsPosition(line) + .declaringArkFile(arkFile) + .build(); +} +function buildDefaultExportInfo(im, file, arkExport) { + return new ExportInfo.Builder() + .exportClauseType(arkExport?.getExportType() ?? ExportType.CLASS) + .exportClauseName(im.getOriginName()) + .declaringArkFile(file) + .arkExport(arkExport ?? file.getDefaultClass()) + .build(); +} +function buildExportDeclaration(node, sourceFile, arkFile) { + const originTsPosition = LineColPosition.buildFromNode(node, sourceFile); + const tsSourceCode = node.getText(sourceFile); + const modifiers = node.modifiers ? buildModifiers(node) : 0; + let exportFrom = ''; + if (node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) { + exportFrom = node.moduleSpecifier.text; + } + let exportInfos = []; + // just like: export {xxx as x} from './yy' + if (node.exportClause && ts.isNamedExports(node.exportClause) && node.exportClause.elements) { + node.exportClause.elements.forEach(element => { + let builder = new ExportInfo.Builder() + .exportClauseType(ExportType.UNKNOWN) + .exportClauseName(element.name.text) + .tsSourceCode(tsSourceCode) + .exportFrom(exportFrom) + .originTsPosition(originTsPosition) + .declaringArkFile(arkFile) + .setLeadingComments(IRUtils.getCommentsMetadata(node, sourceFile, arkFile.getScene().getOptions(), true)) + .setTrailingComments(IRUtils.getCommentsMetadata(node, sourceFile, arkFile.getScene().getOptions(), false)) + .modifiers(modifiers); + if (element.propertyName && ts.isIdentifier(element.propertyName)) { + builder.nameBeforeAs(element.propertyName.text); + } + exportInfos.push(builder.build()); + }); + return exportInfos; + } + let builder1 = new ExportInfo.Builder() + .exportClauseType(ExportType.UNKNOWN) + .nameBeforeAs(ALL) + .modifiers(modifiers) + .tsSourceCode(tsSourceCode) + .exportFrom(exportFrom) + .declaringArkFile(arkFile) + .setLeadingComments(IRUtils.getCommentsMetadata(node, sourceFile, arkFile.getScene().getOptions(), true)) + .setTrailingComments(IRUtils.getCommentsMetadata(node, sourceFile, arkFile.getScene().getOptions(), false)) + .originTsPosition(originTsPosition); + if (node.exportClause && ts.isNamespaceExport(node.exportClause) && ts.isIdentifier(node.exportClause.name)) { + // just like: export * as xx from './yy' + exportInfos.push(builder1.exportClauseName(node.exportClause.name.text).build()); + } + else if (!node.exportClause && node.moduleSpecifier) { + // just like: export * from './yy' + exportInfos.push(builder1.exportClauseName(ALL).build()); + } + return exportInfos; +} +function buildExportAssignment(node, sourceFile, arkFile) { + let exportInfos = []; + if (!node.expression) { + return exportInfos; + } + const originTsPosition = LineColPosition.buildFromNode(node, sourceFile); + const tsSourceCode = node.getText(sourceFile); + let modifiers = buildModifiers(node); + if (isKeyword(node.getChildren(sourceFile), ts.SyntaxKind.DefaultKeyword) || node.isExportEquals) { + modifiers |= ModifierType.DEFAULT; + } + let exportInfo = new ExportInfo.Builder() + .exportClauseType(ExportType.UNKNOWN) + .modifiers(modifiers) + .tsSourceCode(tsSourceCode) + .originTsPosition(originTsPosition) + .declaringArkFile(arkFile) + .exportClauseName(DEFAULT) + .setLeadingComments(IRUtils.getCommentsMetadata(node, sourceFile, arkFile.getScene().getOptions(), true)) + .setTrailingComments(IRUtils.getCommentsMetadata(node, sourceFile, arkFile.getScene().getOptions(), false)); + if (ts.isNewExpression(node.expression) && ts.isClassExpression(node.expression.expression)) { + let cls = new ArkClass(); + buildNormalArkClassFromArkFile(node.expression.expression, arkFile, cls, sourceFile); + } + if (ts.isIdentifier(node.expression)) { + // just like: export default xx + exportInfo.nameBeforeAs(node.expression.text); + } + else if (ts.isAsExpression(node.expression)) { + // just like: export default xx as YY + exportInfo.nameBeforeAs(node.expression.expression.getText(sourceFile)); + } + exportInfos.push(exportInfo.build()); + return exportInfos; +} +/** + * export const c = '', b = 1; + * @param node + * @param sourceFile + * @param arkFile + */ +function buildExportVariableStatement(node, sourceFile, arkFile, namespace) { + let exportInfos = []; + const originTsPosition = LineColPosition.buildFromNode(node, sourceFile); + const modifiers = node.modifiers ? buildModifiers(node) : 0; + const tsSourceCode = node.getText(sourceFile); + node.declarationList.declarations.forEach(dec => { + const exportInfoBuilder = new ExportInfo.Builder() + .exportClauseName(dec.name.getText(sourceFile)) + .exportClauseType(ExportType.LOCAL) + .modifiers(modifiers) + .tsSourceCode(tsSourceCode) + .originTsPosition(originTsPosition) + .declaringArkFile(arkFile); + if (namespace) { + exportInfoBuilder.declaringArkNamespace(namespace); + } + exportInfos.push(exportInfoBuilder.build()); + }); + return exportInfos; +} +/** + * export type MyType = string; + * @param node + * @param sourceFile + * @param arkFile + */ +function buildExportTypeAliasDeclaration(node, sourceFile, arkFile) { + let exportInfos = []; + const originTsPosition = LineColPosition.buildFromNode(node, sourceFile); + const modifiers = node.modifiers ? buildModifiers(node) : 0; + const tsSourceCode = node.getText(sourceFile); + const exportInfo = new ExportInfo.Builder() + .exportClauseName(node.name.text) + .exportClauseType(ExportType.TYPE) + .tsSourceCode(tsSourceCode) + .modifiers(modifiers) + .originTsPosition(originTsPosition) + .declaringArkFile(arkFile) + .build(); + exportInfos.push(exportInfo); + return exportInfos; +} +function isExported(modifierArray) { + if (!modifierArray) { + return false; + } + for (let child of modifierArray) { + if (child.kind === ts.SyntaxKind.ExportKeyword) { + return true; + } + } + return false; +} +function isKeyword(modifierArray, keyword) { + if (!modifierArray) { + return false; + } + for (let child of modifierArray) { + if (child.kind === keyword) { + return true; + } + } + return false; +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$h = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'SdkUtils'); +class SdkUtils { + static sdkImportMap = new Map(); + static buildSdkImportMap(file) { + const fileName = path$1.basename(file.getName()); + if (fileName.startsWith('@')) { + this.sdkImportMap.set(fileName.replace(/\.d\.e?ts$/, ''), file); + } + } + static getImportSdkFile(from) { + return this.sdkImportMap.get(from); + } + static buildGlobalMap(file, globalMap) { + const isGlobalPath = file + .getScene() + .getOptions() + .sdkGlobalFolders?.find(x => file.getFilePath().includes(path$1.sep + x + path$1.sep)); + if (!isGlobalPath) { + return; + } + ModelUtils.getAllClassesInFile(file).forEach(cls => { + if (!cls.isAnonymousClass() && !cls.isDefaultArkClass()) { + SdkUtils.loadClass(globalMap, cls); + } + if (cls.isDefaultArkClass()) { + cls.getMethods() + .filter(mtd => !mtd.isDefaultArkMethod() && !mtd.isAnonymousMethod()) + .forEach(mtd => globalMap.set(mtd.getName(), mtd)); + } + }); + const defaultArkMethod = file.getDefaultClass().getDefaultArkMethod(); + if (defaultArkMethod) { + TypeInference.inferTypeInMethod(defaultArkMethod); + } + defaultArkMethod + ?.getBody() + ?.getLocals() + .forEach(local => { + const name = local.getName(); + if (name !== THIS_NAME && !name.startsWith(TEMP_LOCAL_PREFIX)) { + this.loadGlobalLocal(local, defaultArkMethod, globalMap); + } + }); + defaultArkMethod + ?.getBody() + ?.getAliasTypeMap() + ?.forEach(a => globalMap.set(a[0].getName(), a[0])); + ModelUtils.getAllNamespacesInFile(file).forEach(ns => globalMap.set(ns.getName(), ns)); + } + static loadClass(globalMap, cls) { + const old = globalMap.get(cls.getName()); + if (old instanceof ArkClass && old.getDeclaringArkFile().getProjectName() === cls.getDeclaringArkFile().getProjectName()) { + if (old.getCategory() === ClassCategory.CLASS) { + this.copyMembers(cls, old); + } + else { + this.copyMembers(old, cls); + globalMap.delete(cls.getName()); + globalMap.set(cls.getName(), cls); + } + } + else { + if (old) { + logger$h.error(`${old.getSignature()} is override`); + } + globalMap.set(cls.getName(), cls); + } + } + static loadGlobalLocal(local, defaultArkMethod, globalMap) { + const name = local.getName(); + local.setSignature(new LocalSignature(name, defaultArkMethod.getSignature())); + const scene = defaultArkMethod.getDeclaringArkFile().getScene(); + if (scene.getOptions().isScanAbc) { + const instance = globalMap.get(name + 'Interface'); + const attr = globalMap.get(name + COMPONENT_ATTRIBUTE); + if (attr instanceof ArkClass && instance instanceof ArkClass) { + this.copyMembers(instance, attr); + globalMap.set(name, attr); + return; + } + } + const old = globalMap.get(name); + if (!old) { + globalMap.set(name, local); + } + else if (old instanceof ArkClass && local.getType() instanceof ClassType) { + const localConstructor = scene.getClass(local.getType().getClassSignature()); + if (localConstructor) { + this.copyMembers(localConstructor, old); + } + } + } + static copyMembers(from, to) { + from.getMethods().forEach(method => { + const dist = method.isStatic() ? to.getStaticMethodWithName(method.getName()) : to.getMethodWithName(method.getName()); + const distSignatures = dist?.getDeclareSignatures(); + if (distSignatures) { + method.getDeclareSignatures()?.forEach(x => distSignatures.push(x)); + } + else { + to.addMethod(method); + } + }); + from.getFields().forEach(field => { + const dist = field.isStatic() ? to.getStaticFieldWithName(field.getName()) : to.getFieldWithName(field.getName()); + if (!dist) { + to.addField(field); + } + }); + } + static computeGlobalThis(leftOp, arkMethod) { + const globalThis = arkMethod.getDeclaringArkFile().getScene().getSdkGlobal(GLOBAL_THIS_NAME); + if (globalThis instanceof ArkNamespace) { + const exportInfo = new ExportInfo.Builder() + .exportClauseName(leftOp.getFieldName()) + .arkExport(new Local(leftOp.getFieldName(), leftOp.getType())) + .build(); + globalThis.addExportInfo(exportInfo); + } + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class ModelUtils { + static implicitArkUIBuilderMethods = new Set(); + static getMethodSignatureFromArkClass(arkClass, methodName) { + for (const arkMethod of arkClass.getMethods()) { + if (arkMethod.getName() === methodName) { + return arkMethod.getSignature(); + } + } + return null; + } + static getClassWithNameInNamespaceRecursively(className, ns) { + if (className === '') { + return null; + } + let res = null; + res = ns.getClassWithName(className); + if (res == null) { + let declaringNs = ns.getDeclaringArkNamespace(); + if (declaringNs != null) { + res = this.getClassWithNameInNamespaceRecursively(className, declaringNs); + } + else { + res = this.getClassInFileWithName(className, ns.getDeclaringArkFile()); + } + } + return res; + } + static getClassWithNameFromClass(className, startFrom) { + if (!className.includes('.')) { + let res = null; + const arkNamespace = startFrom.getDeclaringArkNamespace(); + if (arkNamespace) { + res = this.getClassWithNameInNamespaceRecursively(className, arkNamespace); + } + else { + res = this.getClassInFileWithName(className, startFrom.getDeclaringArkFile()); + } + return res; + } + else { + const names = className.split('.'); + let nameSpace = this.getNamespaceWithNameFromClass(names[0], startFrom); + for (let i = 1; i < names.length - 1; i++) { + if (nameSpace) { + nameSpace = nameSpace.getNamespaceWithName(names[i]); + } + } + if (nameSpace) { + return nameSpace.getClassWithName(names[names.length - 1]); + } + } + return null; + } + /** + * search class within the file that contain the given method + */ + static getClassWithName(className, thisClass) { + if (thisClass.getName() === className) { + return thisClass; + } + let classSearched = thisClass.getDeclaringArkNamespace()?.getClassWithName(className); + if (!classSearched) { + classSearched = thisClass.getDeclaringArkFile().getClassWithName(className); + } + return classSearched; + } + /** search class within the given file */ + static getClassInFileWithName(className, arkFile) { + let classSearched = arkFile.getClassWithName(className); + if (classSearched != null) { + return classSearched; + } + return null; + } + static getClassInImportInfoWithName(className, arkFile) { + let arkExport = this.getArkExportInImportInfoWithName(className, arkFile); + if (arkExport instanceof ArkClass) { + return arkExport; + } + return null; + } + /** search type within the given file import infos */ + static getArkExportInImportInfoWithName(name, arkFile) { + return arkFile.getImportInfoBy(name)?.getLazyExportInfo()?.getArkExport() ?? null; + } + /** search method within the file that contain the given method */ + static getMethodWithName(methodName, startFrom) { + if (!methodName.includes('.')) { + if (startFrom.getName() === methodName) { + return startFrom; + } + const thisClass = startFrom.getDeclaringArkClass(); + let methodSearched = thisClass.getMethodWithName(methodName); + if (!methodSearched) { + methodSearched = thisClass.getStaticMethodWithName(methodName); + } + return methodSearched; + } + else { + const names = methodName.split('.'); + let nameSpace = this.getNamespaceWithName(names[0], startFrom.getDeclaringArkClass()); + for (let i = 1; i < names.length - 1; i++) { + if (nameSpace) { + nameSpace = nameSpace.getNamespaceWithName(names[i]); + } + } + if (nameSpace) { + return nameSpace.getDefaultClass().getMethodWithName(names[names.length - 1]); + } + } + return null; + } + static getNamespaceWithNameFromClass(namespaceName, startFrom) { + const thisNamespace = startFrom.getDeclaringArkNamespace(); + let namespaceSearched = null; + if (thisNamespace) { + namespaceSearched = thisNamespace.getNamespaceWithName(namespaceName); + if (namespaceSearched) { + return namespaceSearched; + } + } + const thisFile = startFrom.getDeclaringArkFile(); + namespaceSearched = this.getNamespaceInFileWithName(namespaceName, thisFile); + return namespaceSearched; + } + static getNamespaceWithName(namespaceName, thisClass) { + let thisNamespace = thisClass.getDeclaringArkNamespace(); + let namespaceSearched = null; + while (!namespaceSearched && thisNamespace) { + namespaceSearched = thisNamespace.getNamespaceWithName(namespaceName); + thisNamespace = thisNamespace.getDeclaringArkNamespace(); + } + if (!namespaceSearched) { + namespaceSearched = thisClass.getDeclaringArkFile().getNamespaceWithName(namespaceName); + } + return namespaceSearched; + } + static getNamespaceInFileWithName(namespaceName, arkFile) { + let namespaceSearched = arkFile.getNamespaceWithName(namespaceName); + if (namespaceSearched) { + return namespaceSearched; + } + return null; + } + static getNamespaceInImportInfoWithName(namespaceName, arkFile) { + let arkExport = this.getArkExportInImportInfoWithName(namespaceName, arkFile); + if (arkExport instanceof ArkNamespace) { + return arkExport; + } + return null; + } + static getStaticMethodWithName(methodName, thisClass) { + const thisNamespace = thisClass.getDeclaringArkNamespace(); + if (thisNamespace) { + const defaultClass = thisNamespace.getClassWithName(DEFAULT_ARK_CLASS_NAME); + if (defaultClass) { + const method = defaultClass.getMethodWithName(methodName); + if (method) { + return method; + } + } + } + return this.getStaticMethodInFileWithName(methodName, thisClass.getDeclaringArkFile()); + } + static getStaticMethodInFileWithName(methodName, arkFile) { + const defaultClass = arkFile.getClasses().find(cls => cls.getName() === DEFAULT_ARK_CLASS_NAME) || null; + if (defaultClass) { + let method = defaultClass.getMethodWithName(methodName); + if (method) { + return method; + } + } + return null; + } + static getStaticMethodInImportInfoWithName(methodName, arkFile) { + let arkExport = this.getArkExportInImportInfoWithName(methodName, arkFile); + if (arkExport instanceof ArkMethod) { + return arkExport; + } + return null; + } + static getLocalInImportInfoWithName(localName, arkFile) { + let arkExport = this.getArkExportInImportInfoWithName(localName, arkFile); + if (arkExport instanceof Local) { + return arkExport; + } + return null; + } + /* get nested namespaces in a file */ + static getAllNamespacesInFile(arkFile) { + const arkNamespaces = arkFile.getNamespaces(); + for (const arkNamespace of arkFile.getNamespaces()) { + this.getAllNamespacesInNamespace(arkNamespace, arkNamespaces); + } + return arkNamespaces; + } + /* get nested namespaces in a namespace */ + static getAllNamespacesInNamespace(arkNamespace, allNamespaces) { + allNamespaces.push(...arkNamespace.getNamespaces()); + for (const nestedNamespace of arkNamespace.getNamespaces()) { + this.getAllNamespacesInNamespace(nestedNamespace, allNamespaces); + } + } + static getAllClassesInFile(arkFile) { + const allClasses = arkFile.getClasses(); + this.getAllNamespacesInFile(arkFile).forEach(namespace => { + allClasses.push(...namespace.getClasses()); + }); + return allClasses; + } + static getAllMethodsInFile(arkFile) { + const allMethods = []; + this.getAllClassesInFile(arkFile).forEach(cls => { + allMethods.push(...cls.getMethods()); + }); + return allMethods; + } + static isArkUIBuilderMethod(arkMethod) { + let isArkUIBuilderMethod = arkMethod.hasBuilderDecorator() || this.implicitArkUIBuilderMethods.has(arkMethod); + if (!isArkUIBuilderMethod && arkMethod.getName() === 'build' && arkMethod.getDeclaringArkClass().hasComponentDecorator() && !arkMethod.isStatic()) { + const fileName = arkMethod.getDeclaringArkClass().getDeclaringArkFile().getName(); + if (fileName.endsWith('.ets')) { + isArkUIBuilderMethod = true; + } + } + return isArkUIBuilderMethod; + } + static getArkClassInBuild(scene, classType) { + const classSignature = classType.getClassSignature(); + const file = scene.getFile(classSignature.getDeclaringFileSignature()); + const namespaceSignature = classSignature.getDeclaringNamespaceSignature(); + if (namespaceSignature) { + return file?.getNamespace(namespaceSignature)?.getClass(classSignature) || null; + } + return file?.getClassWithName(classSignature.getClassName()) || null; + } + static getDefaultClass(arkClass) { + return arkClass.getDeclaringArkNamespace()?.getDefaultClass() ?? arkClass.getDeclaringArkFile().getDefaultClass(); + } + static getClass(method, signature) { + let cls = method.getDeclaringArkFile().getScene().getClass(signature); + if (cls) { + return cls; + } + let importInfo = method.getDeclaringArkFile().getImportInfoBy(signature.getClassName()); + let exportInfo = importInfo ? findExportInfo(importInfo) : null; + let arkExport = exportInfo?.getArkExport(); + if (arkExport instanceof ArkClass) { + return arkExport; + } + cls = method.getDeclaringArkClass().getDeclaringArkNamespace()?.getClassWithName(signature.getClassName()); + if (cls) { + return cls; + } + for (const ns of method.getDeclaringArkFile().getAllNamespacesUnderThisFile()) { + cls = ns.getClassWithName(signature.getClassName()); + if (cls) { + return cls; + } + } + return method.getDeclaringArkFile().getClassWithName(signature.getClassName()); + } + static findPropertyInNamespace(name, namespace) { + return (namespace.getDefaultClass()?.getMethodWithName(name) ?? + findArkExport(namespace.getExportInfoBy(name)) ?? + namespace.getClassWithName(name) ?? + namespace.getNamespaceWithName(name) ?? + namespace.getDefaultClass()?.getDefaultArkMethod()?.getBody()?.getAliasTypeByName(name) ?? + namespace.getDefaultClass()?.getDefaultArkMethod()?.getBody()?.getLocals()?.get(name)); + } + static findPropertyInClass(name, arkClass) { + let property = arkClass.getMethodWithName(name) ?? + arkClass.getStaticMethodWithName(name) ?? + arkClass.getFieldWithName(name) ?? + arkClass.getStaticFieldWithName(name); + if (property) { + return property; + } + if (arkClass.isDefaultArkClass()) { + return findArkExport(arkClass.getDeclaringArkFile().getExportInfoBy(name)); + } + for (const heritage of arkClass.getAllHeritageClasses()) { + property = this.findPropertyInClass(name, heritage); + if (property) { + return property; + } + } + return null; + } + static findDeclaredLocal(local, arkMethod, times = 0) { + if (arkMethod.getDeclaringArkFile().getScene().getOptions().isScanAbc) { + return null; + } + const name = local.getName(); + if (name === THIS_NAME || name.startsWith(TEMP_LOCAL_PREFIX)) { + return null; + } + const parameter = arkMethod.getParameters().find(p => p.getName() === name); + if (parameter) { + return new Local(parameter.getName(), parameter.getType()); + } + if (times > 0) { + const declaredLocal = arkMethod.getBody()?.getLocals().get(name); + if (declaredLocal && + declaredLocal.getDeclaringStmt() instanceof ArkAssignStmt && + !(declaredLocal.getDeclaringStmt().getRightOp() instanceof ClosureFieldRef)) { + return declaredLocal; + } + } + let parentName = arkMethod.getName(); + if (parentName === DEFAULT_ARK_METHOD_NAME) { + return null; + } + let invokeMethod = arkMethod.getOuterMethod(); + if (!invokeMethod) { + const className = arkMethod.getDeclaringArkClass().getName(); + const outerStart = className.indexOf(NAME_DELIMITER); + const outerEnd = className.lastIndexOf('.'); + if (outerStart > -1 && outerEnd > -1) { + invokeMethod = arkMethod + .getDeclaringArkFile() + .getClassWithName(className.substring(outerStart + 1, outerEnd)) + ?.getMethodWithName(className.substring(outerEnd + 1)); + } + else { + const cls = arkMethod.getDeclaringArkClass(); + invokeMethod = cls.getDefaultArkMethod() ?? cls.getDeclaringArkFile().getDefaultClass()?.getDefaultArkMethod(); + } + } + if (invokeMethod) { + return this.findDeclaredLocal(local, invokeMethod, ++times); + } + return null; + } + static findArkModel(baseName, arkClass) { + let arkModel = arkClass.getMethodWithName(baseName) ?? + arkClass.getStaticMethodWithName(baseName) ?? + arkClass.getFieldWithName(baseName) ?? + arkClass.getStaticFieldWithName(baseName); + if (arkModel) { + return arkModel; + } + arkModel = + ModelUtils.getDefaultClass(arkClass)?.getDefaultArkMethod()?.getBody()?.getLocals()?.get(baseName) ?? + ModelUtils.getClassWithName(baseName, arkClass) ?? + ModelUtils.getNamespaceWithName(baseName, arkClass) ?? + ModelUtils.getDefaultClass(arkClass)?.getMethodWithName(baseName) ?? + ModelUtils.getDefaultClass(arkClass)?.getDefaultArkMethod()?.getBody()?.getAliasTypeByName(baseName) ?? + ModelUtils.getArkExportInImportInfoWithName(baseName, arkClass.getDeclaringArkFile()); + if (!arkModel && !arkClass.getDeclaringArkFile().getImportInfoBy(baseName)) { + arkModel = arkClass.getDeclaringArkFile().getScene().getSdkGlobal(baseName); + } + return arkModel; + } + static findGlobalRef(refName, method) { + return (this.findDeclaredLocal(new Local(refName), method, 1) ?? + this.getArkExportInImportInfoWithName(refName, method.getDeclaringArkFile()) ?? + method.getDeclaringArkFile().getScene().getSdkGlobal(refName)); + } + static findArkModelByRefName(refName, arkClass) { + const singleNames = refName.split('.'); + let model = null; + for (let i = 0; i < singleNames.length; i++) { + if (model instanceof Local || model instanceof ArkField) { + const type = model.getType(); + if (type instanceof ClassType) { + model = arkClass.getDeclaringArkFile().getScene().getClass(type.getClassSignature()); + } + else if (type instanceof AnnotationNamespaceType) { + model = arkClass.getDeclaringArkFile().getScene().getNamespace(type.getNamespaceSignature()); + } + } + const name = singleNames[i].replace(/<(\w+)>/, EMPTY_STRING); + if (i === 0) { + model = this.findArkModel(name, arkClass); + } + else if (model instanceof ArkClass) { + model = this.findPropertyInClass(name, model); + } + else if (model instanceof ArkNamespace) { + model = this.findPropertyInNamespace(name, model); + } + if (!model) { + return null; + } + } + return model; + } + static findArkModelBySignature(signature, scene) { + if (signature instanceof ClassSignature) { + return scene.getClass(signature); + } + else if (signature instanceof NamespaceSignature) { + return scene.getNamespace(signature); + } + else if (signature instanceof MethodSignature) { + return scene.getMethod(signature); + } + else if (signature instanceof FieldSignature) { + const declare = this.findArkModelBySignature(signature.getDeclaringSignature(), scene); + if (declare instanceof ArkClass) { + return this.findPropertyInClass(signature.getFieldName(), declare); + } + else if (declare instanceof ArkNamespace) { + return this.findPropertyInNamespace(signature.getFieldName(), declare) || null; + } + return null; + } + else if (signature instanceof LocalSignature) { + const declare = scene.getMethod(signature.getDeclaringMethodSignature()); + return declare?.getBody()?.getLocals().get(signature.getName()) ?? declare?.getBody()?.getAliasTypeByName(signature.getName()) ?? null; + } + else if (signature instanceof AliasTypeSignature) { + const declare = scene.getMethod(signature.getDeclaringMethodSignature()); + return declare?.getBody()?.getAliasTypeByName(signature.getName()) ?? null; + } + return null; + } + static parseArkBaseModel2Type(arkBaseModel) { + if (arkBaseModel instanceof ArkClass) { + return new ClassType(arkBaseModel.getSignature(), arkBaseModel.getGenericsTypes()); + } + else if (arkBaseModel instanceof ArkNamespace) { + return AnnotationNamespaceType.getInstance(arkBaseModel.getSignature()); + } + else if (arkBaseModel instanceof ArkMethod) { + return new FunctionType(arkBaseModel.getSignature()); + } + else if (arkBaseModel instanceof ArkField) { + if (arkBaseModel.getType() instanceof UnknownType || arkBaseModel.getType() instanceof UnclearReferenceType) { + return null; + } + return arkBaseModel.getType(); + } + return null; + } +} +const logger$g = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'ModelUtils'); +let moduleMap; +/** + * find arkFile by from info + * export xx from '../xx' + * import xx from '@ohos/xx' + * import xx from '@ohos.xx' + * @param im importInfo or exportInfo + */ +function getArkFile(im) { + const from = im.getFrom(); + if (!from) { + return null; + } + if (/^([^@]*\/)([^\/]*)$/.test(from)) { + //relative path + const parentPath = /^\.{1,2}\//.test(from) ? path$1.dirname(im.getDeclaringArkFile().getFilePath()) : im.getDeclaringArkFile().getProjectDir(); + const originPath = path$1.resolve(parentPath, from); + return getArkFileFromScene(im, originPath); + } + else if (/^@[a-z|\-]+?\//.test(from)) { + //module path + const arkFile = getArkFileFromOtherModule(im); + if (arkFile) { + return arkFile; + } + } + //sdk path + const file = SdkUtils.getImportSdkFile(from); + if (file) { + return file; + } + const scene = im.getDeclaringArkFile().getScene(); + for (const sdk of scene.getProjectSdkMap().values()) { + const arkFile = getArkFileFormMap(sdk.name, processSdkPath(sdk, from), scene); + if (arkFile) { + return arkFile; + } + } + return null; +} +/** + * find from info's export + * @param fromInfo importInfo or exportInfo + */ +function findExportInfo(fromInfo) { + let file = getArkFile(fromInfo); + if (!file) { + logger$g.warn(`${fromInfo.getOriginName()} ${fromInfo.getFrom()} file not found: ${fromInfo.getDeclaringArkFile()?.getFileSignature()?.toString()}`); + return null; + } + if (fileSignatureCompare(file.getFileSignature(), fromInfo.getDeclaringArkFile().getFileSignature())) { + for (let exportInfo of file.getExportInfos()) { + if (exportInfo.getOriginName() === fromInfo.getOriginName()) { + exportInfo.setArkExport(file.getDefaultClass()); + return exportInfo; + } + } + return null; + } + let exportInfo = findExportInfoInfile(fromInfo, file) || null; + if (exportInfo === null) { + logger$g.warn('export info not found, ' + fromInfo.getFrom() + ' in file: ' + fromInfo.getDeclaringArkFile().getFileSignature().toString()); + return null; + } + const arkExport = findArkExport(exportInfo); + exportInfo.setArkExport(arkExport); + if (arkExport) { + exportInfo.setExportClauseType(arkExport.getExportType()); + } + return exportInfo; +} +function findArkExport(exportInfo) { + if (!exportInfo) { + return null; + } + let arkExport = exportInfo.getArkExport(); + if (arkExport || arkExport === null) { + return arkExport; + } + if (!exportInfo.getFrom()) { + const name = exportInfo.getOriginName(); + const defaultClass = exportInfo.getDeclaringArkNamespace()?.getDefaultClass() ?? exportInfo.getDeclaringArkFile().getDefaultClass(); + if (exportInfo.getExportClauseType() === ExportType.LOCAL) { + arkExport = defaultClass.getDefaultArkMethod()?.getBody()?.getExportLocalByName(name); + } + else if (exportInfo.getExportClauseType() === ExportType.TYPE) { + arkExport = defaultClass.getDefaultArkMethod()?.getBody()?.getAliasTypeByName(name); + } + else { + arkExport = findArkExportInFile(name, exportInfo.getDeclaringArkFile()); + } + } + else if (exportInfo.getExportClauseType() === ExportType.UNKNOWN) { + const result = findExportInfo(exportInfo); + if (result) { + arkExport = result.getArkExport() || null; + } + } + if (arkExport) { + exportInfo.setArkExport(arkExport); + } + else { + const file = exportInfo.getDeclaringArkFile().getFileSignature().toString(); + logger$g.warn(`${exportInfo.getExportClauseName()} get arkExport fail from ${exportInfo.getFrom()} at ${file}`); + } + return arkExport || null; +} +function findArkExportInFile(name, declaringArkFile) { + let arkExport = declaringArkFile.getNamespaceWithName(name) ?? + declaringArkFile.getDefaultClass().getDefaultArkMethod()?.getBody()?.getAliasTypeByName(name) ?? + declaringArkFile.getClassWithName(name) ?? + declaringArkFile.getDefaultClass().getMethodWithName(name) ?? + declaringArkFile.getDefaultClass().getDefaultArkMethod()?.getBody()?.getExportLocalByName(name); + if (!arkExport) { + const importInfo = declaringArkFile.getImportInfoBy(name); + if (importInfo) { + const result = findExportInfo(importInfo); + if (result) { + arkExport = result.getArkExport(); + } + } + } + return arkExport || null; +} +function processSdkPath(sdk, formPath) { + let originPath = path$1.join(sdk.path, formPath); + if (FileUtils.isDirectory(originPath)) { + formPath = path$1.join(formPath, FileUtils.getIndexFileName(originPath)); + } + return `${formPath}`; +} +function getArkFileFromScene(im, originPath) { + if (FileUtils.isDirectory(originPath)) { + originPath = path$1.join(originPath, FileUtils.getIndexFileName(originPath)); + } + const fileName = path$1.relative(im.getDeclaringArkFile().getProjectDir(), originPath); + const scene = im.getDeclaringArkFile().getScene(); + if (/\.e?ts$/.test(originPath)) { + const fromSignature = new FileSignature(im.getDeclaringArkFile().getProjectName(), fileName); + return scene.getFile(fromSignature); + } + const projectName = im.getDeclaringArkFile().getProjectName(); + return getArkFileFormMap(projectName, fileName, scene); +} +function getArkFileFormMap(projectName, filePath, scene) { + if (/\.e?ts$/.test(filePath)) { + return scene.getFile(new FileSignature(projectName, filePath)); + } + const fileSuffixArray = scene.getOptions().supportFileExts; + if (!fileSuffixArray) { + return null; + } + for (const suffix of fileSuffixArray) { + const arkFile = scene.getFile(new FileSignature(projectName, filePath + suffix)); + if (arkFile) { + return arkFile; + } + } + return null; +} +function findExportInfoInfile(fromInfo, file) { + const exportName = fromInfo.isDefault() ? DEFAULT : fromInfo.getOriginName(); + let exportInfo = file.getExportInfoBy(exportName); + if (exportInfo) { + return exportInfo; + } + if (exportName === DEFAULT) { + exportInfo = file.getExportInfos().find(p => p.isDefault()); + if (exportInfo) { + file.addExportInfo(exportInfo, DEFAULT); + return exportInfo; + } + } + if (fromInfo.getOriginName() === ALL) { + exportInfo = buildDefaultExportInfo(fromInfo, file); + file.addExportInfo(exportInfo, ALL); + } + else if (/\.d\.e?ts$/.test(file.getName())) { + let declare = exportName === DEFAULT ? undefined : findArkExportInFile(fromInfo.getOriginName(), file) || undefined; + exportInfo = buildDefaultExportInfo(fromInfo, file, declare); + } + return exportInfo; +} +function initModulePathMap(ohPkgContentMap) { + if (moduleMap) { + moduleMap.clear(); + } + moduleMap = FileUtils.generateModuleMap(ohPkgContentMap); +} +function getArkFileFromOtherModule(fromInfo) { + if (!moduleMap || moduleMap.size === 0) { + return undefined; + } + const from = fromInfo.getFrom(); + let index; + let file; + let modulePath; + //find file by given from like '@ohos/module/src/xxx' '@ohos/module/index' + if ((index = from.indexOf('src')) > 0 || (index = from.indexOf('Index')) > 0 || (index = from.indexOf('index')) > 0) { + modulePath = moduleMap.get(from.substring(0, index).replace(/\/*$/, '')); + file = findFileInModule(fromInfo, modulePath, from.substring(index)); + } + if (file) { + return file; + } + modulePath = modulePath ?? moduleMap.get(from); + if (!modulePath) { + return file; + } + //find file in module json main path + if (modulePath.main) { + file = getArkFileFromScene(fromInfo, modulePath.main); + } + //find file in module path Index.ts + if (!file && FileUtils.isDirectory(modulePath.path)) { + file = findFileInModule(fromInfo, modulePath, FileUtils.getIndexFileName(modulePath.path)); + } + //find file in module path/src/main/ets/TsIndex.ts + if (!file) { + file = findFileInModule(fromInfo, modulePath, '/src/main/ets/TsIndex.ts'); + } + return file; +} +function findFileInModule(fromInfo, modulePath, contentPath) { + if (!modulePath) { + return undefined; + } + const originPath = path$1.join(modulePath.path, contentPath); + let file; + if (originPath !== modulePath.main) { + file = getArkFileFromScene(fromInfo, originPath); + } + if (file && findExportInfoInfile(fromInfo, file)) { + return file; + } + return undefined; +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @category core/base + */ +class Local { + name; + type; + originalValue; + declaringStmt; + usedStmts; + signature; + constFlag; + constructor(name, type = UnknownType.getInstance()) { + this.name = name; + this.type = type; + this.originalValue = null; + this.declaringStmt = null; + this.usedStmts = []; + } + inferType(arkMethod) { + if (this.name === THIS_NAME && this.type instanceof UnknownType) { + const declaringArkClass = arkMethod.getDeclaringArkClass(); + this.type = new ClassType(declaringArkClass.getSignature(), declaringArkClass.getRealTypes()); + } + else if (TypeInference.isUnclearType(this.type)) { + const type = TypeInference.inferBaseType(this.name, arkMethod.getDeclaringArkClass()) ?? + ModelUtils.findDeclaredLocal(this, arkMethod)?.getType(); + if (type) { + this.type = type; + } + } + if (this.type instanceof FunctionType) { + this.type.getMethodSignature().getMethodSubSignature().getParameters() + .forEach(p => TypeInference.inferParameterType(p, arkMethod)); + TypeInference.inferSignatureReturnType(this.type.getMethodSignature(), arkMethod); + } + return this; + } + /** + * Returns the name of local value. + * @returns The name of local value. + * @example + * 1. get the name of local value. + + ```typescript + arkClass.getDefaultArkMethod()?.getBody().getLocals().forEach(local => { + const arkField = new ArkField(); + arkField.setFieldType(ArkField.DEFAULT_ARK_Field); + arkField.setDeclaringClass(defaultClass); + arkField.setType(local.getType()); + arkField.setName(local.getName()); + arkField.genSignature(); + defaultClass.addField(arkField); + }); + ``` + */ + getName() { + return this.name; + } + setName(name) { + this.name = name; + } + /** + * Returns the type of this local. + * @returns The type of this local. + */ + getType() { + return this.type; + } + setType(newType) { + this.type = newType; + } + getOriginalValue() { + return this.originalValue; + } + setOriginalValue(originalValue) { + this.originalValue = originalValue; + } + /** + * Returns the declaring statement, which may also be a **null**. + * For example, if the code snippet in a function is `let dd = cc + 5;` where `cc` is a **number** + * and `dd` is not defined before, then the declaring statemet of local `dd`: + * - its **string** text is "dd = cc + 5". + * - the **strings** of right operand and left operand are "cc + 5" and "dd", respectively. + * - three values are used in this statement: `cc + 5` (i.e., a normal binary operation expression), `cc` (a local), and `5` (a constant), respectively. + * @returns The declaring statement (maybe a **null**) of the local. + * @example + * 1. get the statement that defines the local for the first time. + + ```typescript + let stmt = local.getDeclaringStmt(); + if (stmt !== null) { + ... + } + ``` + */ + getDeclaringStmt() { + return this.declaringStmt; + } + setDeclaringStmt(declaringStmt) { + this.declaringStmt = declaringStmt; + } + /** + * Returns an **array** of values which are contained in this local. + * @returns An **array** of values used by this local. + */ + getUses() { + return []; + } + addUsedStmt(usedStmt) { + this.usedStmts.push(usedStmt); + } + /** + * Returns an array of statements used by the local, i.e., the statements in which the local participate. + * For example, if the code snippet is `let dd = cc + 5;` where `cc` is a local and `cc` only appears once, + * then the length of **array** returned is 1 and `Stmts[0]` will be same as the example described + * in the `Local.getDeclaringStmt()`. + * @returns An array of statements used by the local. + */ + getUsedStmts() { + return this.usedStmts; + } + /** + * Get a string of local name in Local + * @returns The string of local name. + * @example + * 1. get a name string. + + ```typescript + for (const value of stmt.getUses()) { + const name = value.toString(); + ... + } + ``` + */ + toString() { + return this.getName(); + } + getExportType() { + return ExportType.LOCAL; + } + getModifiers() { + return 0; + } + containsModifier(modifierType) { + if (modifierType === ModifierType.CONST) { + return this.getConstFlag(); + } + return false; + } + getSignature() { + return (this.signature ?? + new LocalSignature(this.name, new MethodSignature(ClassSignature.DEFAULT, ArkSignatureBuilder.buildMethodSubSignatureFromMethodName(UNKNOWN_METHOD_NAME)))); + } + setSignature(signature) { + this.signature = signature; + } + getConstFlag() { + if (!this.constFlag) { + return false; + } + return this.constFlag; + } + setConstFlag(newConstFlag) { + this.constFlag = newConstFlag; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$f = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'TypeInference'); +class TypeInference { + static inferTypeInArkField(arkField) { + const arkClass = arkField.getDeclaringArkClass(); + const stmts = arkField.getInitializer(); + const method = arkClass.getMethodWithName(INSTANCE_INIT_METHOD_NAME) ?? arkClass.getMethodWithName(CONSTRUCTOR_NAME); + for (const stmt of stmts) { + if (method) { + this.resolveStmt(stmt, method); + } + } + const beforeType = arkField.getType(); + if (!this.isUnclearType(beforeType)) { + return; + } + let rightType; + let fieldRef; + const lastStmt = stmts[stmts.length - 1]; + if (lastStmt instanceof ArkAssignStmt) { + rightType = lastStmt.getRightOp().getType(); + if (lastStmt.getLeftOp() instanceof ArkInstanceFieldRef) { + fieldRef = lastStmt.getLeftOp(); + } + } + let fieldType; + if (beforeType) { + fieldType = this.inferUnclearedType(beforeType, arkClass); + } + if (fieldType) { + arkField.getSignature().setType(fieldType); + fieldRef?.setFieldSignature(arkField.getSignature()); + } + else if (rightType && this.isUnclearType(beforeType) && !this.isUnclearType(rightType)) { + arkField.getSignature().setType(rightType); + fieldRef?.setFieldSignature(arkField.getSignature()); + } + } + /** + * Infer type for a given unclear type. + * It returns an array with 2 items, original object and original type. + * The original object is null if there is no object, or it failed to find the object. + * The original type is null if failed to infer the type. + * @param leftOpType + * @param declaringArkClass + * @param visited + * @returns + */ + static inferUnclearedType(leftOpType, declaringArkClass, visited = new Set()) { + if (visited.has(leftOpType)) { + return leftOpType; + } + else { + visited.add(leftOpType); + } + let type; + if (leftOpType instanceof ClassType && leftOpType.getClassSignature().getDeclaringFileSignature().getFileName() === UNKNOWN_FILE_NAME) { + type = TypeInference.inferUnclearRefName(leftOpType.getClassSignature().getClassName(), declaringArkClass); + } + else if (leftOpType instanceof UnionType || leftOpType instanceof IntersectionType || leftOpType instanceof TupleType) { + let types = leftOpType.getTypes(); + for (let i = 0; i < types.length; i++) { + let newType = this.inferUnclearedType(types[i], declaringArkClass, visited); + if (newType) { + types[i] = newType; + } + } + type = leftOpType; + } + else if (leftOpType instanceof ArrayType) { + let baseType = this.inferUnclearedType(leftOpType.getBaseType(), declaringArkClass, visited); + if (baseType) { + leftOpType.setBaseType(baseType); + type = leftOpType; + } + } + else if (leftOpType instanceof AliasType) { + let baseType = this.inferUnclearedType(leftOpType.getOriginalType(), declaringArkClass, visited); + if (baseType) { + leftOpType.setOriginalType(baseType); + type = leftOpType; + } + } + else if (leftOpType instanceof AnnotationNamespaceType) { + type = this.inferBaseType(leftOpType.getOriginType(), declaringArkClass); + } + else if (leftOpType instanceof UnclearReferenceType) { + type = this.inferUnclearRefType(leftOpType, declaringArkClass); + } + return type; + } + static inferTypeInMethod(arkMethod) { + const arkClass = arkMethod.getDeclaringArkClass(); + this.inferGenericType(arkMethod.getGenericTypes(), arkClass); + const signatures = []; + arkMethod.getDeclareSignatures()?.forEach(m => signatures.push(m)); + const impl = arkMethod.getImplementationSignature(); + if (impl) { + signatures.push(impl); + } + signatures.forEach(s => { + s.getMethodSubSignature() + .getParameters() + .forEach(p => { + this.inferParameterType(p, arkMethod); + }); + }); + const body = arkMethod.getBody(); + if (!body) { + signatures.forEach(s => this.inferSignatureReturnType(s, arkMethod)); + return; + } + body.getUsedGlobals()?.forEach((value, key) => { + if (value instanceof GlobalRef && !value.getRef()) { + const arkExport = ModelUtils.findGlobalRef(key, arkMethod); + if (arkExport instanceof Local) { + arkExport.getUsedStmts().push(...value.getUsedStmts()); + value.setRef(arkExport); + } + } + }); + const cfg = body.getCfg(); + for (const block of cfg.getBlocks()) { + for (const stmt of block.getStmts()) { + this.resolveStmt(stmt, arkMethod); + } + } + signatures.forEach(s => this.inferSignatureReturnType(s, arkMethod)); + } + static resolveStmt(stmt, arkMethod) { + try { + this.resolveTypeExprsInStmt(stmt, arkMethod); + this.resolveExprsInStmt(stmt, arkMethod); + this.resolveFieldRefsInStmt(stmt, arkMethod); + this.resolveArkAssignStmt(stmt, arkMethod); + this.resolveArkReturnStmt(stmt, arkMethod); + } + catch (e) { + logger$f.warn('stmt is not correct: ' + stmt.toString()); + } + } + /** + * @Deprecated + * @param arkMethod + */ + static inferSimpleTypeInMethod(arkMethod) { + const body = arkMethod.getBody(); + if (!body) { + logger$f.warn('empty body'); + return; + } + const cfg = body.getCfg(); + for (const block of cfg.getBlocks()) { + for (const stmt of block.getStmts()) { + TypeInference.inferSimpleTypeInStmt(stmt); + } + } + } + /** + * infer type for Exprs in stmt which invoke method. + * such as ArkInstanceInvokeExpr ArkStaticInvokeExpr ArkNewExpr + */ + static resolveExprsInStmt(stmt, arkMethod) { + for (const expr of stmt.getExprs()) { + const newExpr = expr.inferType(arkMethod); + if (stmt.containsInvokeExpr() && + ((expr instanceof ArkInstanceInvokeExpr && newExpr instanceof ArkStaticInvokeExpr) || newExpr instanceof ArkPtrInvokeExpr)) { + stmt.replaceUse(expr, newExpr); + } + } + if (stmt instanceof ArkAliasTypeDefineStmt && this.isUnclearType(stmt.getAliasType().getOriginalType())) { + stmt.getAliasType().setOriginalType(stmt.getAliasTypeExpr().getType()); + } + } + /** + * infer value type for TypeExprs in stmt which specify the type such as TypeQueryExpr + */ + static resolveTypeExprsInStmt(stmt, arkMethod) { + for (let typeExpr of stmt.getTypeExprs()) { + typeExpr.inferType(arkMethod); + } + } + /** + * infer type for fieldRefs in stmt. + */ + static resolveFieldRefsInStmt(stmt, arkMethod) { + for (const use of stmt.getUses()) { + if (use instanceof AbstractRef) { + this.processRef(use, stmt, arkMethod); + } + } + const stmtDef = stmt.getDef(); + if (stmtDef && stmtDef instanceof AbstractRef) { + if (arkMethod.getName() === INSTANCE_INIT_METHOD_NAME && + stmtDef instanceof ArkInstanceFieldRef && + stmtDef.getBase().getName() === THIS_NAME && + arkMethod.getDeclaringArkClass().isAnonymousClass() && + stmtDef.getFieldName().indexOf('.') === -1) { + return; + } + const fieldRef = stmtDef.inferType(arkMethod); + stmt.replaceDef(stmtDef, fieldRef); + } + } + static processRef(use, stmt, arkMethod) { + const fieldRef = use.inferType(arkMethod); + if (fieldRef instanceof ArkStaticFieldRef && stmt instanceof ArkAssignStmt) { + stmt.replaceUse(use, fieldRef); + } + else if (use instanceof ArkInstanceFieldRef && fieldRef instanceof ArkArrayRef && stmt instanceof ArkAssignStmt) { + const index = fieldRef.getIndex(); + if (index instanceof Constant && index.getType() instanceof StringType) { + const local = arkMethod?.getBody()?.getLocals().get(index.getValue()); + if (local) { + fieldRef.setIndex(local); + } + } + stmt.replaceUse(use, fieldRef); + } + } + static parseArkExport2Type(arkExport) { + if (!arkExport) { + return null; + } + if (arkExport instanceof ArkClass) { + return new ClassType(arkExport.getSignature(), arkExport.getGenericsTypes()); + } + else if (arkExport instanceof ArkNamespace) { + return AnnotationNamespaceType.getInstance(arkExport.getSignature()); + } + else if (arkExport instanceof ArkMethod) { + return new FunctionType(arkExport.getSignature()); + } + else if (arkExport instanceof Local) { + if (arkExport.getType() instanceof UnknownType || arkExport.getType() instanceof UnclearReferenceType) { + return null; + } + return arkExport.getType(); + } + else if (arkExport instanceof AliasType) { + return arkExport; + } + else { + return null; + } + } + /** + * infer and pass type for ArkAssignStmt right and left + * @param stmt + * @param arkMethod + */ + static resolveArkAssignStmt(stmt, arkMethod) { + if (!(stmt instanceof ArkAssignStmt)) { + return; + } + const arkClass = arkMethod.getDeclaringArkClass(); + const rightOp = stmt.getRightOp(); + if (rightOp instanceof Local && rightOp.getType() instanceof UnknownType) { + IRInference.inferLocal(rightOp, arkMethod); + } + let rightType = rightOp.getType(); + if (this.isUnclearType(rightType)) { + rightType = this.inferUnclearedType(rightType, arkClass); + if (rightType) { + this.setValueType(rightOp, rightType); + } + } + TypeInference.resolveLeftOp(stmt, arkClass, rightType, arkMethod); + } + static resolveLeftOp(stmt, arkClass, rightType, arkMethod) { + const leftOp = stmt.getLeftOp(); + let leftType = leftOp.getType(); + if (this.isUnclearType(leftType)) { + const newLeftType = this.inferUnclearedType(leftType, arkClass); + if (!newLeftType && !this.isUnclearType(rightType)) { + leftType = rightType; + } + else if (newLeftType) { + leftType = newLeftType; + } + } + else if (leftOp instanceof Local && leftOp.getName() === THIS_NAME) { + const thisLocal = IRInference.inferThisLocal(arkMethod); + if (thisLocal) { + stmt.setLeftOp(thisLocal); + } + else { + leftType = rightType; + } + } + if (leftType && !this.isUnclearType(leftType)) { + this.setValueType(leftOp, leftType); + if (leftOp instanceof Local && stmt.getOriginalText()?.startsWith(leftOp.getName())) { + let localDef = ModelUtils.findDeclaredLocal(leftOp, arkMethod); + if (localDef && this.isUnclearType(localDef.getType())) { + localDef.setType(leftType); + } + } + if (rightType) { + IRInference.inferRightWithSdkType(leftType, rightType, arkClass); + } + if (leftOp instanceof AbstractFieldRef) { + const declaringSignature = leftOp.getFieldSignature().getDeclaringSignature(); + if (declaringSignature instanceof NamespaceSignature && declaringSignature.getNamespaceName() === GLOBAL_THIS_NAME) { + SdkUtils.computeGlobalThis(leftOp, arkMethod); + } + } + } + } + static setValueType(value, type) { + if (value instanceof Local || value instanceof ArkParameterRef) { + value.setType(type); + } + else if (value instanceof AbstractFieldRef) { + value.getFieldSignature().setType(type); + } + } + static isUnclearType(type) { + // TODO: For UnionType, IntersectionType and TupleType, it should recurse check every item of them. + if (!type || type instanceof UnknownType || type instanceof UnclearReferenceType || type instanceof NullType || + type instanceof UndefinedType || type instanceof GenericType) { + return true; + } + else if (type instanceof ClassType && + (type.getClassSignature().getDeclaringFileSignature().getFileName() === UNKNOWN_FILE_NAME || + (type.getClassSignature().getClassName() === PROMISE && !type.getRealGenericTypes()) || + (type.getClassSignature().getDeclaringFileSignature().getFileName() === Builtin.DUMMY_FILE_NAME && + type.getRealGenericTypes()?.find(t => t instanceof GenericType)))) { + return true; + } + else if (type instanceof UnionType || type instanceof IntersectionType || type instanceof TupleType) { + return !!type.getTypes().find(t => this.hasUnclearReferenceType(t)); + } + else if (type instanceof ArrayType) { + const baseType = type.getBaseType(); + return this.hasUnclearReferenceType(baseType) || baseType instanceof GenericType; + } + else if (type instanceof AliasType) { + return this.isUnclearType(type.getOriginalType()); + } + else if (type instanceof KeyofTypeExpr) { + return this.isUnclearType(type.getOpType()); + } + else if (type instanceof TypeQueryExpr) { + return this.isUnclearType(type.getType()); + } + return false; + } + // This is the temporal function to check unclearReferenceType recursively and can be removed after typeInfer supports multiple candidate types. + static hasUnclearReferenceType(type, visited = new Set()) { + if (visited.has(type)) { + return false; + } + else { + visited.add(type); + } + if (type instanceof UnclearReferenceType) { + return true; + } + else if (type instanceof UnionType || type instanceof IntersectionType || type instanceof TupleType) { + return !!type.getTypes().find(t => this.hasUnclearReferenceType(t, visited)); + } + else if (type instanceof ArrayType) { + return this.hasUnclearReferenceType(type.getBaseType(), visited); + } + else if (type instanceof AliasType) { + return this.hasUnclearReferenceType(type.getOriginalType(), visited); + } + else if (type instanceof KeyofTypeExpr) { + return this.hasUnclearReferenceType(type.getOpType(), visited); + } + else if (type instanceof TypeQueryExpr) { + return this.hasUnclearReferenceType(type.getType(), visited); + } + return false; + } + static inferSimpleTypeInStmt(stmt) { + if (stmt instanceof ArkAssignStmt) { + const leftOp = stmt.getLeftOp(); + if (leftOp instanceof Local) { + const leftOpType = leftOp.getType(); + if (leftOpType instanceof UnknownType) { + const rightOp = stmt.getRightOp(); + leftOp.setType(rightOp.getType()); + } + } + } + } + // Deal only with simple situations + static buildTypeFromStr(typeStr) { + switch (typeStr) { + case BOOLEAN_KEYWORD: + return BooleanType.getInstance(); + case NUMBER_KEYWORD: + return NumberType.getInstance(); + case STRING_KEYWORD: + return StringType.getInstance(); + case UNDEFINED_KEYWORD: + return UndefinedType.getInstance(); + case NULL_KEYWORD: + return NullType.getInstance(); + case ANY_KEYWORD: + return AnyType.getInstance(); + case VOID_KEYWORD: + return VoidType.getInstance(); + case NEVER_KEYWORD: + return NeverType.getInstance(); + case BIGINT_KEYWORD: + return BigIntType.getInstance(); + case 'RegularExpression': { + const classSignature = Builtin.REGEXP_CLASS_SIGNATURE; + return new ClassType(classSignature); + } + default: + return new UnclearReferenceType(typeStr); + } + } + static inferValueType(value, arkMethod) { + if (value instanceof ArkInstanceFieldRef || value instanceof ArkInstanceInvokeExpr) { + this.inferValueType(value.getBase(), arkMethod); + } + if (value instanceof AbstractRef || value instanceof AbstractExpr || value instanceof Local) { + value.inferType(arkMethod); + } + return value.getType(); + } + static inferParameterType(param, arkMethod) { + let pType = param.getType(); + const arkClass = arkMethod.getDeclaringArkClass(); + let type; + if (pType instanceof AbstractTypeExpr) { + pType.inferType(arkMethod); + } + else if (param.getName() === 'value' && arkClass.hasComponentDecorator() && arkMethod.getName() === CONSTRUCTOR_NAME) { + type = this.parseArkExport2Type(arkClass); + } + else { + type = TypeInference.inferUnclearedType(pType, arkClass); + } + if (type) { + param.setType(type); + } + } + static inferSignatureReturnType(oldSignature, arkMethod) { + if (oldSignature.getMethodSubSignature().getMethodName() === CONSTRUCTOR_NAME) { + const newReturnType = new ClassType(oldSignature.getDeclaringClassSignature()); + oldSignature.getMethodSubSignature().setReturnType(newReturnType); + return; + } + const currReturnType = oldSignature.getType(); + if (!this.isUnclearType(currReturnType)) { + return; + } + if (currReturnType instanceof AbstractTypeExpr) { + currReturnType.inferType(arkMethod); + return; + } + if (currReturnType instanceof ArrayType && currReturnType.getBaseType() instanceof AbstractTypeExpr) { + currReturnType.getBaseType().inferType(arkMethod); + return; + } + const newReturnType = this.inferUnclearedType(currReturnType, arkMethod.getDeclaringArkClass()); + if (newReturnType) { + oldSignature.getMethodSubSignature().setReturnType(newReturnType); + } + else if (arkMethod.getBody()) { + const returnType = TypeInference.inferReturnType(arkMethod); + if (returnType) { + oldSignature.getMethodSubSignature().setReturnType(returnType); + } + } + } + static inferReturnType(arkMethod) { + const typeMap = new Map(); + for (let returnValue of arkMethod.getReturnValues()) { + const type = returnValue.getType(); + if (type instanceof UnionType) { + type.flatType() + .filter(t => !TypeInference.isUnclearType(t)) + .forEach(t => typeMap.set(t.toString(), t)); + } + else if (!TypeInference.isUnclearType(type)) { + typeMap.set(type.toString(), type); + } + } + if (typeMap.size > 0) { + const types = Array.from(typeMap.values()); + let returnType = types.length === 1 ? types[0] : new UnionType(types); + if (arkMethod.containsModifier(ModifierType.ASYNC)) { + const promise = arkMethod.getDeclaringArkFile().getScene().getSdkGlobal(PROMISE); + if (promise instanceof ArkClass) { + returnType = new ClassType(promise.getSignature(), [returnType]); + } + } + return returnType; + } + return null; + } + static inferGenericType(types, arkClass) { + types?.forEach(type => { + const defaultType = type.getDefaultType(); + if (defaultType instanceof UnclearReferenceType) { + const newDefaultType = TypeInference.inferUnclearRefName(defaultType.getName(), arkClass); + if (newDefaultType) { + type.setDefaultType(this.replaceTypeWithReal(newDefaultType)); + } + } + const constraint = type.getConstraint(); + if (constraint instanceof UnclearReferenceType) { + const newConstraint = TypeInference.inferUnclearRefName(constraint.getName(), arkClass); + if (newConstraint) { + type.setConstraint(this.replaceTypeWithReal(newConstraint)); + } + } + }); + } + /** + * Infer type for a given {@link UnclearReferenceType} type. + * It returns original type. + * The original type is null if it failed to infer the type. + * @param urType + * @param arkClass + * @returns + */ + static inferUnclearRefType(urType, arkClass) { + const realTypes = urType.getGenericTypes(); + this.inferRealGenericTypes(realTypes, arkClass); + if (urType.getName() === Builtin.ARRAY) { + return new ArrayType(realTypes[0] ?? AnyType.getInstance(), 1); + } + const type = this.inferUnclearRefName(urType.getName(), arkClass); + return type ? this.replaceTypeWithReal(type, realTypes) : null; + } + /** + * Find out the original object and type for a given unclear reference type name. + * It returns original type. + * The original type is null if it failed to infer the type. + * @param refName + * @param arkClass + * @returns + */ + static inferUnclearRefName(refName, arkClass) { + if (!refName) { + return null; + } + //split and iterate to infer each type + const singleNames = refName.split('.'); + let type = null; + for (let i = 0; i < singleNames.length; i++) { + let genericName = EMPTY_STRING; + const name = singleNames[i].replace(/<(\w+)>/, (match, group1) => { + genericName = group1; + return EMPTY_STRING; + }); + if (i === 0) { + type = singleNames.length > 1 ? this.inferBaseType(name, arkClass) : this.inferTypeByName(name, arkClass); + } + else if (type) { + type = this.inferFieldType(type, name, arkClass)?.[1]; + } + if (!type) { + return null; + } + if (genericName) { + const realTypes = genericName.split(',').map(generic => { + const realType = this.inferUnclearRefName(generic, arkClass); + return realType ?? new UnclearReferenceType(generic); + }); + if (type instanceof ClassType) { + type = new ClassType(type.getClassSignature(), realTypes); + } + else if (type instanceof FunctionType) { + type = new FunctionType(type.getMethodSignature(), realTypes); + } + } + } + return type; + } + /** + * Find out the original object and type for a given base type and the field name. + * It returns an array with 2 items, original object and original type. + * The original object is null if there is no object, or it failed to find the object. + * The original type is null if it failed to infer the type. + * @param baseType + * @param fieldName + * @param declareClass + * @returns + */ + static inferFieldType(baseType, fieldName, declareClass) { + if (baseType instanceof AliasType) { + baseType = baseType.getOriginalType(); + } + else if (baseType instanceof UnionType && baseType.getCurrType()) { + baseType = baseType.getCurrType(); + } + let propertyAndType = null; + if (baseType instanceof ClassType) { + if (fieldName === Builtin.ITERATOR_RESULT_VALUE && + baseType.getClassSignature().getDeclaringFileSignature().getProjectName() === Builtin.DUMMY_PROJECT_NAME) { + const types = baseType.getRealGenericTypes(); + if (types && types.length > 0) { + return [null, types[0]]; + } + return null; + } + propertyAndType = this.inferClassFieldType(declareClass, baseType, fieldName); + } + else if (baseType instanceof AnnotationNamespaceType) { + const namespace = declareClass.getDeclaringArkFile().getScene().getNamespace(baseType.getNamespaceSignature()); + if (namespace) { + const property = ModelUtils.findPropertyInNamespace(fieldName, namespace); + const propertyType = this.parseArkExport2Type(property); + if (propertyType) { + propertyAndType = [property, propertyType]; + } + } + } + else { + logger$f.warn('infer unclear reference type fail: ' + fieldName); + } + return propertyAndType; + } + static inferClassFieldType(declareClass, baseType, fieldName) { + const arkClass = declareClass.getDeclaringArkFile().getScene().getClass(baseType.getClassSignature()); + if (!arkClass) { + return null; + } + const property = ModelUtils.findPropertyInClass(fieldName, arkClass); + let propertyType = null; + if (property instanceof ArkField) { + if (arkClass.getCategory() === ClassCategory.ENUM) { + let constant; + const lastStmt = property.getInitializer().at(-1); + if (lastStmt instanceof ArkAssignStmt && lastStmt.getRightOp() instanceof Constant) { + constant = lastStmt.getRightOp(); + } + propertyType = new EnumValueType(property.getSignature(), constant); + } + else { + propertyType = this.replaceTypeWithReal(property.getType(), baseType.getRealGenericTypes()); + } + } + else if (property) { + propertyType = this.parseArkExport2Type(property); + } + if (propertyType) { + return [property, propertyType]; + } + else if (arkClass.isAnonymousClass()) { + const fieldType = this.inferUnclearRefName(fieldName, arkClass); + return fieldType ? [null, fieldType] : null; + } + return null; + } + /** + * Find out the original object and type for a given base name. + * It returns original type. + * The original type is null if failed to infer the type. + * @param baseName + * @param arkClass + * @returns + */ + static inferBaseType(baseName, arkClass) { + if (SUPER_NAME === baseName) { + return this.parseArkExport2Type(arkClass.getSuperClass()); + } + else if (DEFAULT === baseName) { + return this.parseArkExport2Type(arkClass.getDeclaringArkFile().getExportInfoBy(DEFAULT)?.getArkExport()); + } + const field = ModelUtils.getDefaultClass(arkClass)?.getDefaultArkMethod()?.getBody()?.getLocals()?.get(baseName); + if (field && !this.isUnclearType(field.getType())) { + return field.getType(); + } + let arkExport = ModelUtils.getClassWithName(baseName, arkClass) ?? + ModelUtils.getDefaultClass(arkClass)?.getDefaultArkMethod()?.getBody()?.getAliasTypeByName(baseName) ?? + ModelUtils.getNamespaceWithName(baseName, arkClass) ?? + ModelUtils.getDefaultClass(arkClass)?.getMethodWithName(baseName) ?? + ModelUtils.getArkExportInImportInfoWithName(baseName, arkClass.getDeclaringArkFile()); + if (!arkExport && !arkClass.getDeclaringArkFile().getImportInfoBy(baseName)) { + arkExport = arkClass.getDeclaringArkFile().getScene().getSdkGlobal(baseName); + } + return this.parseArkExport2Type(arkExport); + } + static inferTypeByName(typeName, arkClass) { + let arkExport = ModelUtils.getClassWithName(typeName, arkClass) ?? + ModelUtils.getDefaultClass(arkClass)?.getDefaultArkMethod()?.getBody()?.getAliasTypeByName(typeName) ?? + ModelUtils.getArkExportInImportInfoWithName(typeName, arkClass.getDeclaringArkFile()); + if (!arkExport && !arkClass.getDeclaringArkFile().getImportInfoBy(typeName)) { + arkExport = arkClass.getDeclaringArkFile().getScene().getSdkGlobal(typeName); + } + const type = this.parseArkExport2Type(arkExport); + if (type instanceof ClassType || type instanceof AliasType) { + return type; + } + return null; + } + static inferRealGenericTypes(realTypes, arkClass) { + if (!realTypes) { + return; + } + for (let i = 0; i < realTypes.length; i++) { + const mayType = realTypes[i]; + if (this.isUnclearType(mayType)) { + const newType = this.inferUnclearedType(mayType, arkClass); + if (newType) { + realTypes[i] = newType; + } + } + } + } + static inferDynamicImportType(from, arkClass) { + const importInfo = new ImportInfo(); + importInfo.setNameBeforeAs(ALL); + importInfo.setImportClauseName(ALL); + importInfo.setImportFrom(from); + importInfo.setDeclaringArkFile(arkClass.getDeclaringArkFile()); + return TypeInference.parseArkExport2Type(importInfo.getLazyExportInfo()?.getArkExport()); + } + static replaceTypeWithReal(type, realTypes, visited = new Set()) { + if (visited.has(type)) { + return type; + } + else { + visited.add(type); + } + if (type instanceof GenericType) { + const realType = realTypes?.[type.getIndex()] ?? type.getDefaultType() ?? type.getConstraint(); + return realType ?? type; + } + else if (type instanceof AnyType) { + const realType = realTypes?.[0]; + return realType ?? type; + } + return this.replaceRecursiveType(type, visited, realTypes); + } + static replaceRecursiveType(type, visited, realTypes) { + if (type instanceof ClassType) { + const replacedTypes = type.getRealGenericTypes()?.map(g => this.replaceTypeWithReal(g, realTypes, visited)) ?? realTypes; + return replacedTypes && replacedTypes.length > 0 ? new ClassType(type.getClassSignature(), replacedTypes) : type; + } + else if (type instanceof FunctionType) { + const replacedTypes = type.getRealGenericTypes()?.map(g => this.replaceTypeWithReal(g, realTypes, visited)) ?? realTypes; + return replacedTypes && replacedTypes.length > 0 ? new FunctionType(type.getMethodSignature(), replacedTypes) : type; + } + else if (type instanceof AliasType && realTypes) { + const newObjectType = this.replaceTypeWithReal(type.getOriginalType(), realTypes, visited); + const replacedTypes = type.getRealGenericTypes()?.map(g => this.replaceTypeWithReal(g, realTypes, visited)) ?? realTypes; + if (replacedTypes.length > 0) { + const newAliasType = new AliasType(type.getName(), newObjectType, type.getSignature(), type.getGenericTypes()); + newAliasType.setRealGenericTypes(replacedTypes); + return newAliasType; + } + } + else if (type instanceof UnionType && realTypes) { + const types = []; + type.flatType().forEach(t => types.push(this.replaceTypeWithReal(t, realTypes, visited))); + return new UnionType(types, this.replaceTypeWithReal(type.getCurrType(), realTypes, visited)); + } + else if (type instanceof IntersectionType && realTypes) { + const types = []; + type.getTypes().forEach(t => types.push(this.replaceTypeWithReal(t, realTypes, visited))); + return new IntersectionType(types); + } + else if (type instanceof ArrayType && realTypes) { + const replacedBaseType = this.replaceTypeWithReal(type.getBaseType(), realTypes, visited); + return new ArrayType(replacedBaseType, type.getDimension()); + } + else if (type instanceof TupleType && realTypes) { + let replacedTypes = []; + type.getTypes().forEach(t => replacedTypes.push(this.replaceTypeWithReal(t, realTypes, visited))); + return new TupleType(replacedTypes); + } + return type; + } + static replaceAliasType(type) { + let aliasType = type; + while (aliasType instanceof AliasType) { + aliasType = aliasType.getOriginalType(); + } + return aliasType; + } + static inferFunctionType(argType, paramSubSignature, realTypes) { + const returnType = argType.getMethodSignature().getMethodSubSignature().getReturnType(); + const declareType = paramSubSignature?.getReturnType(); + if (declareType instanceof GenericType && realTypes && !this.isUnclearType(returnType) && !(returnType instanceof VoidType)) { + realTypes[declareType.getIndex()] = returnType; + } + const params = paramSubSignature?.getParameters(); + if (!params) { + return; + } + argType + .getMethodSignature() + .getMethodSubSignature() + .getParameters() + .filter(p => !p.getName().startsWith(LEXICAL_ENV_NAME_PREFIX)) + .forEach((p, i) => { + if (this.isUnclearType(p.getType())) { + let type = params?.[i]?.getType(); + if (type instanceof GenericType && realTypes) { + type = realTypes?.[type.getIndex()]; + } + if (type) { + p.setType(type); + } + } + }); + } + static resolveArkReturnStmt(stmt, arkMethod) { + if (!(stmt instanceof ArkReturnStmt)) { + return; + } + let returnType = arkMethod.getSignature().getType(); + if (returnType instanceof ClassType && returnType.getClassSignature().getClassName() === PROMISE) { + returnType = returnType.getRealGenericTypes()?.at(0); + } + if (returnType) { + IRInference.inferRightWithSdkType(returnType, stmt.getOp().getType(), arkMethod.getDeclaringArkClass()); + } + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @category core/base/expr + */ +class AbstractExpr { + inferType(arkMethod) { + return this; + } +} +class AbstractInvokeExpr extends AbstractExpr { + methodSignature; + args; + realGenericTypes; //新增 + constructor(methodSignature, args, realGenericTypes) { + super(); + this.methodSignature = methodSignature; + this.args = args; + this.realGenericTypes = realGenericTypes; + } + /** + * Get method Signature. The method signature is consist of ClassSignature and MethodSubSignature. + * It is the unique flag of a method. It is usually used to compose a expression string in ArkIRTransformer. + * @returns The class method signature, such as ArkStaticInvokeExpr. + * @example + * 1. 3AC information composed of getMethodSignature (). + + ```typescript + let strs: string[] = []; + strs.push('staticinvoke <'); + strs.push(this.getMethodSignature().toString()); + strs.push('>('); + ``` + */ + getMethodSignature() { + return this.methodSignature; + } + setMethodSignature(newMethodSignature) { + this.methodSignature = newMethodSignature; + } + /** + * Returns an argument used in the expression according to its index. + * @param index - the index of the argument. + * @returns An argument used in the expression. + */ + getArg(index) { + return this.args[index]; + } + /** + * Returns an **array** of arguments used in the expression. + * @returns An **array** of arguments used in the expression. + * @example + * 1. get args number. + + ```typescript + const argsNum = expr.getArgs().length; + if (argsNum < 5) { + ... ... + } + ``` + + 2. iterate arg based on expression + + ```typescript + for (const arg of this.getArgs()) { + strs.push(arg.toString()); + strs.push(', '); + } + ``` + */ + getArgs() { + return this.args; + } + setArgs(newArgs) { + this.args = newArgs; + } + getType() { + const type = this.methodSignature.getType(); + if (this.realGenericTypes) { + return TypeInference.replaceTypeWithReal(type, this.realGenericTypes); + } + return type; + } + getRealGenericTypes() { + return this.realGenericTypes; + } + setRealGenericTypes(realTypes) { + if (realTypes) { + this.realGenericTypes = realTypes; + } + } + getUses() { + let uses = []; + uses.push(...this.args); + for (const arg of this.args) { + uses.push(...arg.getUses()); + } + return uses; + } +} +class ArkInstanceInvokeExpr extends AbstractInvokeExpr { + base; + constructor(base, methodSignature, args, realGenericTypes) { + super(methodSignature, args, realGenericTypes); + this.base = base; + } + /** + * Returns the local of the instance of invoke expression. + * @returns The local of the invoke expression's instance.. + */ + getBase() { + return this.base; + } + setBase(newBase) { + this.base = newBase; + } + /** + * Returns an **array** of values used in this invoke expression, + * including all arguments and values each arguments used. + * For {@link ArkInstanceInvokeExpr}, the return also contains the caller base and uses of base. + * @returns An **array** of arguments used in the invoke expression. + */ + getUses() { + let uses = []; + uses.push(this.base); + uses.push(...this.base.getUses()); + uses.push(...this.getArgs()); + for (const arg of this.getArgs()) { + uses.push(...arg.getUses()); + } + return uses; + } + toString() { + let strs = []; + strs.push('instanceinvoke '); + strs.push(this.base.toString()); + strs.push('.<'); + strs.push(this.getMethodSignature().toString()); + strs.push('>('); + if (this.getArgs().length > 0) { + for (const arg of this.getArgs()) { + strs.push(arg.toString()); + strs.push(', '); + } + strs.pop(); + } + strs.push(')'); + return strs.join(''); + } + inferType(arkMethod) { + return IRInference.inferInstanceInvokeExpr(this, arkMethod); + } +} +class ArkStaticInvokeExpr extends AbstractInvokeExpr { + constructor(methodSignature, args, realGenericTypes) { + super(methodSignature, args, realGenericTypes); + } + toString() { + let strs = []; + strs.push('staticinvoke <'); + strs.push(this.getMethodSignature().toString()); + strs.push('>('); + if (this.getArgs().length > 0) { + for (const arg of this.getArgs()) { + strs.push(arg.toString()); + strs.push(', '); + } + strs.pop(); + } + strs.push(')'); + return strs.join(''); + } + inferType(arkMethod) { + return IRInference.inferStaticInvokeExpr(this, arkMethod); + } +} +/** + * 1. Local PtrInvokeExpr + * + * ```typescript + * func foo():void { + * } + * let ptr = foo; + * ptr(); + * ``` + * 2. FieldRef PtrInvokeExpr + * + * ```typescript + * class A { + * b:()=> void() + * } + * new A().b() + * ``` + */ +class ArkPtrInvokeExpr extends AbstractInvokeExpr { + funPtr; + constructor(methodSignature, ptr, args, realGenericTypes) { + super(methodSignature, args, realGenericTypes); + this.funPtr = ptr; + } + setFunPtrLocal(ptr) { + this.funPtr = ptr; + } + getFuncPtrLocal() { + return this.funPtr; + } + toString() { + let strs = []; + strs.push('ptrinvoke <'); + let ptrName = ''; + if (this.funPtr instanceof Local) { + ptrName = this.funPtr.getName(); + } + else if (this.funPtr instanceof ArkInstanceFieldRef) { + ptrName = this.funPtr.getBase().getName() + '.' + this.funPtr.getFieldName(); + } + else if (this.funPtr instanceof ArkStaticFieldRef) { + ptrName = this.funPtr.getFieldName(); + } + strs.push(this.getMethodSignature().toString(ptrName)); + strs.push('>('); + if (this.getArgs().length > 0) { + for (const arg of this.getArgs()) { + strs.push(arg.toString()); + strs.push(', '); + } + strs.pop(); + } + strs.push(')'); + return strs.join(''); + } + getUses() { + let uses = []; + uses.push(this.getFuncPtrLocal()); + uses.push(...this.getArgs()); + for (const arg of this.getArgs()) { + uses.push(...arg.getUses()); + } + return uses; + } +} +class ArkNewExpr extends AbstractExpr { + classType; + constructor(classType) { + super(); + this.classType = classType; + } + getClassType() { + return this.classType; + } + getUses() { + return []; + } + getType() { + return this.classType; + } + toString() { + return 'new ' + this.classType; + } + inferType(arkMethod) { + const classSignature = this.classType.getClassSignature(); + if (classSignature.getDeclaringFileSignature().getFileName() === UNKNOWN_FILE_NAME) { + const className = classSignature.getClassName(); + let type = ModelUtils.findDeclaredLocal(new Local(className), arkMethod, 1)?.getType(); + if (TypeInference.isUnclearType(type)) { + type = TypeInference.inferUnclearRefName(className, arkMethod.getDeclaringArkClass()); + } + if (type && type instanceof ClassType) { + const instanceType = this.constructorSignature(type, arkMethod) ?? type; + this.classType.setClassSignature(instanceType.getClassSignature()); + TypeInference.inferRealGenericTypes(this.classType.getRealGenericTypes(), arkMethod.getDeclaringArkClass()); + } + } + return this; + } + constructorSignature(type, arkMethod) { + const classConstructor = arkMethod.getDeclaringArkFile().getScene().getClass(type.getClassSignature()); + if (classConstructor?.getCategory() === ClassCategory.INTERFACE) { + const type = classConstructor.getMethodWithName('construct-signature')?.getReturnType(); + if (type) { + const returnType = TypeInference.replaceAliasType(type); + return returnType instanceof ClassType ? returnType : undefined; + } + } + return undefined; + } +} +class ArkNewArrayExpr extends AbstractExpr { + baseType; + size; + fromLiteral; + constructor(baseType, size, fromLiteral = false) { + super(); + this.baseType = baseType; + this.size = size; + this.fromLiteral = fromLiteral; + } + getSize() { + return this.size; + } + setSize(newSize) { + this.size = newSize; + } + getType() { + return new ArrayType(this.baseType, 1); + } + getBaseType() { + return this.baseType; + } + setBaseType(newType) { + this.baseType = newType; + } + isFromLiteral() { + return this.fromLiteral; + } + inferType(arkMethod) { + const type = TypeInference.inferUnclearedType(this.baseType, arkMethod.getDeclaringArkClass()); + if (type) { + this.baseType = type; + } + return this; + } + getUses() { + let uses = [this.size]; + uses.push(...this.size.getUses()); + return uses; + } + toString() { + return 'newarray (' + this.baseType + ')[' + this.size + ']'; + } +} +class ArkDeleteExpr extends AbstractExpr { + field; + constructor(field) { + super(); + this.field = field; + } + getField() { + return this.field; + } + setField(newField) { + this.field = newField; + } + getType() { + return BooleanType.getInstance(); + } + getUses() { + const uses = []; + uses.push(this.field); + uses.push(...this.field.getUses()); + return uses; + } + toString() { + return 'delete ' + this.field; + } +} +class ArkAwaitExpr extends AbstractExpr { + promise; + constructor(promise) { + super(); + this.promise = promise; + } + getPromise() { + return this.promise; + } + setPromise(newPromise) { + this.promise = newPromise; + } + getType() { + const type = this.promise.getType(); + if (type instanceof UnclearReferenceType) { + return type.getGenericTypes()[0]; + } + else if (type instanceof ClassType) { + return type.getRealGenericTypes()?.[0] ?? type; + } + return type; + } + inferType(arkMethod) { + TypeInference.inferValueType(this.promise, arkMethod); + return this; + } + getUses() { + const uses = []; + uses.push(this.promise); + uses.push(...this.promise.getUses()); + return uses; + } + toString() { + const str = 'await ' + this.promise; + return str; + } +} +class ArkYieldExpr extends AbstractExpr { + yieldValue; + constructor(yieldValue) { + super(); + this.yieldValue = yieldValue; + } + getYieldValue() { + return this.yieldValue; + } + setYieldValue(newYieldValue) { + this.yieldValue = newYieldValue; + } + getType() { + return this.yieldValue.getType(); + } + getUses() { + const uses = []; + uses.push(this.yieldValue); + uses.push(...this.yieldValue.getUses()); + return uses; + } + toString() { + const str = 'yield ' + this.yieldValue; + return str; + } +} +exports.NormalBinaryOperator = void 0; +(function (NormalBinaryOperator) { + // TODO: unfold it + NormalBinaryOperator["NullishCoalescing"] = "??"; + // arithmetic + NormalBinaryOperator["Exponentiation"] = "**"; + NormalBinaryOperator["Division"] = "/"; + NormalBinaryOperator["Addition"] = "+"; + NormalBinaryOperator["Subtraction"] = "-"; + NormalBinaryOperator["Multiplication"] = "*"; + NormalBinaryOperator["Remainder"] = "%"; + // shift + NormalBinaryOperator["LeftShift"] = "<<"; + NormalBinaryOperator["RightShift"] = ">>"; + NormalBinaryOperator["UnsignedRightShift"] = ">>>"; + // Bitwise + NormalBinaryOperator["BitwiseAnd"] = "&"; + NormalBinaryOperator["BitwiseOr"] = "|"; + NormalBinaryOperator["BitwiseXor"] = "^"; + // Logical + NormalBinaryOperator["LogicalAnd"] = "&&"; + NormalBinaryOperator["LogicalOr"] = "||"; +})(exports.NormalBinaryOperator || (exports.NormalBinaryOperator = {})); +exports.RelationalBinaryOperator = void 0; +(function (RelationalBinaryOperator) { + RelationalBinaryOperator["LessThan"] = "<"; + RelationalBinaryOperator["LessThanOrEqual"] = "<="; + RelationalBinaryOperator["GreaterThan"] = ">"; + RelationalBinaryOperator["GreaterThanOrEqual"] = ">="; + RelationalBinaryOperator["Equality"] = "=="; + RelationalBinaryOperator["InEquality"] = "!="; + RelationalBinaryOperator["StrictEquality"] = "==="; + RelationalBinaryOperator["StrictInequality"] = "!=="; + RelationalBinaryOperator["isPropertyOf"] = "in"; +})(exports.RelationalBinaryOperator || (exports.RelationalBinaryOperator = {})); +// 二元运算表达式 +class AbstractBinopExpr extends AbstractExpr { + op1; + op2; + operator; + type; + constructor(op1, op2, operator) { + super(); + this.op1 = op1; + this.op2 = op2; + this.operator = operator; + } + /** + * Returns the first operand in the binary operation expression. + * For example, the first operand in `a + b;` is `a`. + * @returns The first operand in the binary operation expression. + */ + getOp1() { + return this.op1; + } + setOp1(newOp1) { + this.op1 = newOp1; + } + /** + * Returns the second operand in the binary operation expression. + * For example, the second operand in `a + b;` is `b`. + * @returns The second operand in the binary operation expression. + */ + getOp2() { + return this.op2; + } + setOp2(newOp2) { + this.op2 = newOp2; + } + /** + * Get the binary operator from the statement. + * The binary operator can be divided into two categories, + * one is the normal binary operator and the other is relational binary operator. + * @returns The binary operator from the statement. + * @example + ```typescript + if (expr instanceof AbstractBinopExpr) { + let op1: Value = expr.getOp1(); + let op2: Value = expr.getOp2(); + let operator: string = expr.getOperator(); + ... ... + } + ``` + */ + getOperator() { + return this.operator; + } + getType() { + if (!this.type) { + this.setType(); + } + return this.type; + } + getUses() { + let uses = []; + uses.push(this.op1); + uses.push(...this.op1.getUses()); + uses.push(this.op2); + uses.push(...this.op2.getUses()); + return uses; + } + toString() { + return this.op1 + ' ' + this.operator + ' ' + this.op2; + } + inferOpType(op, arkMethod) { + if (op instanceof AbstractExpr || op instanceof AbstractRef) { + TypeInference.inferValueType(op, arkMethod); + } + } + setType() { + let op1Type = this.op1.getType(); + let op2Type = this.op2.getType(); + if (op1Type instanceof UnionType) { + op1Type = op1Type.getCurrType(); + } + if (op2Type instanceof UnionType) { + op2Type = op2Type.getCurrType(); + } + let type = UnknownType.getInstance(); + switch (this.operator) { + case '+': + if (op1Type === StringType.getInstance() || op2Type === StringType.getInstance()) { + type = StringType.getInstance(); + } + if (op1Type === NumberType.getInstance() && op2Type === NumberType.getInstance()) { + type = NumberType.getInstance(); + } + if (op1Type === BigIntType.getInstance() && op2Type === BigIntType.getInstance()) { + type = BigIntType.getInstance(); + } + break; + case '-': + case '*': + case '/': + case '%': + if (op1Type === NumberType.getInstance() && op2Type === NumberType.getInstance()) { + type = NumberType.getInstance(); + } + if (op1Type === BigIntType.getInstance() && op2Type === BigIntType.getInstance()) { + type = BigIntType.getInstance(); + } + break; + case '!=': + case '!==': + case '<': + case '>': + case '<=': + case '>=': + case '&&': + case '||': + case '==': + case '===': + case 'in': + type = BooleanType.getInstance(); + break; + case '&': + case '|': + case '^': + case '<<': + case '>>': + if (op1Type === NumberType.getInstance() && op2Type === NumberType.getInstance()) { + type = NumberType.getInstance(); + } + if (op1Type === BigIntType.getInstance() && op2Type === BigIntType.getInstance()) { + type = BigIntType.getInstance(); + } + break; + case '>>>': + if (op1Type === NumberType.getInstance() && op2Type === NumberType.getInstance()) { + type = NumberType.getInstance(); + } + break; + case '??': + if (op1Type === UnknownType.getInstance() || op1Type === UndefinedType.getInstance() || op1Type === NullType.getInstance()) { + type = op2Type; + } + else { + type = op1Type; + } + break; + } + this.type = type; + } + inferType(arkMethod) { + this.inferOpType(this.op1, arkMethod); + this.inferOpType(this.op2, arkMethod); + this.setType(); + return this; + } +} +class ArkConditionExpr extends AbstractBinopExpr { + constructor(op1, op2, operator) { + super(op1, op2, operator); + } + inferType(arkMethod) { + this.inferOpType(this.op1, arkMethod); + const op1Type = this.op1.getType(); + if (this.operator === exports.RelationalBinaryOperator.InEquality && this.op2 === ValueUtil.getOrCreateNumberConst(0)) { + if (op1Type instanceof StringType) { + this.op2 = ValueUtil.createStringConst(EMPTY_STRING); + } + else if (op1Type instanceof BooleanType) { + this.op2 = ValueUtil.getBooleanConstant(false); + } + else if (op1Type instanceof ClassType) { + this.op2 = ValueUtil.getUndefinedConst(); + } + } + else { + this.inferOpType(this.getOp2(), arkMethod); + } + this.type = BooleanType.getInstance(); + return this; + } +} +class ArkNormalBinopExpr extends AbstractBinopExpr { + constructor(op1, op2, operator) { + super(op1, op2, operator); + } +} +class ArkTypeOfExpr extends AbstractExpr { + op; + constructor(op) { + super(); + this.op = op; + } + getOp() { + return this.op; + } + setOp(newOp) { + this.op = newOp; + } + getUses() { + let uses = []; + uses.push(this.op); + uses.push(...this.op.getUses()); + return uses; + } + getType() { + return this.op.getType(); + } + toString() { + return 'typeof ' + this.op; + } + inferType(arkMethod) { + if (this.op instanceof AbstractRef || this.op instanceof AbstractExpr) { + this.op.inferType(arkMethod); + } + return this; + } +} +class ArkInstanceOfExpr extends AbstractExpr { + op; + checkType; + constructor(op, checkType) { + super(); + this.op = op; + this.checkType = checkType; + } + getOp() { + return this.op; + } + setOp(newOp) { + this.op = newOp; + } + getCheckType() { + return this.checkType; + } + getType() { + return BooleanType.getInstance(); + } + getUses() { + let uses = []; + uses.push(this.op); + uses.push(...this.op.getUses()); + return uses; + } + toString() { + return this.op + ' instanceof ' + this.checkType; + } + inferType(arkMethod) { + TypeInference.inferValueType(this.op, arkMethod); + if (TypeInference.isUnclearType(this.checkType)) { + const newType = TypeInference.inferUnclearedType(this.checkType, arkMethod.getDeclaringArkClass()); + if (newType) { + this.checkType = newType; + } + } + return this; + } +} +// 类型转换 +class ArkCastExpr extends AbstractExpr { + op; + type; + constructor(op, type) { + super(); + this.op = op; + this.type = type; + } + getOp() { + return this.op; + } + setOp(newOp) { + this.op = newOp; + } + getUses() { + let uses = []; + uses.push(this.op); + uses.push(...this.op.getUses()); + return uses; + } + getType() { + return this.type; + } + inferType(arkMethod) { + if (TypeInference.isUnclearType(this.getType())) { + const type = TypeInference.inferUnclearedType(this.type, arkMethod.getDeclaringArkClass()) ?? this.op.getType(); + if (type !== undefined && !TypeInference.isUnclearType(type)) { + this.type = type; + IRInference.inferRightWithSdkType(type, this.op.getType(), arkMethod.getDeclaringArkClass()); + } + } + return this; + } + toString() { + return '<' + this.type + '>' + this.op; + } +} +class ArkPhiExpr extends AbstractExpr { + args; + argToBlock; + constructor() { + super(); + this.args = []; + this.argToBlock = new Map(); + } + getUses() { + let uses = []; + uses.push(...this.args); + return uses; + } + getArgs() { + return this.args; + } + setArgs(args) { + this.args = args; + } + getArgToBlock() { + return this.argToBlock; + } + setArgToBlock(argToBlock) { + this.argToBlock = argToBlock; + } + getType() { + return this.args[0].getType(); + } + toString() { + let strs = []; + strs.push('phi('); + if (this.args.length > 0) { + for (const arg of this.args) { + strs.push(arg.toString()); + strs.push(', '); + } + strs.pop(); + } + strs.push(')'); + return strs.join(''); + } +} +exports.UnaryOperator = void 0; +(function (UnaryOperator) { + UnaryOperator["Neg"] = "-"; + UnaryOperator["BitwiseNot"] = "~"; + UnaryOperator["LogicalNot"] = "!"; +})(exports.UnaryOperator || (exports.UnaryOperator = {})); +// unary operation expression +class ArkUnopExpr extends AbstractExpr { + op; + operator; + constructor(op, operator) { + super(); + this.op = op; + this.operator = operator; + } + getUses() { + let uses = []; + uses.push(this.op); + uses.push(...this.op.getUses()); + return uses; + } + getOp() { + return this.op; + } + setOp(newOp) { + this.op = newOp; + } + getType() { + return this.op.getType(); + } + /** + * Get the unary operator from the statement, such as `-`,`~`,`!`. + * @returns the unary operator of a statement. + */ + getOperator() { + return this.operator; + } + toString() { + return this.operator + this.op; + } +} +/** + * Expression of the right hand of the type alias definition statement. + * @category core/base/expr + * @extends AbstractExpr + * @example + ```typescript + let a: number = 123; + type ABC = typeof a; + ``` + * The AliasTypeExpr of the previous statement is with local 'a' as the 'originalObject' and 'transferWithTypeOf' is true. + * + * The Following case: import type with no clause name is not supported now, + * whose 'originalObject' is {@link ImportInfo} with 'null' 'lazyExportInfo'. + ```typescript + let a = typeof import('./abc'); + ``` + */ +class AliasTypeExpr extends AbstractExpr { + originalObject; + transferWithTypeOf = false; + realGenericTypes; + constructor(originalObject, transferWithTypeOf) { + super(); + this.originalObject = originalObject; + if (transferWithTypeOf !== undefined) { + this.transferWithTypeOf = transferWithTypeOf; + } + } + getOriginalObject() { + return this.originalObject; + } + setOriginalObject(object) { + this.originalObject = object; + } + getTransferWithTypeOf() { + return this.transferWithTypeOf; + } + setRealGenericTypes(realGenericTypes) { + this.realGenericTypes = realGenericTypes; + } + getRealGenericTypes() { + return this.realGenericTypes; + } + getType() { + function getTypeOfImportInfo(importInfo) { + const arkExport = importInfo.getLazyExportInfo()?.getArkExport(); + const importClauseName = importInfo.getImportClauseName(); + let type; + if (importClauseName.includes('.') && arkExport instanceof ArkClass) { + type = TypeInference.inferUnclearRefName(importClauseName, arkExport); + } + else if (arkExport) { + type = TypeInference.parseArkExport2Type(arkExport); + } + return type ?? UnknownType.getInstance(); + } + const operator = this.getOriginalObject(); + if (!this.getTransferWithTypeOf()) { + if (operator instanceof Type) { + return TypeInference.replaceTypeWithReal(operator, this.getRealGenericTypes()); + } + if (operator instanceof ImportInfo) { + return getTypeOfImportInfo(operator); + } + if (operator instanceof ArkClass) { + return TypeInference.replaceTypeWithReal(new ClassType(operator.getSignature(), operator.getGenericsTypes()), this.getRealGenericTypes()); + } + return UnknownType.getInstance(); + } + if (operator instanceof ImportInfo) { + return getTypeOfImportInfo(operator); + } + if (operator instanceof Local || operator instanceof ArkField) { + return operator.getType(); + } + if (operator instanceof ArkClass) { + return TypeInference.replaceTypeWithReal(new ClassType(operator.getSignature(), operator.getGenericsTypes()), this.getRealGenericTypes()); + } + if (operator instanceof ArkMethod) { + return TypeInference.replaceTypeWithReal(new FunctionType(operator.getSignature(), operator.getGenericTypes()), this.getRealGenericTypes()); + } + return UnknownType.getInstance(); + } + inferType(arkMethod) { + return IRInference.inferAliasTypeExpr(this, arkMethod); + } + /** + * Returns all used values which mainly used for def-use chain analysis. + * @returns Always returns empty array because her is the alias type definition which has no relationship with value flow. + */ + getUses() { + return []; + } + toString() { + let typeOf = ''; + if (this.getTransferWithTypeOf()) { + typeOf = 'typeof '; + } + const typeObject = this.getOriginalObject(); + if (typeObject instanceof AliasType && this.getRealGenericTypes()) { + return `${typeOf}${typeObject.getSignature().toString()}<${this.getRealGenericTypes().join(',')}>`; + } + if (typeObject instanceof Type) { + return `${typeOf}${typeObject.getTypeString()}`; + } + if (typeObject instanceof ImportInfo) { + let res = `${typeOf}import('${typeObject.getFrom()}')`; + if (typeObject.getImportClauseName() !== '') { + res = `${res}.${typeObject.getImportClauseName()}`; + } + return res; + } + if (typeObject instanceof Local) { + return `${typeOf}${typeObject.toString()}`; + } + if (typeObject instanceof ArkClass || typeObject instanceof ArkMethod) { + let res = `${typeOf}${typeObject.getSignature().toString()}`; + if (this.getRealGenericTypes() && typeObject instanceof ArkClass) { + res += `<${this.getRealGenericTypes().join(',')}>`; + } + else if (this.getRealGenericTypes() && typeObject instanceof ArkMethod) { + const genericTypes = this.getRealGenericTypes().join(','); + res = res.replace('(', `<${genericTypes}>(`).replace(/\([^)]*\)/g, `(${genericTypes})`); + } + return res; + } + return `${typeOf}${typeObject.getName()}`; + } + static isAliasTypeOriginalModel(object) { + return (object instanceof Type || + object instanceof ImportInfo || + object instanceof Local || + object instanceof ArkClass || + object instanceof ArkMethod || + object instanceof ArkField); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$e = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'PTA'); +class StatTraits { + TotalTime = 0; + startTime = 0; + endTime = 0; + getStat() { + return ''; + } + printStat() { + logger$e.trace(this.getStat()); + } +} +class PTAStat extends StatTraits { + pta; + numProcessedAddr = 0; + numProcessedCopy = 0; + numProcessedLoad = 0; + numProcessedWrite = 0; + numProcessedThis = 0; + numRealWrite = 0; + numRealLoad = 0; + numUnhandledFun = 0; + numTotalValuesInHandedFun = 0; + numTotalHandledValue = 0; + // Original type is UnknownType but inferred by PTA + numInferedUnknownValue = 0; + // Original type is not UnknownType and inferred with different type by PTA + numInferedDiffTypeValue = 0; + // Total number of values in the functions visited by PTA + totalValuesInVisitedFunc = 0; + // Original type is UnkonwnType and not inferred by PTA as well + numNotInferedUnknownValue = 0; + numUnhandledFunc = 0; + iterTimes = 0; + startMemUsage; + endMemUsage; + rssUsed = 0; + heapUsed = 0; + constructor(pta) { + super(); + this.pta = pta; + } + startStat() { + this.startTime = this.getNow(); + this.startMemUsage = process.memoryUsage(); + } + endStat() { + this.endTime = this.getNow(); + this.endMemUsage = process.memoryUsage(); + this.TotalTime = (this.endTime - this.startTime) / 1000; + this.rssUsed = Number(this.endMemUsage.rss - this.startMemUsage.rss) / Number(1024 * 1024); + this.heapUsed = Number(this.endMemUsage.heapTotal - this.startMemUsage.heapTotal) / Number(1024 * 1024); + this.getInferedStat(); + this.getUnhandledFuncStat(); + } + getNow() { + return new Date().getTime(); + } + getInferedStat() { + let stmtStat = (s) => { + if (!(s instanceof ArkAssignStmt)) { + return; + } + let lop = s.getLeftOp(); + if (visited.has(lop)) { + return; + } + visited.add(lop); + if (inferred.includes(lop)) { + if (lop.getType() instanceof UnknownType) { + this.numInferedUnknownValue++; + } + else { + this.numInferedDiffTypeValue++; + } + } + else { + if (lop.getType() instanceof UnknownType) { + this.numNotInferedUnknownValue++; + } + } + this.totalValuesInVisitedFunc++; + }; + let inferred = Array.from(this.pta.getTypeDiffMap().keys()); + let visited = new Set(); + let cg = this.pta.getCallGraph(); + this.pta.getHandledFuncs().forEach(funcID => { + let f = cg.getArkMethodByFuncID(funcID); + f + ?.getCfg() + ?.getStmts() + .forEach(s => stmtStat(s)); + }); + } + getUnhandledFuncStat() { + let cg = this.pta.getCallGraph(); + this.pta.getUnhandledFuncs().forEach(funcID => { + let cgNode = cg.getNode(funcID); + if (cgNode.isSdkMethod()) { + return; + } + let f = cg.getArkMethodByFuncID(funcID); + if (f) { + this.numUnhandledFun++; + } + }); + } + getStat() { + // TODO: get PAG stat and CG stat + let output; + output = '==== Pointer analysis Statictics: ====\n'; + output = output + `Processed address\t${this.numProcessedAddr}\n`; + output = output + `Processed copy\t\t${this.numProcessedCopy}\n`; + output = output + `Processed load\t\t${this.numProcessedLoad}\n`; + output = output + `Processed write\t\t${this.numProcessedWrite}\n`; + output = output + `Real write\t\t${this.numRealWrite}\n`; + output = output + `Real load\t\t${this.numRealLoad}\n`; + output = output + `Processed This\t\t${this.numProcessedThis}\n\n`; + output = output + `Unhandled function\t${this.numUnhandledFun}\n`; + output = output + `Total values in visited function\t${this.totalValuesInVisitedFunc}\n`; + output = output + `Infered Value unknown+different type\t${this.numInferedUnknownValue}+${this.numInferedDiffTypeValue}\n\n`; + output = output + `Total Time\t\t${this.TotalTime} S\n`; + output = output + `Total iterator Times\t${this.iterTimes}\n`; + output = output + `RSS used\t\t${this.rssUsed.toFixed(3)} Mb\n`; + output = output + `Heap used\t\t${this.heapUsed.toFixed(3)} Mb\n`; + return output; + } + printStat() { + logger$e.trace(this.getStat()); + } +} +class PAGStat extends StatTraits { + numDynamicCall = 0; + numTotalFunction = 0; + numTotalNode = 0; + getStat() { + let output; + output = '==== PAG Statictics: ====\n'; + output = output + `Dynamic call\t\t${this.numDynamicCall}\n`; + output = output + `Total function handled\t${this.numTotalFunction}\n`; + output = output + `Total PAG Nodes\t\t${this.numTotalNode}\n`; + return output; + } + printStat() { + logger$e.trace(this.getStat()); + } +} +class CGStat extends StatTraits { + //real, vitual, intrinsic, constructor + numTotalNode = 0; + numReal = 0; + numVirtual = 0; + numIntrinsic = 0; + numConstructor = 0; + numBlank = 0; + startStat() { + this.startTime = new Date().getTime(); + } + endStat() { + this.endTime = new Date().getTime(); + this.TotalTime = (this.endTime - this.startTime) / 1000; + } + addNodeStat(kind) { + switch (kind) { + case exports.CallGraphNodeKind.real: + this.numReal++; + break; + case exports.CallGraphNodeKind.vitual: + this.numVirtual++; + break; + case exports.CallGraphNodeKind.constructor: + this.numConstructor++; + break; + case exports.CallGraphNodeKind.intrinsic: + this.numIntrinsic++; + break; + default: + this.numBlank++; + } + this.numTotalNode++; + } + getStat() { + let output; + output = '==== CG Statictics: ====\n'; + output = output + `CG construction Total Time\t\t${this.TotalTime} S\n`; + output = output + `Real function\t\t${this.numReal}\n`; + output = output + `Intrinsic function\t${this.numIntrinsic}\n`; + output = output + `Constructor function\t${this.numConstructor}\n`; + output = output + `Virtual function\t\t${this.numVirtual}\n`; + output = output + `Blank function\t\t${this.numBlank}\n`; + output = output + `Total\t\t\t${this.numTotalNode}\n`; + return output; + } +} + +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class CallSite { + callStmt; + args; + calleeFuncID; + callerFuncID; + constructor(s, a, ce, cr) { + this.callStmt = s; + this.args = a; + this.calleeFuncID = ce; + this.callerFuncID = cr; + } +} +class DynCallSite { + callStmt; + args; + protentialCalleeFuncID; + callerFuncID; + constructor(s, a, ptcCallee, caller) { + this.callerFuncID = caller; + this.callStmt = s; + this.args = a; + this.protentialCalleeFuncID = ptcCallee; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +exports.CallGraphNodeKind = void 0; +(function (CallGraphNodeKind) { + CallGraphNodeKind[CallGraphNodeKind["real"] = 0] = "real"; + CallGraphNodeKind[CallGraphNodeKind["vitual"] = 1] = "vitual"; + CallGraphNodeKind[CallGraphNodeKind["intrinsic"] = 2] = "intrinsic"; + CallGraphNodeKind[CallGraphNodeKind["constructor"] = 3] = "constructor"; + CallGraphNodeKind[CallGraphNodeKind["blank"] = 4] = "blank"; +})(exports.CallGraphNodeKind || (exports.CallGraphNodeKind = {})); +class CallGraphEdge extends BaseEdge { + directCalls = new Set(); + specialCalls = new Set(); + indirectCalls = new Set(); + // private callSiteID: CallSiteID; + constructor(src, dst) { + super(src, dst, 0); + } + addDirectCallSite(stmt) { + this.directCalls.add(stmt); + } + addSpecialCallSite(stmt) { + this.specialCalls.add(stmt); + } + addInDirectCallSite(stmt) { + this.indirectCalls.add(stmt); + } + getDotAttr() { + const indirectCallNums = this.indirectCalls.size; + const directCallNums = this.directCalls.size; + const specialCallNums = this.specialCalls.size; + if ([exports.CallGraphNodeKind.intrinsic, exports.CallGraphNodeKind.constructor].includes(this.getDstNode().getKind())) { + return ''; + } + if (indirectCallNums !== 0 && directCallNums === 0) { + return 'color=red'; + } + else if (specialCallNums !== 0) { + return 'color=yellow'; + } + else if (indirectCallNums === 0 && directCallNums !== 0) { + return 'color=black'; + } + else { + return 'color=black'; + } + } +} +class CallGraphNode extends BaseNode { + method; + ifSdkMethod = false; + constructor(id, m, k = exports.CallGraphNodeKind.real) { + super(id, k); + this.method = m; + } + getMethod() { + return this.method; + } + setSdkMethod(v) { + this.ifSdkMethod = v; + } + isSdkMethod() { + return this.ifSdkMethod; + } + get isBlankMethod() { + return this.kind === exports.CallGraphNodeKind.blank; + } + getDotAttr() { + if ([exports.CallGraphNodeKind.intrinsic, exports.CallGraphNodeKind.constructor].includes(this.getKind())) { + return ''; + } + return 'shape=box'; + } + getDotLabel() { + let label = 'ID: ' + this.getID() + '\n'; + label = label + this.getMethod().toString(); + return label; + } +} +class CallGraph extends BaseExplicitGraph { + scene; + idToCallSiteMap = new Map(); + callSiteToIdMap = new Map(); + stmtToCallSitemap = new Map(); + stmtToDynCallSitemap = new Map(); + methodToCGNodeMap = new Map(); + callPairToEdgeMap = new Map(); + methodToCallSiteMap = new Map(); + callSiteNum = 0; + entries; + cgStat; + dummyMainMethodID; + constructor(s) { + super(); + this.scene = s; + this.cgStat = new CGStat(); + } + getCallPairString(srcID, dstID) { + return `${srcID}-${dstID}`; + } + getCallEdgeByPair(srcID, dstID) { + let key = this.getCallPairString(srcID, dstID); + return this.callPairToEdgeMap.get(key); + } + addCallGraphNode(method, kind = exports.CallGraphNodeKind.real) { + let id = this.nodeNum; + let cgNode = new CallGraphNode(id, method, kind); + // check if sdk method + cgNode.setSdkMethod(this.scene.hasSdkFile(method.getDeclaringClassSignature().getDeclaringFileSignature())); + this.addNode(cgNode); + this.methodToCGNodeMap.set(method.toString(), cgNode.getID()); + this.cgStat.addNodeStat(kind); + return cgNode; + } + removeCallGraphNode(nodeID) { + // remove edge relate to node first + this.removeCallGraphEdge(nodeID); + let node = this.getNode(nodeID); + // remove node itself + this.removeNode(nodeID); + this.methodToCGNodeMap.delete(node.getMethod().toString()); + } + getCallGraphNodeByMethod(method) { + if (!method) { + throw new Error(); + } + let n = this.methodToCGNodeMap.get(method.toString()); + if (n === undefined) { + // The method can't be found + // means the method has no implementation, or base type is unclear to find it + // Create a virtual CG Node + // TODO: this virtual CG Node need be remove once the base type is clear + return this.addCallGraphNode(method, exports.CallGraphNodeKind.vitual); + } + return this.getNode(n); + } + addDirectOrSpecialCallEdge(caller, callee, callStmt, isDirectCall = true) { + let callerNode = this.getCallGraphNodeByMethod(caller); + let calleeNode = this.getCallGraphNodeByMethod(callee); + let args = callStmt.getInvokeExpr()?.getArgs(); + let cs = new CallSite(callStmt, args, calleeNode.getID(), callerNode.getID()); + let csID; + if (!this.callSiteToIdMap.has(cs)) { + csID = this.callSiteNum++; + this.idToCallSiteMap.set(csID, cs); + this.callSiteToIdMap.set(cs, csID); + } + else { + csID = this.callSiteToIdMap.get(cs); + } + if (this.addStmtToCallSiteMap(callStmt, cs)) ; + // TODO: check if edge exists + let callEdge = this.getCallEdgeByPair(callerNode.getID(), calleeNode.getID()); + if (callEdge === undefined) { + callEdge = new CallGraphEdge(callerNode, calleeNode); + callEdge.getSrcNode().addOutgoingEdge(callEdge); + callEdge.getDstNode().addIncomingEdge(callEdge); + this.callPairToEdgeMap.set(this.getCallPairString(callerNode.getID(), calleeNode.getID()), callEdge); + } + if (isDirectCall) { + callEdge.addDirectCallSite(callStmt); + } + else { + callEdge.addSpecialCallSite(callStmt); + } + } + removeCallGraphEdge(nodeID) { + let node = this.getNode(nodeID); + for (const inEdge of node.getIncomingEdge()) { + node.removeIncomingEdge(inEdge); + } + for (const outEdge of node.getOutgoingEdges()) { + node.removeIncomingEdge(outEdge); + } + } + addDynamicCallInfo(callStmt, caller, protentialCallee) { + let callerNode = this.getCallGraphNodeByMethod(caller); + let calleeNode; + if (protentialCallee) { + calleeNode = this.getCallGraphNodeByMethod(protentialCallee); + } + let args = callStmt.getInvokeExpr()?.getArgs(); + let cs = new DynCallSite(callStmt, args, calleeNode?.getID(), callerNode.getID()); + this.stmtToDynCallSitemap.set(callStmt, cs); + } + addDynamicCallEdge(callerID, calleeID, callStmt) { + let callerNode = this.getNode(callerID); + let calleeNode = this.getNode(calleeID); + let callEdge = this.getCallEdgeByPair(callerNode.getID(), calleeNode.getID()); + if (callEdge === undefined) { + callEdge = new CallGraphEdge(callerNode, calleeNode); + callEdge.getSrcNode().addOutgoingEdge(callEdge); + callEdge.getDstNode().addIncomingEdge(callEdge); + this.callPairToEdgeMap.set(this.getCallPairString(callerNode.getID(), calleeNode.getID()), callEdge); + } + callEdge.addInDirectCallSite(callStmt); + } + getDynCallsiteByStmt(stmt) { + return this.stmtToDynCallSitemap.get(stmt); + } + addStmtToCallSiteMap(stmt, cs) { + if (this.stmtToCallSitemap.has(stmt)) { + let callSites = this.stmtToCallSitemap.get(stmt) ?? []; + this.stmtToCallSitemap.set(stmt, [...callSites, cs]); + return false; + } + this.stmtToCallSitemap.set(stmt, [cs]); + return true; + } + getCallSiteByStmt(stmt) { + return this.stmtToCallSitemap.get(stmt) ?? []; + } + addMethodToCallSiteMap(funcID, cs) { + if (this.methodToCallSiteMap.has(funcID)) { + this.methodToCallSiteMap.get(funcID).add(cs); + } + else { + this.methodToCallSiteMap.set(funcID, new Set([cs])); + } + } + getCallSitesByMethod(func) { + let funcID; + if (func instanceof MethodSignature) { + funcID = this.getCallGraphNodeByMethod(func).getID(); + } + else { + funcID = func; + } + return this.methodToCallSiteMap.get(funcID) ?? new Set(); + } + getInvokeStmtByMethod(func) { + let callSites = this.getCallSitesByMethod(func); + let invokeStmts = []; + callSites.forEach(cs => { + invokeStmts.push(cs.callStmt); + }); + return invokeStmts; + } + getDynEdges() { + let callMap = new Map(); + this.callPairToEdgeMap.forEach((edge) => { + let srcMethod = edge.getSrcNode().getMethod(); + let dstMethod = edge.getDstNode().getMethod(); + let dstSet; + if (callMap.has(srcMethod)) { + dstSet = callMap.get(srcMethod); + } + else { + dstSet = new Set(); + } + callMap.set(srcMethod, dstSet.add(dstMethod)); + }); + return callMap; + } + getMethodByFuncID(id) { + let node = this.getNode(id); + if (node !== undefined) { + return node.getMethod(); + } + return null; + } + getArkMethodByFuncID(id) { + let method = this.getMethodByFuncID(id); + if (method != null) { + // TODO: SDK Method search + return this.scene.getMethod(method); + } + return null; + } + getEntries() { + return this.entries; + } + setEntries(n) { + this.entries = n; + } + dump(name, entry) { + let printer = new GraphPrinter(this); + if (entry) { + printer.setStartID(entry); + } + PrinterBuilder.dump(printer, name); + } + detectReachable(fromID, dstID) { + let dWorklist = []; + let travserdFuncs = new Set(); + dWorklist.push(fromID); + while (dWorklist.length > 0) { + let nodeID = dWorklist.shift(); + if (travserdFuncs.has(nodeID)) { + continue; + } + travserdFuncs.add(nodeID); + let node = this.getNode(nodeID); + for (let e of node.getOutgoingEdges()) { + let dst = e.getDstID(); + if (dst === dstID) { + return true; + } + dWorklist.push(dst); + } + } + return false; + } + startStat() { + this.cgStat.startStat(); + } + endStat() { + this.cgStat.endStat(); + } + printStat() { + this.cgStat.printStat(); + } + getStat() { + return this.cgStat.getStat(); + } + setDummyMainFuncID(dummyMainMethodID) { + this.dummyMainMethodID = dummyMainMethodID; + } + getDummyMainFuncID() { + return this.dummyMainMethodID; + } + isUnknownMethod(funcID) { + let method = this.getMethodByFuncID(funcID); + if (method) { + if (!(method.getDeclaringClassSignature().getDeclaringFileSignature().getFileName() === UNKNOWN_FILE_NAME)) { + return false; + } + } + return true; + } + getGraphName() { + return 'CG'; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class ClassHierarchyAnalysis extends AbstractAnalysis { + constructor(scene, cg, cb) { + super(scene, cg); + this.cgBuilder = cb; + } + resolveCall(callerMethod, invokeStmt) { + let invokeExpr = invokeStmt.getInvokeExpr(); + let resolveResult = []; + if (!invokeExpr) { + return []; + } + // process anonymous method call + this.getParamAnonymousMethod(invokeExpr).forEach(method => { + resolveResult.push(new CallSite(invokeStmt, undefined, this.cg.getCallGraphNodeByMethod(method).getID(), callerMethod)); + }); + let calleeMethod = this.resolveInvokeExpr(invokeExpr); + if (!calleeMethod) { + return resolveResult; + } + if (invokeExpr instanceof ArkStaticInvokeExpr) { + // get specific method + resolveResult.push(new CallSite(invokeStmt, undefined, this.cg.getCallGraphNodeByMethod(calleeMethod.getSignature()).getID(), callerMethod)); + } + else { + let declareClass = calleeMethod.getDeclaringArkClass(); + // TODO: super class method should be placed at the end + this.getClassHierarchy(declareClass).forEach((arkClass) => { + if (arkClass.isAbstract()) { + return; + } + let possibleCalleeMethod = arkClass.getMethodWithName(calleeMethod.getName()); + if (possibleCalleeMethod && + possibleCalleeMethod.isGenerated() && + arkClass.getSignature().toString() !== declareClass.getSignature().toString()) { + // remove the generated method in extended classes + return; + } + if (possibleCalleeMethod && !possibleCalleeMethod.isAbstract()) { + resolveResult.push(new CallSite(invokeStmt, undefined, this.cg.getCallGraphNodeByMethod(possibleCalleeMethod.getSignature()).getID(), callerMethod)); + } + }); + } + return resolveResult; + } + preProcessMethod() { + // do nothing + return []; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$d = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'RTA'); +class RapidTypeAnalysis extends AbstractAnalysis { + // TODO: signature duplicated check + instancedClasses = new Set(); + // TODO: Set duplicated check + ignoredCalls = new Map(); + constructor(scene, cg) { + super(scene, cg); + } + resolveCall(callerMethod, invokeStmt) { + let invokeExpr = invokeStmt.getInvokeExpr(); + let resolveResult = []; + if (!invokeExpr) { + return []; + } + // process anonymous method call + this.getParamAnonymousMethod(invokeExpr).forEach(method => { + resolveResult.push(new CallSite(invokeStmt, undefined, this.cg.getCallGraphNodeByMethod(method).getID(), callerMethod)); + }); + let calleeMethod = this.resolveInvokeExpr(invokeExpr); + if (!calleeMethod) { + return resolveResult; + } + if (invokeExpr instanceof ArkStaticInvokeExpr) { + // get specific method + resolveResult.push(new CallSite(invokeStmt, undefined, this.cg.getCallGraphNodeByMethod(calleeMethod.getSignature()).getID(), callerMethod)); + } + else { + let declareClass = calleeMethod.getDeclaringArkClass(); + // TODO: super class method should be placed at the end + this.getClassHierarchy(declareClass).forEach((arkClass) => { + if (arkClass.isAbstract()) { + return; + } + let possibleCalleeMethod = arkClass.getMethodWithName(calleeMethod.getName()); + if (possibleCalleeMethod && + possibleCalleeMethod.isGenerated() && + arkClass.getSignature().toString() !== declareClass.getSignature().toString()) { + // remove the generated method in extended classes + return; + } + if (!(possibleCalleeMethod && !possibleCalleeMethod.isAbstract())) { + return; + } + if (!this.instancedClasses.has(arkClass.getSignature())) { + this.addIgnoredCalls(arkClass.getSignature(), callerMethod, this.cg.getCallGraphNodeByMethod(possibleCalleeMethod.getSignature()).getID(), invokeStmt); + } + else { + resolveResult.push(new CallSite(invokeStmt, undefined, this.cg.getCallGraphNodeByMethod(possibleCalleeMethod.getSignature()).getID(), callerMethod)); + } + }); + } + return resolveResult; + } + preProcessMethod(funcID) { + let newCallSites = []; + let instancedClasses = this.collectInstancedClassesInMethod(funcID); + let newlyInstancedClasses = new Set(Array.from(instancedClasses).filter(item => !this.instancedClasses.has(item))); + newlyInstancedClasses.forEach(sig => { + let ignoredCalls = this.ignoredCalls.get(sig); + if (ignoredCalls) { + ignoredCalls.forEach(call => { + this.cg.addDynamicCallEdge(call.caller, call.callee, call.callStmt); + newCallSites.push(new CallSite(call.callStmt, undefined, call.callee, call.caller)); + }); + } + this.instancedClasses.add(sig); + this.ignoredCalls.delete(sig); + }); + return newCallSites; + } + collectInstancedClassesInMethod(funcID) { + let instancedClasses = new Set(); + let arkMethod = this.cg.getArkMethodByFuncID(funcID); + if (!arkMethod) { + logger$d.error(`can not find arkMethod by funcID`); + return instancedClasses; + } + let cfg = arkMethod.getCfg(); + if (!cfg) { + logger$d.error(`arkMethod ${arkMethod.getSignature().toString()} has no cfg`); + return instancedClasses; + } + for (let stmt of cfg.getStmts()) { + let stmtExpr = stmt.getExprs()[0]; + if (stmtExpr instanceof ArkNewExpr) { + let classSig = stmtExpr.getType().getClassSignature(); + if (classSig != null) { + // TODO: need to check if different stmt has single sig + instancedClasses.add(classSig); + } + } + } + return instancedClasses; + } + addIgnoredCalls(arkClass, callerID, calleeID, invokeStmt) { + let classMap = this.ignoredCalls.get(arkClass) ?? new Set(); + classMap.add({ caller: callerID, callee: calleeID, callStmt: invokeStmt }); + this.ignoredCalls.set(arkClass, classMap); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class CallGraphBuilder { + cg; + scene; + constructor(c, s) { + this.cg = c; + this.scene = s; + } + buildDirectCallGraphForScene() { + const methods = this.scene.getMethods(); + this.buildDirectCallGraph(methods); + // set entries at end + this.setEntries(); + } + /* + * Create CG Node for ArkMethods + */ + buildCGNodes(methods) { + for (const method of methods) { + let m = method.getSignature(); + let kind = exports.CallGraphNodeKind.real; + if (method.isGenerated()) { + kind = exports.CallGraphNodeKind.intrinsic; + } + else if (method.getBody() === undefined || method.getCfg() === undefined) { + kind = exports.CallGraphNodeKind.blank; + } + else if (method.getName() === 'constructor') { + kind = exports.CallGraphNodeKind.constructor; + } + this.cg.addCallGraphNode(m, kind); + } + } + buildDirectCallGraph(methods) { + this.buildCGNodes(methods); + for (const method of methods) { + let cfg = method.getCfg(); + if (cfg === undefined) { + // abstract method cfg is undefined + continue; + } + let stmts = cfg.getStmts(); + for (const stmt of stmts) { + let invokeExpr = stmt.getInvokeExpr(); + if (invokeExpr === undefined) { + continue; + } + let callee = this.getDCCallee(invokeExpr); + // abstract method will also be added into direct cg + if (callee && invokeExpr instanceof ArkStaticInvokeExpr) { + this.cg.addDirectOrSpecialCallEdge(method.getSignature(), callee, stmt); + } + else if (callee && invokeExpr instanceof ArkInstanceInvokeExpr && + (this.isConstructor(callee) || this.scene.getMethod(callee)?.isGenerated())) { + this.cg.addDirectOrSpecialCallEdge(method.getSignature(), callee, stmt, false); + } + else { + this.cg.addDynamicCallInfo(stmt, method.getSignature(), callee); + } + } + } + } + buildClassHierarchyCallGraph(entries, displayGeneratedMethod = false) { + let cgEntries = []; + entries.forEach((entry) => { + cgEntries.push(this.cg.getCallGraphNodeByMethod(entry).getID()); + }); + this.cg.setEntries(cgEntries); + let classHierarchyAnalysis = new ClassHierarchyAnalysis(this.scene, this.cg, this); + classHierarchyAnalysis.start(displayGeneratedMethod); + } + buildCHA4WholeProject(displayGeneratedMethod = false) { + let classHierarchyAnalysis = new ClassHierarchyAnalysis(this.scene, this.cg, this); + classHierarchyAnalysis.projectStart(displayGeneratedMethod); + } + buildRapidTypeCallGraph(entries, displayGeneratedMethod = false) { + let cgEntries = []; + entries.forEach((entry) => { + cgEntries.push(this.cg.getCallGraphNodeByMethod(entry).getID()); + }); + this.cg.setEntries(cgEntries); + let rapidTypeAnalysis = new RapidTypeAnalysis(this.scene, this.cg); + rapidTypeAnalysis.start(displayGeneratedMethod); + } + /// Get direct call callee + getDCCallee(invokeExpr) { + return invokeExpr.getMethodSignature(); + } + isConstructor(m) { + return m.getMethodSubSignature().getMethodName() === 'constructor'; + } + setEntries() { + let nodesIter = this.cg.getNodesIter(); + let entries = Array.from(nodesIter) + .filter(node => !node.hasIncomingEdges() && node.getKind() === exports.CallGraphNodeKind.real && !node.isBlankMethod) + .map(node => node.getID()); + this.cg.setEntries(entries); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const DUMMY_CID = 0; +class Context { + contextElems; + static sEmptyCtx = new Context([]); + constructor(contextElems = []) { + this.contextElems = contextElems; + } + static newEmpty() { + return new Context(); + } + static new(contextElems) { + return new Context(contextElems); + } + // use old context and a new element to create a new k-limited Context + static newKLimitedContext(oldCtx, elem, k) { + let elems = []; + if (k > 0) { + elems.push(elem); + if (oldCtx.contextElems.length < k) { + elems = elems.concat(oldCtx.contextElems); + } + else { + elems = elems.concat(oldCtx.contextElems.slice(0, k - 1)); + } + } + return new Context(elems); + } + static kLimitedContext(ctx, k) { + if (ctx.length() <= k) { + return new Context(ctx.contextElems); + } + else { + const elems = ctx.contextElems.slice(0, k); + return new Context(elems); + } + } + length() { + return this.contextElems.length; + } + get(index) { + if (index < 0 || index >= this.contextElems.length) { + throw new Error('Index out of bounds'); + } + return this.contextElems[index]; + } + toString() { + return this.contextElems.join('-'); + } +} +class ContextCache { + contextList = []; + contextToIDMap = new Map(); + constructor() { + this.contextList = []; + this.contextToIDMap = new Map(); + } + getOrNewContextID(context) { + let cStr = context.toString(); + if (this.contextToIDMap.has(cStr)) { + return this.contextToIDMap.get(cStr); + } + else { + // real cid start from 1 + const id = this.contextList.length; + this.contextList.push(context); + this.contextToIDMap.set(cStr, id); + return id; + } + } + updateContext(id, newContext, oldContext) { + if (this.contextList.length < id) { + return false; + } + this.contextList[id] = newContext; + let oldCStr = oldContext.toString(); + let newCStr = newContext.toString(); + this.contextToIDMap.delete(oldCStr); + this.contextToIDMap.set(newCStr, id); + return true; + } + getContextID(context) { + let cStr = context.toString(); + if (this.contextToIDMap.has(cStr)) { + return this.contextToIDMap.get(cStr); + } + return undefined; + } + getContext(id) { + if (id > this.contextList.length) { + return undefined; + } + return this.contextList[id]; + } + getContextList() { + return this.contextList; + } +} +class KLimitedContextSensitive { + k; + ctxCache; + constructor(k) { + this.k = k; + this.ctxCache = new ContextCache(); + // put dummy cid + this.getEmptyContextID(); + } + emptyContext() { + return new Context([]); + } + getEmptyContextID() { + return this.getContextID(Context.newEmpty()); + } + getContextID(context) { + return this.ctxCache.getOrNewContextID(context); + } + getContextByID(context_id) { + return this.ctxCache.getContext(context_id); + } + getNewContextID(callerFuncId) { + return this.ctxCache.getOrNewContextID(Context.new([callerFuncId])); + } + getOrNewContext(callerCid, calleeFuncId, findCalleeAsTop = false) { + const callerCtx = this.ctxCache.getContext(callerCid); + if (!callerCtx) { + throw new Error(`Context with id ${callerCid} not found.`); + } + const calleeNewCtx = Context.newKLimitedContext(callerCtx, calleeFuncId, this.k); + if (findCalleeAsTop) { + const calleeAsTopCtx = Context.newKLimitedContext(Context.sEmptyCtx, calleeFuncId, this.k); + let topID = this.ctxCache.getContextID(calleeAsTopCtx); + if (topID) { + this.ctxCache.updateContext(topID, calleeNewCtx, calleeAsTopCtx); + return topID; + } + } + const calleeCid = this.ctxCache.getOrNewContextID(calleeNewCtx); + return calleeCid; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$c = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'Dummy Call'); +/** + * TODO: constructor pointer and cid + */ +class DummyCallCreator { + scene; + pageMap; + // TODO: classSig -> str ? + componentMap; + constructor(scene) { + this.scene = scene; + this.componentMap = new Map(); + this.pageMap = new Map(); + } + getDummyCallByPage(classSig, basePage) { + let dummyCallStmts = this.pageMap.get(classSig); + if (dummyCallStmts) { + return dummyCallStmts; + } + dummyCallStmts = this.buildDummyCallBody(classSig, basePage); + this.pageMap.set(classSig, dummyCallStmts); + return dummyCallStmts; + } + getDummyCallByComponent(classSig, baseComponent) { + let dummyCallStmts = this.componentMap.get(classSig); + if (dummyCallStmts) { + return dummyCallStmts; + } + dummyCallStmts = this.buildDummyCallBody(classSig, baseComponent); + this.componentMap.set(classSig, dummyCallStmts); + return dummyCallStmts; + } + /** + * build dummy call edge with class signature, including a class new expr and call back function invokes + * @param classSig class signature + * @returns dummy call edges + */ + buildDummyCallBody(classSig, baseComponent) { + let dummyCallStmts = new Set(); + this.getComponentCallStmts(classSig, baseComponent).forEach(stmt => dummyCallStmts.add(stmt)); + return dummyCallStmts; + } + getComponentCallStmts(classSig, base) { + let componentClass = this.scene.getClass(classSig); + if (!componentClass) { + logger$c.error(`can not find class ${classSig.toString()}`); + return []; + } + let callStmts = []; + // filter callback method + componentClass + .getMethods() + .filter(method => COMPONENT_LIFECYCLE_METHOD_NAME.includes(method.getName())) + .forEach((method) => { + // TODO: args pointer ? + if (method.getParameters().length === 0) { + callStmts.push(new ArkInvokeStmt(new ArkInstanceInvokeExpr(base, method.getSignature(), []))); + } + else { + logger$c.warn(`parameters in callback function hasn't been processed: ${method.getSignature().toString()}`); + } + }); + return callStmts; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +function IsCollectionClass(classSignature) { + if (classSignature.toString().endsWith('lib.es2015.collection.d.ts: Set') || classSignature.toString().endsWith('lib.es2015.collection.d.ts: Map')) { + return true; + } + return false; +} +var BuiltApiType; +(function (BuiltApiType) { + BuiltApiType[BuiltApiType["SetAdd"] = 0] = "SetAdd"; + BuiltApiType[BuiltApiType["MapSet"] = 1] = "MapSet"; + BuiltApiType[BuiltApiType["FunctionCall"] = 2] = "FunctionCall"; + BuiltApiType[BuiltApiType["FunctionApply"] = 3] = "FunctionApply"; + BuiltApiType[BuiltApiType["FunctionBind"] = 4] = "FunctionBind"; + BuiltApiType[BuiltApiType["NotBuiltIn"] = 5] = "NotBuiltIn"; +})(BuiltApiType || (BuiltApiType = {})); +function getBuiltInApiType(method) { + let methodSigStr = method.toString(); + const regex = /lib\.es5\.d\.ts: Function\.(call|apply|bind)\(/; + if (methodSigStr.endsWith('lib.es2015.collection.d.ts: Set.add(T)')) { + return BuiltApiType.SetAdd; + } + else if (methodSigStr.endsWith('lib.es2015.collection.d.ts: Map.set(K, V)')) { + return BuiltApiType.MapSet; + } + else { + const match = methodSigStr.match(regex); + if (match) { + const functionName = match[1]; + switch (functionName) { + case 'call': + return BuiltApiType.FunctionCall; + case 'apply': + return BuiltApiType.FunctionApply; + case 'bind': + return BuiltApiType.FunctionBind; + } + } + } + return BuiltApiType.NotBuiltIn; +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var PtaAnalysisScale; +(function (PtaAnalysisScale) { + PtaAnalysisScale[PtaAnalysisScale["WholeProgram"] = 0] = "WholeProgram"; + PtaAnalysisScale[PtaAnalysisScale["MethodLevel"] = 1] = "MethodLevel"; +})(PtaAnalysisScale || (PtaAnalysisScale = {})); +class PointerAnalysisConfig { + static instance; + kLimit; + outputDirectory; + detectTypeDiff; + dotDump; + unhandledFuncDump; + analysisScale; + ptsCollectionType; + ptsCollectionCtor; + /* + * Note: DO NOT use `new PointerAnalysisConfig` to initialize ptaconfig + * Use PointerAnalysisConfig.create() for singleton pattern + */ + constructor(kLimit, outputDirectory, detectTypeDiff = false, dotDump = false, unhandledFuncDump = false, analysisScale = PtaAnalysisScale.WholeProgram, ptsCoType = PtsCollectionType.Set) { + if (kLimit > 5) { + throw new Error('K Limit too large'); + } + this.kLimit = kLimit; + this.outputDirectory = outputDirectory; + this.detectTypeDiff = detectTypeDiff; + this.dotDump = dotDump; + this.unhandledFuncDump = unhandledFuncDump; + this.analysisScale = analysisScale; + this.ptsCollectionType = ptsCoType; + this.ptsCollectionCtor = createPtsCollectionCtor(ptsCoType); + if (!fs__namespace.existsSync(outputDirectory)) { + fs__namespace.mkdirSync(outputDirectory, { recursive: true }); + } + } + /* + * Create Singleton instance + * The instance can be created multi-times and be overwrited + */ + static create(kLimit, outputDirectory, detectTypeDiff = false, dotDump = false, unhandledFuncDump = false, analysisScale = PtaAnalysisScale.WholeProgram, ptsCoType = PtsCollectionType.Set) { + PointerAnalysisConfig.instance = new PointerAnalysisConfig(kLimit, outputDirectory, detectTypeDiff, dotDump, unhandledFuncDump, analysisScale, ptsCoType); + return PointerAnalysisConfig.instance; + } + /* + * Get Singleton instance + */ + static getInstance() { + if (!PointerAnalysisConfig.instance) { + throw new Error('PTA config: instance is not existing'); + } + return PointerAnalysisConfig.instance; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$b = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'PTA'); +/* + * Implementation of pointer-to assignment graph for pointer analysis + */ +const DUMMY_PAG_NODE_ID = -1; +exports.PagEdgeKind = void 0; +(function (PagEdgeKind) { + PagEdgeKind[PagEdgeKind["Address"] = 0] = "Address"; + PagEdgeKind[PagEdgeKind["Copy"] = 1] = "Copy"; + PagEdgeKind[PagEdgeKind["Load"] = 2] = "Load"; + PagEdgeKind[PagEdgeKind["Write"] = 3] = "Write"; + PagEdgeKind[PagEdgeKind["This"] = 4] = "This"; + PagEdgeKind[PagEdgeKind["Unknown"] = 5] = "Unknown"; + PagEdgeKind[PagEdgeKind["InterProceduralCopy"] = 6] = "InterProceduralCopy"; +})(exports.PagEdgeKind || (exports.PagEdgeKind = {})); +exports.StorageType = void 0; +(function (StorageType) { + StorageType[StorageType["APP_STORAGE"] = 0] = "APP_STORAGE"; + StorageType[StorageType["LOCAL_STORAGE"] = 1] = "LOCAL_STORAGE"; + StorageType[StorageType["Undefined"] = 2] = "Undefined"; +})(exports.StorageType || (exports.StorageType = {})); +exports.StorageLinkEdgeType = void 0; +(function (StorageLinkEdgeType) { + StorageLinkEdgeType[StorageLinkEdgeType["Property2Local"] = 0] = "Property2Local"; + StorageLinkEdgeType[StorageLinkEdgeType["Local2Property"] = 1] = "Local2Property"; + StorageLinkEdgeType[StorageLinkEdgeType["TwoWay"] = 2] = "TwoWay"; +})(exports.StorageLinkEdgeType || (exports.StorageLinkEdgeType = {})); +class PagEdge extends BaseEdge { + stmt; + constructor(n, d, k, s) { + super(n, d, k); + this.stmt = s; + } + getDotAttr() { + switch (this.getKind()) { + case exports.PagEdgeKind.Address: + return 'color=green'; + case exports.PagEdgeKind.Copy: + if (this.stmt?.getInvokeExpr() !== undefined || this.stmt instanceof ArkReturnStmt) { + return 'color=black,style=dotted'; + } + return 'color=black'; + case exports.PagEdgeKind.Load: + return 'color=red'; + case exports.PagEdgeKind.Write: + return 'color=blue'; + case exports.PagEdgeKind.This: + return 'color=orange'; + case exports.PagEdgeKind.InterProceduralCopy: + return 'color=purple,style=dashed'; + default: + return 'color=black'; + } + } +} +class AddrPagEdge extends PagEdge { + constructor(n, d, s) { + super(n, d, exports.PagEdgeKind.Address, s); + } +} +class CopyPagEdge extends PagEdge { + constructor(n, d, s) { + super(n, d, exports.PagEdgeKind.Copy, s); + } +} +class LoadPagEdge extends PagEdge { + constructor(n, d, s) { + super(n, d, exports.PagEdgeKind.Copy, s); + } +} +class WritePagEdge extends PagEdge { + constructor(n, d, s) { + super(n, d, exports.PagEdgeKind.Write, s); + } +} +class ThisPagEdge extends PagEdge { + constructor(n, d, s) { + super(n, d, exports.PagEdgeKind.This, s); + } +} +exports.PagNodeKind = void 0; +(function (PagNodeKind) { + PagNodeKind[PagNodeKind["HeapObj"] = 0] = "HeapObj"; + PagNodeKind[PagNodeKind["LocalVar"] = 1] = "LocalVar"; + PagNodeKind[PagNodeKind["RefVar"] = 2] = "RefVar"; + PagNodeKind[PagNodeKind["Param"] = 3] = "Param"; + PagNodeKind[PagNodeKind["ThisRef"] = 4] = "ThisRef"; + PagNodeKind[PagNodeKind["Function"] = 5] = "Function"; + PagNodeKind[PagNodeKind["GlobalThis"] = 6] = "GlobalThis"; + PagNodeKind[PagNodeKind["ExportInfo"] = 7] = "ExportInfo"; +})(exports.PagNodeKind || (exports.PagNodeKind = {})); +class PagNode extends BaseNode { + cid; + value; + stmt; // stmt is just used for graph print + pointTo; + addressInEdges; + addressOutEdges; + copyInEdges; + copyOutEdges; + loadInEdges; + loadOutEdges; + writeInEdges; + writeOutEdges; + thisInEdges; + thisOutEdges; + // Point-to node of base class + // Only PagInstanceRefNode has this field + // Define in base class is for dot print + basePt; + clonedFrom; + constructor(id, cid = undefined, value, k, s) { + super(id, k); + this.cid = cid; + this.value = value; + this.stmt = s; + let ptaConfig = PointerAnalysisConfig.getInstance(); + this.pointTo = new ptaConfig.ptsCollectionCtor(); + } + getBasePt() { + return this.basePt; + } + setBasePt(pt) { + this.basePt = pt; + } + getCid() { + if (this.cid === undefined) { + throw new Error('cid is undefine'); + } + return this.cid; + } + setCid(cid) { + this.cid = cid; + } + setStmt(s) { + this.stmt = s; + } + getStmt() { + return this.stmt; + } + hasOutgoingCopyEdge() { + return this.copyOutEdges.size !== 0; + } + getOutgoingCopyEdges() { + return this.copyOutEdges; + } + getIncomingCopyEdges() { + return this.copyInEdges; + } + getOutgoingLoadEdges() { + return this.loadOutEdges; + } + getOutgoingWriteEdges() { + return this.writeOutEdges; + } + getIncomingWriteEdges() { + return this.writeInEdges; + } + getOutgoingThisEdges() { + return this.thisOutEdges; + } + getIncomingThisEdges() { + return this.thisInEdges; + } + addAddressInEdge(e) { + this.addressInEdges === undefined ? (this.addressInEdges = new Set()) : undefined; + this.addressInEdges.add(e); + this.addIncomingEdge(e); + } + addAddressOutEdge(e) { + this.addressOutEdges === undefined ? (this.addressOutEdges = new Set()) : undefined; + this.addressOutEdges.add(e); + this.addOutgoingEdge(e); + } + addCopyInEdge(e) { + this.copyInEdges === undefined ? (this.copyInEdges = new Set()) : undefined; + this.copyInEdges.add(e); + this.addIncomingEdge(e); + } + addCopyOutEdge(e) { + this.copyOutEdges === undefined ? (this.copyOutEdges = new Set()) : undefined; + this.copyOutEdges.add(e); + this.addOutgoingEdge(e); + } + addLoadInEdge(e) { + this.loadInEdges === undefined ? (this.loadInEdges = new Set()) : undefined; + this.loadInEdges.add(e); + this.addIncomingEdge(e); + } + addLoadOutEdge(e) { + this.loadOutEdges === undefined ? (this.loadOutEdges = new Set()) : undefined; + this.loadOutEdges.add(e); + this.addOutgoingEdge(e); + } + addWriteInEdge(e) { + this.writeInEdges = this.writeInEdges ?? new Set(); + this.writeInEdges.add(e); + this.addIncomingEdge(e); + } + addWriteOutEdge(e) { + this.writeOutEdges = this.writeOutEdges ?? new Set(); + this.writeOutEdges.add(e); + this.addOutgoingEdge(e); + } + addThisInEdge(e) { + this.thisInEdges = this.thisInEdges ?? new Set(); + this.thisInEdges.add(e); + this.addIncomingEdge(e); + } + addThisOutEdge(e) { + this.thisOutEdges = this.thisOutEdges ?? new Set(); + this.thisOutEdges.add(e); + this.addOutgoingEdge(e); + } + getValue() { + return this.value; + } + getPointTo() { + return this.pointTo; + } + addPointToElement(node) { + this.pointTo.insert(node); + } + setPointTo(pts) { + this.pointTo = pts; + } + getOutEdges() { + return { + AddressEdge: this.addressOutEdges, + CopyEdge: this.copyOutEdges, + LoadEdge: this.loadOutEdges, + WriteEdge: this.writeOutEdges, + }; + } + getClonedFrom() { + return this.clonedFrom; + } + setClonedFrom(id) { + this.clonedFrom = id; + } + getDotAttr() { + switch (this.getKind()) { + case exports.PagNodeKind.HeapObj: + case exports.PagNodeKind.Function: + case exports.PagNodeKind.GlobalThis: + return 'shape=box3d'; + case exports.PagNodeKind.LocalVar: + return 'shape=box'; + case exports.PagNodeKind.RefVar: + return 'shape=component'; + case exports.PagNodeKind.Param: + return 'shape=box'; + case exports.PagNodeKind.ExportInfo: + return 'shape=tab,color=purple'; + case exports.PagNodeKind.ThisRef: + return 'shape=box,color=orange'; + default: + return 'shape=box'; + } + } + getDotLabel() { + let label; + let param; + label = exports.PagNodeKind[this.getKind()]; + label = label + ` ID: ${this.getID()} Ctx: ${this.cid}`; + if (this.basePt) { + label = label + ` base:{${this.basePt}}`; + } + label = label + ` pts:{${Array.from(this.pointTo).join(',')}}`; + if (this.getKind() === exports.PagNodeKind.Param) { + param = this.value; + label = label + `\nParam#${param.getIndex()} ${param.toString()}`; + } + if (this.getKind() === exports.PagNodeKind.ThisRef) { + label = label + `\n${this.value.toString()}`; + } + if (this.getKind() === exports.PagNodeKind.Function) { + label = label + ` thisPt:{${this.getThisPt()}}`; + } + if (this.stmt) { + label = label + `\n${this.stmt.toString()}`; + let method = this.stmt.getCfg()?.getDeclaringMethod().getSubSignature().toString(); + if (method) { + label = label + '\n' + method; + } + label = label + ' ln: ' + this.stmt.getOriginPositionInfo().getLineNo(); + } + else if (this.value) { + label += `\n${this.value.toString()}`; + } + return label; + } +} +class PagLocalNode extends PagNode { + relatedDynamicCallSite; + relatedUnknownCallSite; + storageLinked = false; + storageType; + propertyName; + sdkParam = false; + constructor(id, cid = undefined, value, stmt) { + super(id, cid, value, exports.PagNodeKind.LocalVar, stmt); + } + addRelatedDynCallSite(cs) { + this.relatedDynamicCallSite = this.relatedDynamicCallSite ?? new Set(); + this.relatedDynamicCallSite.add(cs); + } + getRelatedDynCallSites() { + return this.relatedDynamicCallSite ?? new Set(); + } + addRelatedUnknownCallSite(cs) { + this.relatedUnknownCallSite = this.relatedUnknownCallSite ?? new Set(); + this.relatedUnknownCallSite.add(cs); + } + getRelatedUnknownCallSites() { + return this.relatedUnknownCallSite ?? new Set(); + } + setStorageLink(storageType, propertyName) { + this.storageLinked = true; + this.storageType = storageType; + this.propertyName = propertyName; + } + getStorage() { + return { + StorageType: this.storageType, + PropertyName: this.propertyName, + }; + } + isStorageLinked() { + return this.storageLinked; + } + setSdkParam() { + this.sdkParam = true; + } + isSdkParam() { + return this.sdkParam; + } +} +class PagInstanceFieldNode extends PagNode { + constructor(id, cid = undefined, instanceFieldRef, stmt) { + super(id, cid, instanceFieldRef, exports.PagNodeKind.RefVar, stmt); + } +} +class PagStaticFieldNode extends PagNode { + constructor(id, cid = undefined, staticFieldRef, stmt) { + super(id, cid, staticFieldRef, exports.PagNodeKind.RefVar, stmt); + } +} +class PagThisRefNode extends PagNode { + pointToNode; + constructor(id, thisRef) { + super(id, DUMMY_PAG_NODE_ID, thisRef, exports.PagNodeKind.ThisRef); + this.pointToNode = []; + } + getThisPTNode() { + return this.pointToNode; + } + addPTNode(ptNode) { + this.pointToNode.push(ptNode); + } +} +class PagArrayNode extends PagNode { + base; + constructor(id, cid = undefined, expr, stmt) { + super(id, cid, expr, exports.PagNodeKind.LocalVar, stmt); + this.base = expr.getBase(); + } +} +/** + * below is heapObj like Node + */ +class PagNewExprNode extends PagNode { + // store the cloned field node + fieldNodes; + constructor(id, cid = undefined, expr, stmt) { + super(id, cid, expr, exports.PagNodeKind.HeapObj, stmt); + } + addFieldNode(fieldSignature, nodeID) { + if (!this.fieldNodes) { + this.fieldNodes = new Map(); + } + if (this.fieldNodes.has(fieldSignature.getFieldSignature().toString())) { + return false; + } + this.fieldNodes.set(fieldSignature.getFieldSignature().toString(), nodeID); + return true; + } + getFieldNode(fieldSignature) { + if (!this.fieldNodes) { + return undefined; + } + return this.fieldNodes.get(fieldSignature.getFieldSignature().toString()); + } + getFieldNodes() { + if (!this.fieldNodes) { + return undefined; + } + return this.fieldNodes; + } +} +class PagNewContainerExprNode extends PagNode { + // store the cloned array ref node + elementNode; + constructor(id, cid = undefined, expr, stmt) { + super(id, cid, expr, exports.PagNodeKind.HeapObj, stmt); + } + addElementNode(nodeID) { + if (!this.elementNode) { + this.elementNode = nodeID; + } + return true; + } + getElementNode() { + if (this.elementNode) { + return this.elementNode; + } + return undefined; + } +} +class PagParamNode extends PagNode { + constructor(id, cid = undefined, r, stmt) { + super(id, cid, r, exports.PagNodeKind.Param, stmt); + } +} +class PagFuncNode extends PagNode { + methodSignature; + thisPt; + methodType; + // for Function.bind, store the original call message and caller cid + originCallSite; + argsOffset = 0; + originCid; + // TODO: may add obj interface + constructor(id, cid = undefined, r, stmt, method, thisInstanceID) { + super(id, cid, r, exports.PagNodeKind.Function, stmt); + if (method) { + this.methodSignature = method; + this.methodType = getBuiltInApiType(method); + } + if (thisInstanceID) { + this.thisPt = thisInstanceID; + } + } + setMethod(method) { + this.methodSignature = method; + this.methodType = getBuiltInApiType(method); + } + getMethod() { + return this.methodSignature; + } + setThisPt(thisPt) { + this.thisPt = thisPt; + } + getThisPt() { + return this.thisPt; + } + setCS(callsite) { + this.originCallSite = callsite; + } + getCS() { + return this.originCallSite; + } + setArgsOffset(offset) { + this.argsOffset = offset; + } + getArgsOffset() { + return this.argsOffset; + } + getMethodType() { + return this.methodType; + } + setOriginCid(cid) { + this.originCid = cid; + } + getOriginCid() { + return this.originCid; + } +} +/** + * almost same as PagNewExprNode, used only for globalThis and its field reference + */ +class PagGlobalThisNode extends PagNode { + fieldNodes; + constructor(id, cid = undefined, r, stmt) { + super(id, cid, r, exports.PagNodeKind.GlobalThis, stmt); + this.fieldNodes = new Map(); + } + addFieldNode(fieldSignature, nodeID) { + if (this.fieldNodes.has(fieldSignature.getFieldSignature().toString())) { + return false; + } + this.fieldNodes.set(fieldSignature.getFieldSignature().toString(), nodeID); + return true; + } + getFieldNode(fieldSignature) { + return this.fieldNodes.get(fieldSignature.getFieldSignature().toString()); + } + getFieldNodes() { + return this.fieldNodes; + } +} +class Pag extends BaseExplicitGraph { + cg; + contextValueToIdMap = new Map(); + ExportInfoToIdMap; + // contextBaseToIdMap will only be used in instance field + // Value: instance field base value, NodeID: abstract nodes + contextBaseToIdMap = new Map(); + // for reanalyze, will return new addr edges + stashAddrEdge = new Set(); + addrEdge = new Set(); + clonedNodeMap = new Map(); + getCG() { + return this.cg; + } + /* + * Clone a PagNode with same cid/value/stmt, + * but different Node ID + */ + getOrClonePagNode(src, basePt) { + if (src.getBasePt() !== undefined) { + throw new Error('This is a cloned ref node, can not be cloned again'); + } + let cloneSet = this.clonedNodeMap.get(src.getID()); + if (!cloneSet) { + cloneSet = new Map(); + this.clonedNodeMap.set(src.getID(), cloneSet); + } + else { + let nodeID = cloneSet.get(basePt); + if (nodeID) { + return this.getNode(nodeID); + } + } + // Not found + let cloneNode = this.addPagNode(src.getCid(), src.getValue(), src.getStmt(), false); + cloneNode.setClonedFrom(src.getID()); + cloneSet.set(basePt, cloneNode.getID()); + return cloneNode; + } + getOrClonePagFieldNode(src, basePt) { + let baseNode = this.getNode(basePt); + if (baseNode instanceof PagNewExprNode || baseNode instanceof PagGlobalThisNode) { + // check if real field node has been created with basePT, using FieldSignature as key + let existedNode = baseNode.getFieldNode(src.getValue()); + if (existedNode) { + return this.getNode(existedNode); + } + let fieldNode = this.getOrClonePagNode(src, basePt); + baseNode.addFieldNode(src.getValue(), fieldNode.getID()); + fieldNode.setBasePt(basePt); + return fieldNode; + } + else { + logger$b.error(`Error clone field node ${src.getValue()}`); + return undefined; + } + } + getOrClonePagContainerFieldNode(basePt, src, base) { + let baseNode = this.getNode(basePt); + if (baseNode instanceof PagNewContainerExprNode) { + // check if Array Ref real node has been created or not, if not: create a real Array Ref node + let existedNode = baseNode.getElementNode(); + let fieldNode; + if (existedNode) { + return this.getNode(existedNode); + } + if (src) { + fieldNode = this.getOrClonePagNode(src, basePt); + } + else if (base) { + const containerFieldSignature = new FieldSignature('field', new ClassSignature('container', new FileSignature('container', 'lib.es2015.collection.d.ts')), new UnclearReferenceType('')); + fieldNode = this.getOrClonePagNode( + // TODO: cid check + this.addPagNode(0, new ArkInstanceFieldRef(base, containerFieldSignature)), basePt); + } + baseNode.addElementNode(fieldNode.getID()); + fieldNode.setBasePt(basePt); + return fieldNode; + } + else if (baseNode instanceof PagNewExprNode) { + // In some cases, the value of a variable of array type may not be an explicit array object. + // For example, it could be a return value of a function (assuming that the call depth has + // exceeded the k-limit). + // In such situation, the `baseNode` will be a PagNewExprNode instead of a PagNewContainerExprNode, + // and a warning will be raised. + logger$b.warn(`[PTA]: Trying to clone an array from a PagNewExprNode instead of a PagNewContainerExprNode`); + } + else { + throw new Error(`Error clone array field node ${baseNode.getValue()}`); + } + return undefined; + } + getOrClonePagFuncNode(basePt) { + let baseNode = this.getNode(basePt); + if (baseNode instanceof PagFuncNode) { + let clonedFuncNode = this.getOrClonePagNode(baseNode, basePt); + return clonedFuncNode; + } + else { + logger$b.error(`Error clone func node ${baseNode.getValue()}`); + return undefined; + } + } + addPagNode(cid, value, stmt, refresh = true) { + let id = this.nodeNum + 1; + let pagNode; + if (value instanceof Local) { + pagNode = this.handleLocalNode(id, cid, value, stmt); + } + else if (value instanceof ArkInstanceFieldRef) { + pagNode = this.handleInstanceFieldNode(id, cid, value, stmt); + } + else if (value instanceof ArkStaticFieldRef) { + pagNode = this.handleStaticFieldNode(id, cid, value, stmt); + } + else if (value instanceof ArkArrayRef) { + pagNode = new PagArrayNode(id, cid, value, stmt); + } + else if (value instanceof ArkNewExpr) { + pagNode = this.handleNewExprNode(id, cid, value, stmt); + } + else if (value instanceof ArkNewArrayExpr) { + pagNode = new PagNewContainerExprNode(id, cid, value, stmt); + } + else if (value instanceof ArkParameterRef) { + pagNode = new PagParamNode(id, cid, value, stmt); + } + else if (value instanceof ArkThisRef) { + throw new Error('This Node needs to use addThisNode method'); + } + else { + throw new Error('unsupported Value type ' + value.getType().toString()); + } + this.addNode(pagNode); + this.addContextOrExportInfoMap(refresh, cid, id, value, pagNode, stmt); + return pagNode; + } + handleLocalNode(id, cid, value, stmt) { + const valueType = value.getType(); + if (valueType instanceof FunctionType && value.getDeclaringStmt() === null) { + return new PagFuncNode(id, cid, value, stmt, valueType.getMethodSignature()); + } + else if (value.getName() === GLOBAL_THIS_NAME && value.getDeclaringStmt() == null) { + return new PagGlobalThisNode(id, -1, value); + } + else { + return new PagLocalNode(id, cid, value, stmt); + } + } + handleInstanceFieldNode(id, cid, value, stmt) { + return this.createFieldNode(id, cid, value, stmt); + } + handleStaticFieldNode(id, cid, value, stmt) { + return this.createFieldNode(id, cid, value, stmt); + } + createFieldNode(id, cid, value, stmt) { + if (value.getType() instanceof FunctionType) { + return new PagFuncNode(id, cid, value, stmt, value.getType().getMethodSignature()); + } + else { + return value instanceof ArkStaticFieldRef ? new PagStaticFieldNode(id, cid, value, stmt) : new PagInstanceFieldNode(id, cid, value, stmt); + } + } + handleNewExprNode(id, cid, value, stmt) { + const classSignature = value.getClassType().getClassSignature(); + if (IsCollectionClass(classSignature)) { + return new PagNewContainerExprNode(id, cid, value, stmt); + } + else { + return new PagNewExprNode(id, cid, value, stmt); + } + } + addContextOrExportInfoMap(refresh, cid, id, value, pagNode, stmt) { + if (!(value instanceof ExportInfo)) { + this.addContextMap(refresh, cid, id, value, stmt, pagNode); + } + else { + this.addExportInfoMap(id, value); + } + } + addExportInfoMap(id, v) { + this.ExportInfoToIdMap = this.ExportInfoToIdMap ?? new Map(); + this.ExportInfoToIdMap.set(v, id); + } + addContextMap(refresh, cid, id, value, stmt, pagNode) { + if (!refresh) { + return; + } + let ctx2NdMap = this.contextValueToIdMap.get(value); + if (!ctx2NdMap) { + ctx2NdMap = new Map(); + this.contextValueToIdMap.set(value, ctx2NdMap); + } + ctx2NdMap.set(cid, id); + if (!(value instanceof ArkInstanceFieldRef || value instanceof ArkArrayRef)) { + return; + } + let base = value.getBase(); + //TODO: remove below once this Local is not uniq in %instInit is fix + if (base instanceof Local && base.getName() === 'this') { + stmt + ?.getCfg() + ?.getStmts() + .forEach(s => { + if (s instanceof ArkAssignStmt && s.getLeftOp() instanceof Local && s.getLeftOp().getName() === 'this') { + base = s.getLeftOp(); + return; + } + }); + } + let ctxMap = this.contextBaseToIdMap.get(base); + if (ctxMap === undefined) { + ctxMap = new Map(); + ctxMap.set(cid, [pagNode.getID()]); + } + else { + let nodes = ctxMap.get(cid); + if (nodes === undefined) { + nodes = [pagNode.getID()]; + } + else { + nodes.push(pagNode.getID()); + } + ctxMap.set(cid, nodes); + } + this.contextBaseToIdMap.set(base, ctxMap); + } + /* + * This node has no context info + * but point to node info + */ + addPagThisRefNode(value) { + let id = this.nodeNum + 1; + let pagNode = new PagThisRefNode(id, value); + this.addNode(pagNode); + return pagNode; + } + addPagThisLocalNode(ptNode, value) { + let id = this.nodeNum + 1; + let pagNode = new PagLocalNode(id, ptNode, value); + this.addNode(pagNode); + return pagNode; + } + getOrNewThisRefNode(thisRefNodeID, value) { + if (thisRefNodeID !== -1) { + return this.getNode(thisRefNodeID); + } + let thisRefNode = this.addPagThisRefNode(value); + return thisRefNode; + } + getOrNewThisLocalNode(cid, ptNode, value, s) { + if (ptNode !== -1) { + return this.getNode(ptNode); + } + else { + return this.getOrNewNode(cid, value, s); + } + } + hasExportNode(v) { + this.ExportInfoToIdMap = this.ExportInfoToIdMap ?? new Map(); + return this.ExportInfoToIdMap.get(v); + } + hasCtxNode(cid, v) { + let ctx2nd = this.contextValueToIdMap.get(v); + if (!ctx2nd) { + return undefined; + } + let ndId = ctx2nd.get(cid); + if (!ndId) { + return undefined; + } + return ndId; + } + hasCtxRetNode(cid, v) { + let ctx2nd = this.contextValueToIdMap.get(v); + if (!ctx2nd) { + return undefined; + } + let ndId = ctx2nd.get(cid); + if (!ndId) { + return undefined; + } + return ndId; + } + getOrNewNode(cid, v, s) { + let nodeId; + // Value + if (!(v instanceof ExportInfo)) { + nodeId = this.hasCtxNode(cid, v); + } + else { + // ExportInfo + nodeId = this.hasExportNode(v); + } + if (nodeId !== undefined) { + return this.getNode(nodeId); + } + return this.addPagNode(cid, v, s); + } + getNodesByValue(v) { + return this.contextValueToIdMap.get(v); + } + getNodesByBaseValue(v) { + return this.contextBaseToIdMap.get(v); + } + addPagEdge(src, dst, kind, stmt) { + // TODO: check if the edge already existing + let edge = new PagEdge(src, dst, kind, stmt); + if (this.ifEdgeExisting(edge)) { + return false; + } + switch (kind) { + case exports.PagEdgeKind.Copy: + case exports.PagEdgeKind.InterProceduralCopy: + src.addCopyOutEdge(edge); + dst.addCopyInEdge(edge); + if (src instanceof PagFuncNode || src instanceof PagGlobalThisNode || src instanceof PagNewExprNode || src instanceof PagNewContainerExprNode) { + this.addrEdge.add(edge); + this.stashAddrEdge.add(edge); + } + break; + case exports.PagEdgeKind.Address: + src.addAddressOutEdge(edge); + dst.addAddressInEdge(edge); + this.addrEdge.add(edge); + this.stashAddrEdge.add(edge); + break; + case exports.PagEdgeKind.Write: + src.addWriteOutEdge(edge); + dst.addWriteInEdge(edge); + break; + case exports.PagEdgeKind.Load: + src.addLoadOutEdge(edge); + dst.addLoadInEdge(edge); + break; + case exports.PagEdgeKind.This: + src.addThisOutEdge(edge); + dst.addThisInEdge(edge); + break; + } + return true; + } + getAddrEdges() { + return this.stashAddrEdge; + } + resetAddrEdges() { + this.stashAddrEdge.clear(); + } + getGraphName() { + return 'PAG'; + } + dump(name) { + let printer = new GraphPrinter(this); + PrinterBuilder.dump(printer, name); + } +} +class FuncPag { + internalEdges; + normalCallSites; + dynamicCallSites; + unknownCallSites; + getInternalEdges() { + return this.internalEdges; + } + addNormalCallSite(cs) { + this.normalCallSites = this.normalCallSites ?? new Set(); + this.normalCallSites.add(cs); + } + getNormalCallSites() { + this.normalCallSites = this.normalCallSites ?? new Set(); + return this.normalCallSites; + } + addDynamicCallSite(cs) { + this.dynamicCallSites = this.dynamicCallSites ?? new Set(); + this.dynamicCallSites.add(cs); + } + getDynamicCallSites() { + this.dynamicCallSites = this.dynamicCallSites ?? new Set(); + return this.dynamicCallSites; + } + addUnknownCallSite(cs) { + this.unknownCallSites = this.unknownCallSites ?? new Set(); + this.unknownCallSites.add(cs); + } + getUnknownCallSites() { + this.unknownCallSites = this.unknownCallSites ?? new Set(); + return this.unknownCallSites; + } + addInternalEdge(stmt, k) { + this.internalEdges === undefined ? (this.internalEdges = new Set()) : undefined; + let lhOp = stmt.getLeftOp(); + let rhOp = stmt.getRightOp(); + if (rhOp instanceof Constant) { + return false; + } + let iEdge = { + src: rhOp, + dst: lhOp, + kind: k, + stmt: stmt, + }; + this.internalEdges.add(iEdge); + return true; + } +} +class InterFuncPag { + interFuncEdges; + constructor() { + this.interFuncEdges = new Set(); + } + getInterProceduralEdges() { + return this.interFuncEdges; + } + addToInterProceduralEdgeSet(e) { + this.interFuncEdges.add(e); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$a = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'PTA'); +class CSFuncID { + cid; + funcID; + constructor(cid, fid) { + this.cid = cid; + this.funcID = fid; + } +} +class PagBuilder { + pag; + cg; + scale; + funcPags; + interFuncPags; + handledFunc = new Set(); + ctx; + scene; + worklist = []; + pagStat; + // TODO: change string to hash value + staticField2UniqInstanceMap = new Map(); + instanceField2UniqInstanceMap = new Map(); + cid2ThisRefPtMap = new Map(); + cid2ThisRefMap = new Map(); + cid2ThisLocalMap = new Map(); + sdkMethodReturnValueMap = new Map(); + // record the SDK API param, and create fake Values + methodParamValueMap = new Map(); + fakeSdkMethodParamDeclaringStmt = new ArkAssignStmt(new Local(''), new Local('')); + funcHandledThisRound = new Set(); + updatedNodesThisRound = new Map(); + singletonFuncMap = new Map(); + globalThisValue = new Local(GLOBAL_THIS_NAME); + globalThisPagNode; + storagePropertyMap = new Map(); + externalScopeVariableMap = new Map(); + retriggerNodesList = new Set(); + constructor(p, cg, s, kLimit, scale) { + this.pag = p; + this.cg = cg; + this.scale = scale; + this.funcPags = new Map(); + this.ctx = new KLimitedContextSensitive(kLimit); + this.scene = s; + this.pagStat = new PAGStat(); + } + buildFuncPagAndAddToWorklist(cs) { + if (this.worklist.includes(cs)) { + return cs; + } + this.buildFuncPag(cs.funcID); + if (this.isSingletonFunction(cs.funcID)) { + cs.cid = DUMMY_CID; + } + this.worklist.push(cs); + return cs; + } + addToFuncHandledListThisRound(id) { + if (this.funcHandledThisRound.has(id)) { + return; + } + this.funcHandledThisRound.add(id); + } + buildForEntries(funcIDs) { + this.worklist = []; + funcIDs.forEach(funcID => { + let cid = this.ctx.getNewContextID(funcID); + let csFuncID = new CSFuncID(cid, funcID); + this.buildFuncPagAndAddToWorklist(csFuncID); + }); + this.handleReachable(); + this.globalThisPagNode = this.getOrNewGlobalThisNode(-1); + this.pag.addPagEdge(this.globalThisPagNode, this.globalThisPagNode, exports.PagEdgeKind.Copy); + } + handleReachable() { + if (this.worklist.length === 0) { + return false; + } + this.funcHandledThisRound.clear(); + while (this.worklist.length > 0) { + let csFunc = this.worklist.shift(); + this.buildPagFromFuncPag(csFunc.funcID, csFunc.cid); + this.addToFuncHandledListThisRound(csFunc.funcID); + } + return true; + } + build() { + for (let funcID of this.cg.getEntries()) { + let cid = this.ctx.getNewContextID(funcID); + let csFuncID = new CSFuncID(cid, funcID); + this.buildFuncPagAndAddToWorklist(csFuncID); + this.handleReachable(); + } + } + buildFuncPag(funcID) { + if (this.funcPags.has(funcID)) { + return false; + } + let arkMethod = this.cg.getArkMethodByFuncID(funcID); + if (arkMethod == null) { + return false; + } + let cfg = arkMethod.getCfg(); + if (!cfg) { + this.buildSDKFuncPag(funcID); + return false; + } + logger$a.trace(`[build FuncPag] ${arkMethod.getSignature().toString()}`); + let fpag = new FuncPag(); + for (let stmt of cfg.getStmts()) { + if (stmt instanceof ArkAssignStmt) { + this.processExternalScopeValue(stmt.getRightOp(), funcID); + // Add non-call edges + let kind = this.getEdgeKindForAssignStmt(stmt); + if (kind !== exports.PagEdgeKind.Unknown) { + fpag.addInternalEdge(stmt, kind); + continue; + } + // handle call + this.buildInvokeExprInStmt(stmt, fpag); + } + else if (stmt instanceof ArkInvokeStmt && this.scale === PtaAnalysisScale.WholeProgram) { + this.processExternalScopeValue(stmt.getInvokeExpr(), funcID); + this.buildInvokeExprInStmt(stmt, fpag); + } + else ; + } + this.funcPags.set(funcID, fpag); + this.pagStat.numTotalFunction++; + return true; + } + buildInvokeExprInStmt(stmt, fpag) { + // TODO: discuss if we need a invokeStmt + let callSites = this.cg.getCallSiteByStmt(stmt); + if (callSites.length !== 0) { + // direct call or constructor call is already existing in CG + // TODO: some ptr invoke stmt is recognized as Static invoke in tests/resources/callgraph/funPtrTest1/fnPtrTest4.ts + // TODO: instance invoke(ptr invoke) + callSites.forEach(cs => { + if (this.cg.isUnknownMethod(cs.calleeFuncID)) { + fpag.addUnknownCallSite(cs); + } + else { + fpag.addNormalCallSite(cs); + } + }); + return; + } + let dycs = this.cg.getDynCallsiteByStmt(stmt); + if (dycs) { + this.addToDynamicCallSite(fpag, dycs); + } + else { + logger$a.error(`can not find callsite by stmt: ${stmt.toString()}`); + } + } + processExternalScopeValue(value, funcID) { + let dummyMainFuncID = this.cg.getDummyMainFuncID(); + if (dummyMainFuncID && funcID === dummyMainFuncID) { + return; + } + if (value instanceof Local) { + this.handleValueFromExternalScope(value, funcID); + } + else if (value instanceof ArkInstanceInvokeExpr) { + value.getUses().forEach(v => { + this.handleValueFromExternalScope(v, funcID); + }); + } + } + /** + * will not create real funcPag, only create param values + */ + buildSDKFuncPag(funcID) { + // check if SDK method + let cgNode = this.cg.getNode(funcID); + if (!cgNode.isSdkMethod()) { + return; + } + let paramArr = this.createDummyParamValue(funcID); + this.methodParamValueMap.set(funcID, paramArr); + } + createDummyParamValue(funcID, type = 1) { + let arkMethod = this.cg.getArkMethodByFuncID(funcID); + if (!arkMethod) { + return new Map(); + } + let args = arkMethod.getParameters(); + if (!args) { + return new Map(); + } + let paramArr = new Map(); + if (type === 0) { + // heapObj + args.forEach((arg, index) => { + let paramType = arg.getType(); + if (!(paramType instanceof ClassType)) { + return; + // TODO: support more type + } + let argInstance = new ArkNewExpr(paramType); + paramArr.set(index, argInstance); + }); + } + else if (type === 1) { + // Local + args.forEach((arg, index) => { + let argInstance = new Local(arg.getName(), arg.getType()); + argInstance.setDeclaringStmt(this.fakeSdkMethodParamDeclaringStmt); + paramArr.set(index, argInstance); + }); + } + return paramArr; + } + createDummyParamPagNodes(value, funcID) { + let paramPagNodes = new Map(); + let method = this.cg.getArkMethodByFuncID(funcID); + if (!method || !method.getCfg()) { + return paramPagNodes; + } + value.forEach((v, index) => { + let paramArkExprNode = this.pag.getOrNewNode(DUMMY_CID, v, this.fakeSdkMethodParamDeclaringStmt); + paramPagNodes.set(index, paramArkExprNode.getID()); + }); + return paramPagNodes; + } + buildPagFromFuncPag(funcID, cid) { + let funcPag = this.funcPags.get(funcID); + if (funcPag === undefined) { + return; + } + if (this.handledFunc.has(`${cid}-${funcID}`)) { + return; + } + this.addEdgesFromFuncPag(funcPag, cid, funcID); + let interFuncPag = this.interFuncPags?.get(funcID); + if (interFuncPag) { + this.addEdgesFromInterFuncPag(interFuncPag, cid); + } + this.addCallsEdgesFromFuncPag(funcPag, cid); + this.addDynamicCallSite(funcPag, funcID, cid); + this.addUnknownCallSite(funcPag, funcID); + this.handledFunc.add(`${cid}-${funcID}`); + } + /// Add Pag Nodes and Edges in function + addEdgesFromFuncPag(funcPag, cid, funcID) { + let inEdges = funcPag.getInternalEdges(); + if (inEdges === undefined) { + return false; + } + let paramNodes; + let paramRefIndex = 0; + if (this.scale === PtaAnalysisScale.MethodLevel) { + paramNodes = this.createDummyParamPagNodes(this.createDummyParamValue(funcID, 0), funcID); + } + for (let e of inEdges) { + let srcPagNode = this.getOrNewPagNode(cid, e.src, e.stmt); + let dstPagNode = this.getOrNewPagNode(cid, e.dst, e.stmt); + this.pag.addPagEdge(srcPagNode, dstPagNode, e.kind, e.stmt); + // Take place of the real stmt for return + if (dstPagNode.getStmt() instanceof ArkReturnStmt) { + dstPagNode.setStmt(e.stmt); + } + // for demand-driven analysis, add fake parameter heapObj nodes + if (e.src instanceof ArkParameterRef && this.scale === PtaAnalysisScale.MethodLevel) { + let paramObjNodeID = paramNodes?.get(paramRefIndex++); + if (!paramObjNodeID) { + continue; + } + this.pag.addPagEdge(this.pag.getNode(paramObjNodeID), srcPagNode, exports.PagEdgeKind.Address); + } + } + return true; + } + /// add Copy edges interprocedural + addCallsEdgesFromFuncPag(funcPag, cid) { + for (let cs of funcPag.getNormalCallSites()) { + let ivkExpr = cs.callStmt.getInvokeExpr(); + let calleeCid = this.ctx.getOrNewContext(cid, cs.calleeFuncID, true); + let calleeCGNode = this.cg.getNode(cs.calleeFuncID); + if (this.scale === PtaAnalysisScale.MethodLevel) { + this.addStaticPagCallReturnEdge(cs, cid, calleeCid); + } + // process the Storage API(Static) + if (!this.processStorage(cs, calleeCGNode, cid)) { + // If not Storage API, process normal edge + this.addStaticPagCallEdge(cs, cid, calleeCid); + } + // Add edge to thisRef for special calls + if (calleeCGNode.getKind() === exports.CallGraphNodeKind.constructor || calleeCGNode.getKind() === exports.CallGraphNodeKind.intrinsic) { + let callee = this.scene.getMethod(this.cg.getMethodByFuncID(cs.calleeFuncID)); + if (ivkExpr instanceof ArkInstanceInvokeExpr) { + let baseNode = this.getOrNewPagNode(cid, ivkExpr.getBase()); + let baseNodeID = baseNode.getID(); + this.addThisRefCallEdge(baseNodeID, cid, ivkExpr.getBase(), callee, calleeCid, cs.callerFuncID); + } + else { + logger$a.error(`constructor or intrinsic func is static ${ivkExpr.toString()}`); + } + } + } + return true; + } + addDynamicCallSite(funcPag, funcID, cid) { + // add dyn callsite in funcpag to base node + for (let cs of funcPag.getDynamicCallSites()) { + let invokeExpr = cs.callStmt.getInvokeExpr(); + let base; + if (invokeExpr instanceof ArkInstanceInvokeExpr) { + base = invokeExpr.getBase(); + } + else if (invokeExpr instanceof ArkPtrInvokeExpr && invokeExpr.getFuncPtrLocal() instanceof Local) { + base = invokeExpr.getFuncPtrLocal(); + } + else if (invokeExpr instanceof ArkPtrInvokeExpr && invokeExpr.getFuncPtrLocal() instanceof AbstractFieldRef) { + /** + * TODO: wait for IR change + * throw error in ptrInvoke with field ref + * this.field() // field is lambda expression + */ + continue; + } + // TODO: check base under different cid + let baseNodeIDs = this.pag.getNodesByValue(base); + if (!baseNodeIDs) { + // bind the call site to export base + let interProceduralLocal = this.getSourceValueFromExternalScope(base, funcID); + if (interProceduralLocal) { + baseNodeIDs = this.pag.getNodesByValue(interProceduralLocal); + } + } + if (!baseNodeIDs) { + logger$a.warn(`[build dynamic call site] can not handle call site with base ${base.toString()}`); + continue; + } + for (let nodeID of baseNodeIDs.values()) { + let node = this.pag.getNode(nodeID); + if (!(node instanceof PagLocalNode)) { + continue; + } + node.addRelatedDynCallSite(cs); + } + if (cs.callStmt instanceof ArkAssignStmt) { + this.getOrNewPagNode(cid, cs.callStmt.getLeftOp(), cs.callStmt); + } + } + } + addUnknownCallSite(funcPag, funcID) { + let method = this.cg.getArkMethodByFuncID(funcID); + if (!method) { + throw new Error(`can not find ArkMethod by FuncID ${funcID}`); + } + let locals = method.getBody()?.getLocals(); + funcPag.getUnknownCallSites().forEach(unknownCallSite => { + let calleeName = unknownCallSite.callStmt.getInvokeExpr()?.getMethodSignature().getMethodSubSignature().getMethodName(); + let base = locals.get(calleeName); + if (!base) { + return; + } + let baseNodeIDs = this.pag.getNodesByValue(base); + if (!baseNodeIDs) { + logger$a.warn(`[build dynamic call site] can not handle call site with base ${base.toString()}`); + return; + } + for (let nodeID of baseNodeIDs.values()) { + let node = this.pag.getNode(nodeID); + if (!(node instanceof PagLocalNode)) { + continue; + } + node.addRelatedUnknownCallSite(unknownCallSite); + } + }); + } + addDynamicCallEdge(cs, baseClassPTNode, cid) { + let srcNodes = []; + let ivkExpr = cs.callStmt.getInvokeExpr(); + let ptNode = this.pag.getNode(baseClassPTNode); + let value = ptNode.getValue(); + let callees = this.getDynamicCallee(ptNode, value, ivkExpr, cs); + for (let callee of callees) { + if (!callee) { + continue; + } + // get caller and callee CG node, add param and return value PAG edge + let dstCGNode = this.cg.getCallGraphNodeByMethod(callee.getSignature()); + let callerNode = this.cg.getNode(cs.callerFuncID); + if (!callerNode) { + throw new Error('Can not get caller method node'); + } + // update call graph + // TODO: movo to cgbuilder + this.cg.addDynamicCallEdge(callerNode.getID(), dstCGNode.getID(), cs.callStmt); + if (this.cg.detectReachable(dstCGNode.getID(), callerNode.getID())) { + return srcNodes; + } + let calleeCid = this.ctx.getOrNewContext(cid, dstCGNode.getID(), true); + let staticCS = new CallSite(cs.callStmt, cs.args, dstCGNode.getID(), cs.callerFuncID); + if (this.scale === PtaAnalysisScale.MethodLevel) { + srcNodes.push(...this.addStaticPagCallReturnEdge(staticCS, baseClassPTNode, calleeCid)); + continue; + } + if (getBuiltInApiType(ivkExpr?.getMethodSignature()) === BuiltApiType.NotBuiltIn) { + srcNodes.push(...this.processNormalMethodPagCallEdge(staticCS, cid, calleeCid, baseClassPTNode)); + } + else { + // special SDK call: Container API, Function API + srcNodes.push(...this.processBuiltInMethodPagCallEdge(staticCS, cid, calleeCid, baseClassPTNode)); + } + } + return srcNodes; + } + /** + * all possible callee methods of a dynamic call site + * handle both PtrInvokeExpr and InstanceInvokeExpr + */ + getDynamicCallee(ptNode, value, ivkExpr, cs) { + let callee = []; + if (ptNode instanceof PagFuncNode) { + // function ptr invoke + let tempCallee = this.scene.getMethod(ptNode.getMethod()); + if (!callee) { + return callee; + } + callee.push(tempCallee); + return callee; + } + //else branch + let calleeName = ivkExpr.getMethodSignature().getMethodSubSignature().getMethodName(); + // instance method invoke + if (!(value instanceof ArkNewExpr || value instanceof ArkNewArrayExpr)) { + return callee; + } + let tempCallee; + // try to get callee by MethodSignature + if (value instanceof ArkNewExpr) { + // get class signature + let clsSig = value.getType().getClassSignature(); + let cls; + cls = this.scene.getClass(clsSig); + while (!tempCallee && cls) { + tempCallee = cls.getMethodWithName(calleeName); + cls = cls.getSuperClass(); + } + if (!tempCallee) { + tempCallee = this.scene.getMethod(ivkExpr.getMethodSignature()); + } + } + if (!tempCallee && cs.args) { + // while pts has {o_1, o_2} and invoke expr represents a method that only {o_1} has + // return empty node when {o_2} come in + // try to get callee by anonymous method in param + for (let arg of cs.args) { + // TODO: anonymous method param and return value pointer pass + let argType = arg.getType(); + if (argType instanceof FunctionType) { + callee.push(this.scene.getMethod(argType.getMethodSignature())); + } + } + } + else if (tempCallee) { + callee.push(tempCallee); + } + return callee; + } + processNormalMethodPagCallEdge(staticCS, cid, calleeCid, baseClassPTNode) { + let srcNodes = []; + let ivkExpr = staticCS.callStmt.getInvokeExpr(); + let ptNode = this.pag.getNode(baseClassPTNode); + let dstCGNode = this.cg.getNode(staticCS.calleeFuncID); + let callee = this.cg.getArkMethodByFuncID(staticCS.calleeFuncID); + // Dynamic call, Ptr call, normal SDK call + srcNodes.push(...this.addStaticPagCallEdge(staticCS, cid, calleeCid, ptNode)); + // Pass base's pts to callee's this pointer + if (!dstCGNode.isSdkMethod() && ivkExpr instanceof ArkInstanceInvokeExpr) { + let srcBaseNode = this.addThisRefCallEdge(baseClassPTNode, cid, ivkExpr.getBase(), callee, calleeCid, staticCS.callerFuncID); + if (srcBaseNode !== -1) { + srcNodes.push(srcBaseNode); + } + } + else if (!dstCGNode.isSdkMethod() && ivkExpr instanceof ArkPtrInvokeExpr) { + let originCS = ptNode.getCS(); + if (!originCS) { + return srcNodes; + } + let thisValue = originCS.args[0]; + if (!(thisValue instanceof Local)) { + return srcNodes; + } + this.addThisRefCallEdge(baseClassPTNode, ptNode.getOriginCid(), thisValue, callee, calleeCid, staticCS.callerFuncID); + } + return srcNodes; + } + /** + * include container API, Function API + */ + processBuiltInMethodPagCallEdge(staticCS, cid, calleeCid, baseClassPTNode) { + let srcNodes = []; + let ivkExpr = staticCS.callStmt.getInvokeExpr(); + let callee = this.scene.getMethod(ivkExpr.getMethodSignature()); + let realCallee = this.cg.getArkMethodByFuncID(staticCS.calleeFuncID); + if (!callee) { + return srcNodes; + } + let builtInType = getBuiltInApiType(callee.getSignature()); + if (builtInType === BuiltApiType.NotBuiltIn || !realCallee) { + return srcNodes; + } + switch (builtInType) { + case BuiltApiType.SetAdd: + case BuiltApiType.MapSet: + this.processContainerPagCallEdge(staticCS, cid, baseClassPTNode, builtInType); + break; + case BuiltApiType.FunctionCall: + /** + * set this and param + * function.call(thisArg, arg1, arg2, ...) + */ + this.handleFunctionCall(staticCS, cid, calleeCid, realCallee, srcNodes, baseClassPTNode); + break; + case BuiltApiType.FunctionApply: + /** + * set this, resolve array param + * function.apply(thisArg, [argsArray]) + */ + this.handleFunctionApply(staticCS, cid, calleeCid, realCallee, srcNodes, baseClassPTNode); + break; + case BuiltApiType.FunctionBind: + /** + * clone the function node and add the this pointer, origin callsite, args offset to it + * let f = function.bind(thisArg, arg1, arg2, ...) + * f(); + */ + this.handleFunctionBind(staticCS, cid, baseClassPTNode, srcNodes); + break; + } + return srcNodes; + } + processContainerPagCallEdge(cs, cid, baseClassPTNode, type) { + let srcNodes = []; + let calleeNode = this.cg.getNode(cs.calleeFuncID); + let calleeMethod = this.scene.getMethod(calleeNode.getMethod()); + let ptNode = this.pag.getNode(baseClassPTNode); + if (!calleeMethod || !(ptNode instanceof PagNewContainerExprNode)) { + return srcNodes; + } + let containerValue = cs.callStmt.getInvokeExpr().getBase(); + const containerValueProcess = (argIndex) => { + let srcNode = this.pag.getOrNewNode(cid, cs.args[argIndex], cs.callStmt); + let realContainerFieldPagNode = this.pag.getOrClonePagContainerFieldNode(baseClassPTNode, undefined, containerValue); + if (realContainerFieldPagNode) { + // In some cases, the value of a variable of array type may not be an explicit array object, + // and the value of `realContainerFieldPagNode` will be undefined. + this.pag.addPagEdge(srcNode, realContainerFieldPagNode, exports.PagEdgeKind.Copy, cs.callStmt); + srcNodes.push(srcNode.getID()); + } + }; + if (type === BuiltApiType.SetAdd) { + containerValueProcess(0); + } + else if (type === BuiltApiType.MapSet) { + containerValueProcess(1); + } + return srcNodes; + } + handleFunctionCall(staticCS, cid, calleeCid, realCallee, srcNodes, baseClassPTNode) { + this.buildFuncPagAndAddToWorklist(new CSFuncID(calleeCid, staticCS.calleeFuncID)); + srcNodes.push(...this.addCallParamPagEdge(realCallee, staticCS.args, staticCS.callStmt, cid, calleeCid, 1)); + this.addThisEdge(staticCS, cid, realCallee, srcNodes, baseClassPTNode, calleeCid); + } + handleFunctionApply(staticCS, cid, calleeCid, realCallee, srcNodes, baseClassPTNode) { + this.buildFuncPagAndAddToWorklist(new CSFuncID(calleeCid, staticCS.calleeFuncID)); + let callerMethod = this.cg.getArkMethodByFuncID(staticCS.callerFuncID); + if (!callerMethod) { + throw new Error('Cannot get caller method'); + } + let argsRealValues = this.transferArrayValues(callerMethod, staticCS.args[1]); + srcNodes.push(...this.addCallParamPagEdge(realCallee, argsRealValues, staticCS.callStmt, cid, calleeCid, 0)); + this.addThisEdge(staticCS, cid, realCallee, srcNodes, baseClassPTNode, calleeCid); + } + handleFunctionBind(staticCS, cid, baseClassPTNode, srcNodes) { + let srcNode = this.pag.getOrClonePagFuncNode(baseClassPTNode); + if (!srcNode) { + return; + } + this.setFunctionThisPt(staticCS, srcNode, cid); + let dstNode = this.getOrNewPagNode(cid, staticCS.callStmt.getLeftOp()); + this.pag.addPagEdge(srcNode, dstNode, exports.PagEdgeKind.Copy, staticCS.callStmt); + srcNodes.push(srcNode.getID()); + srcNode.setCS(staticCS); + srcNode.setArgsOffset(1); + srcNode.setOriginCid(cid); + } + addThisEdge(staticCS, cid, realCallee, srcNodes, baseClassPTNode, calleeCid) { + if (!(staticCS.args[0] instanceof NullConstant) && !realCallee.isStatic()) { + let srcNodeID = this.addThisRefCallEdge(baseClassPTNode, cid, staticCS.args[0], realCallee, calleeCid, staticCS.callerFuncID); + if (srcNodeID !== -1) { + srcNodes.push(srcNodeID); + } + } + } + setFunctionThisPt(staticCS, srcNode, cid) { + let thisLocal = staticCS.args[0]; + if (!(thisLocal instanceof Local)) { + return; + } + let thisInstanceLocal = this.getRealThisLocal(thisLocal, staticCS.callerFuncID); + let baseThisNode = this.pag.getOrNewNode(cid, thisInstanceLocal); + for (let pt of baseThisNode.getPointTo()) { + srcNode.setThisPt(pt); + } + } + handleUnkownDynamicCall(cs, cid) { + let srcNodes = []; + let callerNode = this.cg.getNode(cs.callerFuncID); + let ivkExpr = cs.callStmt.getInvokeExpr(); + logger$a.warn('Handling unknown dyn call site : \n ' + callerNode.getMethod().toString() + '\n --> ' + ivkExpr.toString() + '\n CID: ' + cid); + let callees = []; + let callee = null; + callee = this.scene.getMethod(ivkExpr.getMethodSignature()); + if (!callee) { + cs.args?.forEach(arg => { + if (!(arg.getType() instanceof FunctionType)) { + return; + } + callee = this.scene.getMethod(arg.getType().getMethodSignature()); + if (callee) { + callees.push(callee); + } + }); + } + else { + callees.push(callee); + } + if (callees.length === 0) { + return srcNodes; + } + callees.forEach(callee => { + let dstCGNode = this.cg.getCallGraphNodeByMethod(callee.getSignature()); + if (!callerNode) { + throw new Error('Can not get caller method node'); + } + if (this.processStorage(cs, dstCGNode, cid)) { + if (ivkExpr.getArgs().length !== 0) { + // for AppStorage.set() instance invoke, add obj to reanalyze list + let argsNode = this.pag.getOrNewNode(cid, cs.args[0]); + srcNodes.push(argsNode.getID()); + } + } + logger$a.warn(`\tAdd call edge of unknown call ${callee.getSignature().toString()}`); + this.cg.addDynamicCallEdge(callerNode.getID(), dstCGNode.getID(), cs.callStmt); + if (!this.cg.detectReachable(dstCGNode.getID(), callerNode.getID())) { + let calleeCid = this.ctx.getOrNewContext(cid, dstCGNode.getID(), true); + let staticCS = new CallSite(cs.callStmt, cs.args, dstCGNode.getID(), cs.callerFuncID); + let staticSrcNodes = this.addStaticPagCallEdge(staticCS, cid, calleeCid); + srcNodes.push(...staticSrcNodes); + } + }); + return srcNodes; + } + handleUnprocessedCallSites(processedCallSites) { + let reAnalyzeNodes = []; + for (let funcID of this.funcHandledThisRound) { + let funcPag = this.funcPags.get(funcID); + if (!funcPag) { + logger$a.error(`can not find funcPag of handled func ${funcID}`); + continue; + } + let callSites = funcPag.getDynamicCallSites(); + const diffCallSites = new Set(Array.from(callSites).filter(item => !processedCallSites.has(item))); + diffCallSites.forEach(cs => { + let ivkExpr = cs.callStmt.getInvokeExpr(); + if (!(ivkExpr instanceof ArkInstanceInvokeExpr)) { + return; + } + // Get local of base class + let base = ivkExpr.getBase(); + // TODO: remove this after multiple this local fixed + base = this.getRealThisLocal(base, cs.callerFuncID); + // Get PAG nodes for this base's local + let ctx2NdMap = this.pag.getNodesByValue(base); + if (!ctx2NdMap) { + return; + } + for (let [cid] of ctx2NdMap.entries()) { + reAnalyzeNodes.push(...this.handleUnkownDynamicCall(cs, cid)); + } + }); + } + return reAnalyzeNodes; + } + addThisRefCallEdge(baseClassPTNode, cid, baseLocal, callee, calleeCid, callerFunID) { + let thisRefNodeID = this.recordThisRefNode(baseClassPTNode, callee, calleeCid); + if (thisRefNodeID === -1) { + return -1; + } + let thisRefNode = this.pag.getNode(thisRefNodeID); + let srcBaseLocal = baseLocal; + srcBaseLocal = this.getRealThisLocal(srcBaseLocal, callerFunID); + let srcNodeId = this.pag.hasCtxNode(cid, srcBaseLocal); + if (!srcNodeId) { + // this check is for export local and closure use + // replace the invoke base, because its origin base has no pag node + let interProceduralLocal = this.getSourceValueFromExternalScope(srcBaseLocal, callerFunID); + if (interProceduralLocal) { + srcNodeId = this.pag.hasCtxNode(cid, interProceduralLocal); + } + } + if (!srcNodeId) { + throw new Error('Can not get base node'); + } + this.pag.addPagEdge(this.pag.getNode(srcNodeId), thisRefNode, exports.PagEdgeKind.This); + return srcNodeId; + } + recordThisRefNode(baseClassPTNode, callee, calleeCid) { + if (!callee || !callee.getCfg()) { + logger$a.error(`callee is null`); + return -1; + } + let thisAssignStmt = callee + .getCfg() + ?.getStmts() + .filter(s => s instanceof ArkAssignStmt && s.getRightOp() instanceof ArkThisRef); + let thisPtr = (thisAssignStmt?.at(0)).getRightOp(); + if (!thisPtr) { + throw new Error('Can not get this ptr'); + } + // IMPORTANT: set cid 2 base Pt info firstly + this.cid2ThisRefPtMap.set(calleeCid, baseClassPTNode); + let thisRefNode = this.getOrNewThisRefNode(calleeCid, thisPtr); + thisRefNode.addPTNode(baseClassPTNode); + return thisRefNode.getID(); + } + /* + * Add copy edges from arguments to parameters + * ret edges from return values to callsite + * Return src node + */ + addStaticPagCallEdge(cs, callerCid, calleeCid, ptNode) { + if (!calleeCid) { + calleeCid = this.ctx.getOrNewContext(callerCid, cs.calleeFuncID, true); + } + let srcNodes = []; + // Add reachable + let calleeNode = this.cg.getNode(cs.calleeFuncID); + let calleeMethod = this.scene.getMethod(calleeNode.getMethod()); + if (!calleeMethod) { + // TODO: check if nodes need to delete + return srcNodes; + } + if (calleeNode.isSdkMethod()) { + srcNodes.push(...this.addSDKMethodPagCallEdge(cs, callerCid, calleeCid)); + return srcNodes; + } + if (!calleeMethod.getCfg()) { + // method have no cfg body + return srcNodes; + } + let calleeCS = this.buildFuncPagAndAddToWorklist(new CSFuncID(calleeCid, cs.calleeFuncID)); + // callee cid will updated if callee is singleton + calleeCid = calleeCS.cid; + let realArgs = cs.args ?? []; + let argsOffset = 0; + if (ptNode && ptNode instanceof PagFuncNode && ptNode.getCS()) { + // for ptr invoke cloned by Function.bind() + realArgs = ptNode.getCS().args ?? []; + argsOffset = ptNode.getArgsOffset() ?? 0; + callerCid = ptNode.getOriginCid() ?? callerCid; + } + srcNodes.push(...this.addCallParamPagEdge(calleeMethod, realArgs, cs.callStmt, callerCid, calleeCid, argsOffset)); + srcNodes.push(...this.addCallReturnPagEdge(calleeMethod, cs.callStmt, callerCid, calleeCid)); + return srcNodes; + } + /** + * only process the param PAG edge for invoke stmt + */ + addCallParamPagEdge(calleeMethod, args, callStmt, callerCid, calleeCid, offset) { + let params = calleeMethod + .getCfg() + .getStmts() + .filter(stmt => stmt instanceof ArkAssignStmt && stmt.getRightOp() instanceof ArkParameterRef) + .map(stmt => stmt.getRightOp()); + let srcNodes = []; + /** + * process foreach situation + * e.g. arr.forEach((item) => { ... }) + * cs.args is anonymous method local, will have only 1 parameter + * but inside foreach will have >= 1 parameters + */ + if (callStmt.getInvokeExpr()?.getMethodSignature().getMethodSubSignature().getMethodName() === 'forEach') { + srcNodes.push(...this.addForeachParamPagEdge(callerCid, calleeCid, callStmt, params)); + return srcNodes; + } + // add args to parameters edges + for (let i = offset; i <= args.length; i++) { + let arg = args.at(i); + let param = params.at(i - offset); + if (!arg || !param) { + return srcNodes; + } + if (arg instanceof Constant || arg instanceof AbstractExpr) { + // TODO: handle AbstractExpr + continue; + } + // Get or create new PAG node for argument and parameter + let srcPagNode = this.getOrNewPagNode(callerCid, arg, callStmt); + let dstPagNode = this.getOrNewPagNode(calleeCid, param, callStmt); + this.pag.addPagEdge(srcPagNode, dstPagNode, exports.PagEdgeKind.Copy, callStmt); + srcNodes.push(srcPagNode.getID()); + // TODO: handle other types of parmeters + } + return srcNodes; + } + /** + * temporary solution for foreach + * deprecate when foreach is handled by built-in method + * connect the element node with the value inside foreach + */ + addForeachParamPagEdge(callerCid, calleeCid, callStmt, params) { + // container value is the base value of callstmt, its points-to is PagNewContainerExprNode + let srcNodes = []; + let containerValue = callStmt.getInvokeExpr().getBase(); + let param = params.at(0); + if (!containerValue || !param) { + return srcNodes; + } + let basePagNode = this.getOrNewPagNode(callerCid, containerValue, callStmt); + let dstPagNode = this.getOrNewPagNode(calleeCid, param, callStmt); + for (let pt of basePagNode.getPointTo()) { + let newContainerExprPagNode = this.pag.getNode(pt); + // PagNewContainerExprNode's points-to is the element node + if (!newContainerExprPagNode || !newContainerExprPagNode.getElementNode()) { + continue; + } + let srcPagNode = this.pag.getNode(newContainerExprPagNode.getElementNode()); + // connect the element node with the value inside foreach + this.pag.addPagEdge(srcPagNode, dstPagNode, exports.PagEdgeKind.Copy, callStmt); + srcNodes.push(srcPagNode.getID()); + } + return srcNodes; + } + /** + * process the return value PAG edge for invoke stmt + */ + addCallReturnPagEdge(calleeMethod, callStmt, callerCid, calleeCid) { + let srcNodes = []; + // add ret to caller edges + let retStmts = calleeMethod.getReturnStmt(); + // TODO: call statement must be a assignment state + if (callStmt instanceof ArkAssignStmt) { + let retDst = callStmt.getLeftOp(); + for (let retStmt of retStmts) { + let retValue = retStmt.getOp(); + if (retValue instanceof Local) { + let srcPagNode = this.getOrNewPagNode(calleeCid, retValue, retStmt); + let dstPagNode = this.getOrNewPagNode(callerCid, retDst, callStmt); + this.pag.addPagEdge(srcPagNode, dstPagNode, exports.PagEdgeKind.Copy, retStmt); + } + else if (retValue instanceof Constant) { + continue; + } + else if (retValue instanceof AbstractExpr) { + logger$a.debug(retValue); + continue; + } + else { + throw new Error('return dst not a local or constant, but: ' + retValue.getType().toString()); + } + } + } + return srcNodes; + } + addStaticPagCallReturnEdge(cs, callerCid, calleeCid) { + if (!calleeCid) { + calleeCid = this.ctx.getOrNewContext(callerCid, cs.calleeFuncID, true); + } + let srcNodes = []; + // Add reachable + let calleeNode = this.cg.getNode(cs.calleeFuncID); + let calleeMethod = this.scene.getMethod(calleeNode.getMethod()); + if (!calleeMethod) { + // TODO: check if nodes need to delete + return srcNodes; + } + srcNodes.push(...this.addSDKMethodReturnPagEdge(cs, callerCid, calleeCid, calleeMethod)); + return srcNodes; + } + addSDKMethodPagCallEdge(cs, callerCid, calleeCid) { + let srcNodes = []; + let calleeNode = this.cg.getNode(cs.calleeFuncID); + let calleeMethod = this.scene.getMethod(calleeNode.getMethod()); + if (!calleeMethod) { + return srcNodes; + } + let methodType = getBuiltInApiType(calleeMethod.getSignature()); + // block the container SDK + if (methodType === BuiltApiType.SetAdd || BuiltApiType.MapSet) { + return srcNodes; + } + if (!this.methodParamValueMap.has(calleeNode.getID())) { + this.buildSDKFuncPag(calleeNode.getID()); + } + srcNodes.push(...this.addSDKMethodReturnPagEdge(cs, callerCid, calleeCid, calleeMethod)); + srcNodes.push(...this.addSDKMethodParamPagEdge(cs, callerCid, calleeCid, calleeNode.getID())); + return srcNodes; + } + addSDKMethodReturnPagEdge(cs, callerCid, calleeCid, calleeMethod) { + let srcNodes = []; + let returnType = calleeMethod.getReturnType(); + if (!(returnType instanceof ClassType) || !(cs.callStmt instanceof ArkAssignStmt)) { + return srcNodes; + } + // check fake heap object exists or not + let cidMap = this.sdkMethodReturnValueMap.get(calleeMethod); + if (!cidMap) { + cidMap = new Map(); + } + let newExpr = cidMap.get(calleeCid); + if (!newExpr) { + if (returnType instanceof ClassType) { + newExpr = new ArkNewExpr(returnType); + } + } + cidMap.set(calleeCid, newExpr); + this.sdkMethodReturnValueMap.set(calleeMethod, cidMap); + let srcPagNode = this.getOrNewPagNode(calleeCid, newExpr); + let dstPagNode = this.getOrNewPagNode(callerCid, cs.callStmt.getLeftOp(), cs.callStmt); + this.pag.addPagEdge(srcPagNode, dstPagNode, exports.PagEdgeKind.Address, cs.callStmt); + srcNodes.push(srcPagNode.getID()); + return srcNodes; + } + addSDKMethodParamPagEdge(cs, callerCid, calleeCid, funcID) { + let argNum = cs.args?.length; + let srcNodes = []; + if (!argNum) { + return srcNodes; + } + // add args to parameters edges + for (let i = 0; i < argNum; i++) { + let arg = cs.args?.at(i); + let paramValue; + if (arg instanceof Local && arg.getType() instanceof FunctionType) { + // TODO: cannot find value + paramValue = this.methodParamValueMap.get(funcID).get(i); + } + else { + continue; + } + if (!(arg && paramValue)) { + continue; + } + // Get or create new PAG node for argument and parameter + let srcPagNode = this.getOrNewPagNode(callerCid, arg, cs.callStmt); + let dstPagNode = this.getOrNewPagNode(calleeCid, paramValue, cs.callStmt); + if (dstPagNode instanceof PagLocalNode) { + // set the fake param Value in PagLocalNode + /** + * TODO: !!! + * some API param is in the form of anonymous method: + * component/common.d.ts + * declare function animateTo(value: AnimateParam, event: () => void): void; + * + * this param fake Value will create PagFuncNode rather than PagLocalNode + * when this API is called, the anonymous method pointer will not be able to pass into the fake Value PagNode + */ + dstPagNode.setSdkParam(); + let sdkParamInvokeStmt = new ArkInvokeStmt(new ArkPtrInvokeExpr(arg.getType().getMethodSignature(), paramValue, [])); + // create new DynCallSite + let sdkParamCallSite = new DynCallSite(sdkParamInvokeStmt, undefined, undefined, funcID); + dstPagNode.addRelatedDynCallSite(sdkParamCallSite); + } + this.pag.addPagEdge(srcPagNode, dstPagNode, exports.PagEdgeKind.Copy, cs.callStmt); + srcNodes.push(srcPagNode.getID()); + } + return srcNodes; + } + getOrNewPagNode(cid, v, s) { + if (v instanceof ArkThisRef) { + return this.getOrNewThisRefNode(cid, v); + } + // this local is also not uniq!!! + // remove below block once this issue fixed + // globalThis process can not be removed while all `globalThis` ref is the same Value + if (v instanceof Local) { + if (v.getName() === 'this') { + return this.getOrNewThisLoalNode(cid, v, s); + } + else if (v.getName() === GLOBAL_THIS_NAME && v.getDeclaringStmt() == null) { + // globalThis node has no cid + return this.getOrNewGlobalThisNode(-1); + } + } + if (v instanceof ArkInstanceFieldRef || v instanceof ArkStaticFieldRef) { + v = this.getRealInstanceRef(v); + } + return this.pag.getOrNewNode(cid, v, s); + } + /** + * return ThisRef PAG node according to cid, a cid has a unique ThisRef node + * @param cid: current contextID + */ + getOrNewThisRefNode(cid, v) { + let thisRefNodeID = this.cid2ThisRefMap.get(cid); + if (!thisRefNodeID) { + thisRefNodeID = -1; + } + let thisRefNode = this.pag.getOrNewThisRefNode(thisRefNodeID, v); + this.cid2ThisRefMap.set(cid, thisRefNode.getID()); + return thisRefNode; + } + // TODO: remove it once this local not uniq issue is fixed + getOrNewThisLoalNode(cid, v, s) { + let thisLocalNodeID = this.cid2ThisLocalMap.get(cid); + if (thisLocalNodeID) { + return this.pag.getNode(thisLocalNodeID); + } + let thisNode = this.pag.getOrNewNode(cid, v, s); + this.cid2ThisLocalMap.set(cid, thisNode.getID()); + return thisNode; + } + getOrNewGlobalThisNode(cid) { + return this.pag.getOrNewNode(cid, this.getGlobalThisValue()); + } + getUniqThisLocalNode(cid) { + return this.cid2ThisLocalMap.get(cid); + } + /** + * search the storage map to get propertyNode with given storage and propertyFieldName + * @param storage storage type: AppStorage, LocalStorage etc. + * @param propertyName string property key + * @returns propertyNode: PagLocalNode + */ + getOrNewPropertyNode(storage, propertyName, stmt) { + let propertyNode = this.getPropertyNode(storage, propertyName, stmt); + if (propertyNode) { + return propertyNode; + } + let storageMap = this.storagePropertyMap.get(storage); + let propertyLocal = new Local(propertyName); + storageMap.set(propertyName, propertyLocal); + this.storagePropertyMap.set(storage, storageMap); + return this.getOrNewPagNode(-1, propertyLocal, stmt); + } + getPropertyNode(storage, propertyName, stmt) { + let storageMap = this.storagePropertyMap.get(storage); + let propertyLocal; + if (!storageMap) { + storageMap = new Map(); + this.storagePropertyMap.set(storage, storageMap); + } + if (storageMap.has(propertyName)) { + propertyLocal = storageMap.get(propertyName); + } + if (propertyLocal) { + return this.getOrNewPagNode(-1, propertyLocal, stmt); + } + return undefined; + } + /** + * add PagEdge + * @param edgeKind: edge kind differs from API + * @param propertyNode: PAG node created by protpertyName + * @param obj: heapObj stored with Storage API + */ + addPropertyLinkEdge(propertyNode, storageObj, cid, stmt, edgeKind) { + if (!(storageObj.getType() instanceof ClassType)) { + return false; + } + if (edgeKind === exports.StorageLinkEdgeType.Property2Local) { + // propertyNode --> objNode + this.pag.addPagEdge(propertyNode, this.pag.getOrNewNode(cid, storageObj), exports.PagEdgeKind.Copy, stmt); + } + else if (edgeKind === exports.StorageLinkEdgeType.Local2Property) { + // propertyNode <-- objNode + this.pag.addPagEdge(this.pag.getOrNewNode(cid, storageObj), propertyNode, exports.PagEdgeKind.Copy, stmt); + } + else if (edgeKind === exports.StorageLinkEdgeType.TwoWay) { + // propertyNode <-> objNode + this.pag.addPagEdge(propertyNode, this.pag.getOrNewNode(cid, storageObj), exports.PagEdgeKind.Copy, stmt); + this.pag.addPagEdge(this.pag.getOrNewNode(cid, storageObj), propertyNode, exports.PagEdgeKind.Copy, stmt); + } + return true; + } + /* + * In ArkIR, ArkField has multiple instances for each stmt which use it + * But the unique one is needed for pointer analysis + * This is a temp solution to use a ArkField->(first instance) + * as the unique instance + * + * node merge condition: + * instance field: value and ArkField + * static field: ArkField + */ + getRealInstanceRef(v) { + if (!(v instanceof ArkInstanceFieldRef || v instanceof ArkStaticFieldRef)) { + return v; + } + let sig = v.getFieldSignature(); + let sigStr = sig.toString(); + let base; + let real; + if (v instanceof ArkInstanceFieldRef) { + base = v.getBase(); + if (base instanceof Local && base.getName() === GLOBAL_THIS_NAME && base.getDeclaringStmt() == null) { + // replace the base in fieldRef + base = this.getGlobalThisValue(); + v.setBase(base); + } + let key = `${base.getSignature()}-${sigStr}`; + real = this.instanceField2UniqInstanceMap.get(key); + if (!real) { + this.instanceField2UniqInstanceMap.set(key, v); + real = v; + } + } + else { + real = this.staticField2UniqInstanceMap.get(sigStr); + if (!real) { + this.staticField2UniqInstanceMap.set(sigStr, v); + real = v; + } + } + return real; + } + /** + * check if a method is singleton function + * rule: static method, assign heap obj to global var or static field, return the receiver + */ + isSingletonFunction(funcID) { + if (this.singletonFuncMap.has(funcID)) { + return this.singletonFuncMap.get(funcID); + } + let arkMethod = this.cg.getArkMethodByFuncID(funcID); + if (!arkMethod) { + this.singletonFuncMap.set(funcID, false); + return false; + } + if (!arkMethod.isStatic()) { + this.singletonFuncMap.set(funcID, false); + return false; + } + let funcPag = this.funcPags.get(funcID); + let heapObjects = [...funcPag.getInternalEdges()].filter(edge => edge.kind === exports.PagEdgeKind.Address).map(edge => edge.dst); + let returnValues = arkMethod.getReturnValues(); + let result = this.isValueConnected([...funcPag.getInternalEdges()], heapObjects, returnValues); + this.singletonFuncMap.set(funcID, result); + if (result) { + logger$a.info(`function ${funcID} is marked as singleton function`); + } + return result; + } + isValueConnected(edges, leftNodes, targetNodes) { + // build funcPag graph + const graph = new Map(); + let hasStaticFieldOrGlobalVar = false; + for (const edge of edges) { + let dst = this.getRealInstanceRef(edge.dst); + let src = this.getRealInstanceRef(edge.src); + if (!graph.has(dst)) { + graph.set(dst, []); + } + if (!graph.has(src)) { + graph.set(src, []); + } + if (dst instanceof ArkStaticFieldRef || src instanceof ArkStaticFieldRef) { + hasStaticFieldOrGlobalVar = true; + } + graph.get(src).push(dst); + } + if (!hasStaticFieldOrGlobalVar) { + return false; + } + for (const targetNode of targetNodes) { + for (const leftNode of leftNodes) { + const visited = new Set(); + let meetStaticField = false; + if (this.funcPagDfs(graph, visited, leftNode, targetNode, meetStaticField)) { + return true; // a value pair that satisfy condition + } + { + break; // heap obj will not deal any more + } + } + } + return false; + } + funcPagDfs(graph, visited, currentNode, targetNode, staticFieldFound) { + if (currentNode === targetNode) { + return staticFieldFound; + } + visited.add(currentNode); + for (const neighbor of graph.get(currentNode) || []) { + // TODO: add global variable + const isSpecialNode = neighbor instanceof ArkStaticFieldRef; + if (!visited.has(neighbor)) { + if (isSpecialNode) { + staticFieldFound = true; + } + if (this.funcPagDfs(graph, visited, neighbor, targetNode, staticFieldFound)) { + return true; + } + } + } + return false; + } + getGlobalThisValue() { + return this.globalThisValue; + } + getEdgeKindForAssignStmt(stmt) { + if (this.stmtIsCreateAddressObj(stmt)) { + return exports.PagEdgeKind.Address; + } + if (this.stmtIsCopyKind(stmt)) { + return exports.PagEdgeKind.Copy; + } + if (this.stmtIsReadKind(stmt)) { + return exports.PagEdgeKind.Load; + } + if (this.stmtIsWriteKind(stmt)) { + return exports.PagEdgeKind.Write; + } + return exports.PagEdgeKind.Unknown; + } + /** + * process Storage API + * @returns boolean: check if the cs represent a Storage API, no matter the API will success or fail + */ + processStorage(cs, calleeCGNode, cid) { + let storageName = calleeCGNode.getMethod().getDeclaringClassSignature().getClassName(); + let storageType = this.getStorageType(storageName, cs, cid); + // TODO: add other storages + if (storageType === exports.StorageType.APP_STORAGE) { + let calleeName = calleeCGNode.getMethod().getMethodSubSignature().getMethodName(); + // TODO: complete AppStorage API + if (calleeName === 'setOrCreate') { + this.processStorageSetOrCreate(cs, cid); + } + else if (calleeName === 'link') { + this.processStorageLink(cs, cid); + } + else if (calleeName === 'prop') { + this.processStorageProp(cs, cid); + } + else if (calleeName === 'set') { + this.processStorageSet(cs, cid); + } + else if (calleeName === 'get') { + this.processStorageGet(cs, cid); + } + return true; + } + else if (storageType === exports.StorageType.LOCAL_STORAGE) ; + return false; + } + processStorageSetOrCreate(cs, cid) { + let propertyStr = this.getPropertyName(cs.args[0]); + if (!propertyStr) { + return; + } + let propertyName = propertyStr; + let propertyNode = this.getOrNewPropertyNode(exports.StorageType.APP_STORAGE, propertyName, cs.callStmt); + let storageObj = cs.args[1]; + this.addPropertyLinkEdge(propertyNode, storageObj, cid, cs.callStmt, exports.StorageLinkEdgeType.Local2Property); + } + processStorageLink(cs, cid) { + let propertyStr = this.getPropertyName(cs.args[0]); + if (!propertyStr) { + return; + } + let propertyName = propertyStr; + let propertyNode = this.getOrNewPropertyNode(exports.StorageType.APP_STORAGE, propertyName, cs.callStmt); + let leftOp = cs.callStmt.getLeftOp(); + let linkedOpNode = this.pag.getOrNewNode(cid, leftOp); + if (linkedOpNode instanceof PagLocalNode) { + linkedOpNode.setStorageLink(exports.StorageType.APP_STORAGE, propertyName); + } + this.pag.addPagEdge(propertyNode, linkedOpNode, exports.PagEdgeKind.Copy); + this.pag.addPagEdge(linkedOpNode, propertyNode, exports.PagEdgeKind.Copy); + } + processStorageProp(cs, cid) { + let propertyStr = this.getPropertyName(cs.args[0]); + if (!propertyStr) { + return; + } + let propertyName = propertyStr; + let propertyNode = this.getOrNewPropertyNode(exports.StorageType.APP_STORAGE, propertyName, cs.callStmt); + let leftOp = cs.callStmt.getLeftOp(); + let linkedOpNode = this.pag.getOrNewNode(cid, leftOp); + if (linkedOpNode instanceof PagLocalNode) { + linkedOpNode.setStorageLink(exports.StorageType.APP_STORAGE, propertyName); + } + this.pag.addPagEdge(propertyNode, linkedOpNode, exports.PagEdgeKind.Copy); + } + processStorageSet(cs, cid) { + let ivkExpr = cs.callStmt.getInvokeExpr(); + if (ivkExpr instanceof ArkInstanceInvokeExpr) { + let base = ivkExpr.getBase(); + let baseNode = this.pag.getOrNewNode(cid, base); + if (baseNode.isStorageLinked()) { + let argsNode = this.pag.getOrNewNode(cid, cs.args[0]); + this.pag.addPagEdge(argsNode, baseNode, exports.PagEdgeKind.Copy); + } + } + } + processStorageGet(cs, cid) { + if (!(cs.callStmt instanceof ArkAssignStmt)) { + return; + } + let leftOp = cs.callStmt.getLeftOp(); + let ivkExpr = cs.callStmt.getInvokeExpr(); + let propertyName; + if (ivkExpr instanceof ArkStaticInvokeExpr) { + let propertyStr = this.getPropertyName(cs.args[0]); + if (propertyStr) { + propertyName = propertyStr; + } + } + else if (ivkExpr instanceof ArkInstanceInvokeExpr) { + let baseNode = this.pag.getOrNewNode(cid, ivkExpr.getBase()); + if (baseNode.isStorageLinked()) { + propertyName = baseNode.getStorage().PropertyName; + } + } + let propertyNode = this.getPropertyNode(exports.StorageType.APP_STORAGE, propertyName, cs.callStmt); + if (!propertyNode) { + return; + } + this.pag.addPagEdge(propertyNode, this.pag.getOrNewNode(cid, leftOp, cs.callStmt), exports.PagEdgeKind.Copy, cs.callStmt); + } + getPropertyName(value) { + if (value instanceof Local) { + let type = value.getType(); + if (type instanceof StringType) { + return type.getName(); + } + } + else if (value instanceof Constant) { + return value.getValue(); + } + return undefined; + } + /** + * get storageType enum with method's Declaring ClassName + * + * @param storageName ClassName that method belongs to, currently support AppStorage and SubscribedAbstractProperty + * SubscribedAbstractProperty: in following listing, `link1` is infered as ClassType `SubscribedAbstractProperty`, + * it needs to get PAG node to check the StorageType + * let link1: SubscribedAbstractProperty = AppStorage.link('PropA'); + * link1.set(a); + * @param cs: for search PAG node in SubscribedAbstractProperty + * @param cid: for search PAG node in SubscribedAbstractProperty + * @returns StorageType enum + */ + getStorageType(storageName, cs, cid) { + switch (storageName) { + case 'AppStorage': + return exports.StorageType.APP_STORAGE; + case 'SubscribedAbstractProperty': { + let calleeBaseLocal = cs.callStmt.getInvokeExpr().getBase(); + let calleeBaseLocalNode = this.pag.getOrNewNode(cid, calleeBaseLocal); + if (calleeBaseLocalNode.isStorageLinked()) { + let storage = calleeBaseLocalNode.getStorage(); + return storage.StorageType; + } + return exports.StorageType.Undefined; + } + default: + return exports.StorageType.Undefined; + } + } + /**\ + * ArkNewExpr, ArkNewArrayExpr, function ptr, globalThis + */ + stmtIsCreateAddressObj(stmt) { + let lhOp = stmt.getLeftOp(); + let rhOp = stmt.getRightOp(); + if (rhOp instanceof ArkNewExpr || + rhOp instanceof ArkNewArrayExpr || + (lhOp instanceof Local && + ((rhOp instanceof Local && rhOp.getType() instanceof FunctionType && rhOp.getDeclaringStmt() === null) || + (rhOp instanceof AbstractFieldRef && rhOp.getType() instanceof FunctionType))) || + (rhOp instanceof Local && rhOp.getName() === GLOBAL_THIS_NAME && rhOp.getDeclaringStmt() == null)) { + return true; + } + // TODO: add other Address Obj creation + // like static object + return false; + } + stmtIsCopyKind(stmt) { + let lhOp = stmt.getLeftOp(); + let rhOp = stmt.getRightOp(); + let condition = (lhOp instanceof Local && + (rhOp instanceof Local || rhOp instanceof ArkParameterRef || rhOp instanceof ArkThisRef || rhOp instanceof ArkStaticFieldRef)) || + (lhOp instanceof ArkStaticFieldRef && rhOp instanceof Local); + if (condition) { + return true; + } + return false; + } + stmtIsWriteKind(stmt) { + let lhOp = stmt.getLeftOp(); + let rhOp = stmt.getRightOp(); + if (rhOp instanceof Local && (lhOp instanceof ArkInstanceFieldRef || lhOp instanceof ArkArrayRef)) { + return true; + } + return false; + } + stmtIsReadKind(stmt) { + let lhOp = stmt.getLeftOp(); + let rhOp = stmt.getRightOp(); + if (lhOp instanceof Local && (rhOp instanceof ArkInstanceFieldRef || rhOp instanceof ArkArrayRef)) { + return true; + } + return false; + } + addToDynamicCallSite(funcPag, cs) { + funcPag.addDynamicCallSite(cs); + this.pagStat.numDynamicCall++; + logger$a.trace('[add dynamic callsite] ' + cs.callStmt.toString() + ': ' + cs.callStmt.getCfg()?.getDeclaringMethod().getSignature().toString()); + } + setPtForNode(node, pts) { + if (!pts) { + return; + } + this.pag.getNode(node).setPointTo(pts); + } + getRealThisLocal(input, funcId) { + if (input.getName() !== 'this') { + return input; + } + let real = input; + let f = this.cg.getArkMethodByFuncID(funcId); + f + ?.getCfg() + ?.getStmts() + .forEach(s => { + if (s instanceof ArkAssignStmt && s.getLeftOp() instanceof Local) { + if (s.getLeftOp().getName() === 'this') { + real = s.getLeftOp(); + return; + } + } + }); + return real; + } + doStat() { + this.pagStat.numTotalNode = this.pag.getNodeNum(); + } + printStat() { + this.pagStat.printStat(); + } + getStat() { + return this.pagStat.getStat(); + } + getUnhandledFuncs() { + let handledFuncs = this.getHandledFuncs(); + let unhandleFuncs = Array.from(this.cg.getNodesIter()) + .filter(f => !handledFuncs.includes(f.getID())) + .map(f => f.getID()); + return unhandleFuncs; + } + getHandledFuncs() { + return Array.from(this.funcPags.keys()); + } + /** + * build export edge in internal func pag + * @param value: Value that need to check if it is from import/export + * @param originValue: if Value if InstanceFieldRef, the base will be passed to `value` recursively, + * fieldRef will be passed to `originValue` + */ + handleValueFromExternalScope(value, funcID, originValue) { + if (value instanceof Local) { + if (value.getDeclaringStmt() || value.getName() === 'this') { + // not from external scope + return; + } + if (!value.getType()) { + return; + } + let srcLocal = this.getSourceValueFromExternalScope(value, funcID); + if (srcLocal) { + // if `value` is from field base, use origin value(fieldRef) instead + this.addInterFuncEdge(srcLocal, originValue ?? value, funcID); + } + } + else if (value instanceof ArkInstanceFieldRef) { + let base = value.getBase(); + if (base) { + this.handleValueFromExternalScope(base, funcID, value); + } + } + } + addInterFuncEdge(src, dst, funcID) { + this.interFuncPags = this.interFuncPags ?? new Map(); + let interFuncPag = this.interFuncPags.get(funcID) ?? new InterFuncPag(); + // Export a local + // Add a InterProcedural edge + if (dst instanceof Local) { + let e = { + src: src, + dst: dst, + kind: exports.PagEdgeKind.InterProceduralCopy, + }; + interFuncPag.addToInterProceduralEdgeSet(e); + this.addExportVariableMap(src, dst); + } + else if (dst instanceof ArkInstanceFieldRef) { + // record the export base use + this.addExportVariableMap(src, dst.getBase()); + } + this.interFuncPags.set(funcID, interFuncPag); + // Put the function which the src belongs to to worklist + let srcFunc = src.getDeclaringStmt()?.getCfg().getDeclaringMethod(); + if (srcFunc) { + let srcFuncID = this.cg.getCallGraphNodeByMethod(srcFunc.getSignature()).getID(); + let cid = this.ctx.getNewContextID(srcFuncID); + let csFuncID = new CSFuncID(cid, srcFuncID); + this.buildFuncPagAndAddToWorklist(csFuncID); + } + // Extend other types of src here + } + getSourceValueFromExternalScope(value, funcID) { + let sourceValue; + sourceValue = this.getDefaultMethodSourceValue(value, funcID); + if (!sourceValue) { + sourceValue = this.getExportSourceValue(value, funcID); + } + return sourceValue; + } + getDefaultMethodSourceValue(value, funcID) { + // namespace check + let arkMethod = this.cg.getArkMethodByFuncID(funcID); + if (!arkMethod) { + return undefined; + } + let declaringNameSpace = arkMethod.getDeclaringArkClass().getDeclaringArkNamespace(); + while (declaringNameSpace) { + let nameSpaceLocals = declaringNameSpace.getDefaultClass().getDefaultArkMethod()?.getBody()?.getLocals() ?? new Map(); + if (nameSpaceLocals.has(value.getName())) { + return nameSpaceLocals.get(value.getName()); + } + declaringNameSpace = declaringNameSpace.getDeclaringArkNamespace() ?? undefined; + } + // file check + let declaringFile = arkMethod.getDeclaringArkFile(); + let fileLocals = declaringFile.getDefaultClass().getDefaultArkMethod()?.getBody()?.getLocals() ?? new Map(); + if (!fileLocals.has(value.getName())) { + return undefined; + } + return fileLocals.get(value.getName()); + } + getExportSourceValue(value, funcID) { + let curMethod = this.cg.getArkMethodByFuncID(funcID); + if (!curMethod) { + return undefined; + } + let curFile = curMethod.getDeclaringArkFile(); + let impInfo = curFile.getImportInfoBy(value.getName()); + if (!impInfo) { + return undefined; + } + let exportSource = impInfo.getLazyExportInfo(); + if (!exportSource) { + return undefined; + } + let exportSouceValue = exportSource.getArkExport(); + if (exportSouceValue instanceof Local) { + return exportSouceValue; + } + return undefined; + } + addExportVariableMap(src, dst) { + let exportMap = this.externalScopeVariableMap.get(src) ?? []; + if (!exportMap.includes(dst)) { + exportMap.push(dst); + this.externalScopeVariableMap.set(src, exportMap); + } + } + getExportVariableMap(src) { + return this.externalScopeVariableMap.get(src) ?? []; + } + /// Add inter-procedural Pag Nodes and Edges + addEdgesFromInterFuncPag(interFuncPag, cid) { + let edges = interFuncPag.getInterProceduralEdges(); + if (edges.size === 0) { + return false; + } + for (let e of edges) { + // Existing local exported nodes -> ExportNode + let exportLocal = e.src; + let dstPagNode = this.getOrNewPagNode(cid, e.dst); + // get export local node in all cid + let existingNodes = this.pag.getNodesByValue(exportLocal); + existingNodes?.forEach(n => { + this.pag.addPagEdge(this.pag.getNode(n), dstPagNode, e.kind); + this.retriggerNodesList.add(n); + }); + } + return true; + } + getRetriggerNodes() { + let retriggerNodes = Array.from(this.retriggerNodesList); + this.retriggerNodesList.clear(); + return retriggerNodes; + } + addUpdatedNode(nodeID, diffPT) { + let ptaConfig = PointerAnalysisConfig.getInstance(); + let updatedNode = this.updatedNodesThisRound.get(nodeID) ?? new ptaConfig.ptsCollectionCtor(); + updatedNode.union(diffPT); + this.updatedNodesThisRound.set(nodeID, updatedNode); + } + getUpdatedNodes() { + return this.updatedNodesThisRound; + } + resetUpdatedNodes() { + this.updatedNodesThisRound.clear(); + } + transferArrayValues(method, arrayLocal) { + if (!(arrayLocal instanceof Local) || !(arrayLocal.getType() instanceof ArrayType)) { + return []; + } + /** + * TODO: get array element values + * need to resolve multi dimension array + */ + const usedValuesInArray = arrayLocal.getUsedStmts().flatMap(stmt => { + if (stmt instanceof ArkAssignStmt) { + const rightOp = stmt.getRightOp(); + if (rightOp instanceof Local) { + return rightOp; + } + } + return []; + }); + return usedValuesInArray; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** +收集所有的onCreate,onStart等函数,构造一个虚拟函数,具体为: +%statInit() +... +count = 0 +while (true) { + if (count === 1) { + temp1 = new ability + temp2 = new want + temp1.onCreate(temp2) + } + if (count === 2) { + onDestroy() + } + ... + if (count === *) { + callbackMethod1() + } + ... +} +return +如果是instanceInvoke还要先实例化对象,如果是其他文件的类或者方法还要添加import信息 + */ +class DummyMainCreater { + entryMethods = []; + classLocalMap = new Map(); + dummyMain = new ArkMethod(); + scene; + tempLocalIndex = 0; + constructor(scene) { + this.scene = scene; + // Currently get entries from module.json5 can't visit all of abilities + // Todo: handle ablity/component jump, then get entries from module.json5 + this.entryMethods = this.getMethodsFromAllAbilities(); + this.entryMethods.push(...this.getEntryMethodsFromComponents()); + this.entryMethods.push(...this.getCallbackMethods()); + } + setEntryMethods(methods) { + this.entryMethods = methods; + } + createDummyMain() { + const dummyMainFile = new ArkFile(Language.UNKNOWN); + dummyMainFile.setScene(this.scene); + const dummyMainFileSignature = new FileSignature(this.scene.getProjectName(), '@dummyFile'); + dummyMainFile.setFileSignature(dummyMainFileSignature); + this.scene.setFile(dummyMainFile); + const dummyMainClass = new ArkClass(); + dummyMainClass.setDeclaringArkFile(dummyMainFile); + const dummyMainClassSignature = new ClassSignature('@dummyClass', dummyMainClass.getDeclaringArkFile().getFileSignature(), dummyMainClass.getDeclaringArkNamespace()?.getSignature() || null); + dummyMainClass.setSignature(dummyMainClassSignature); + dummyMainFile.addArkClass(dummyMainClass); + this.dummyMain = new ArkMethod(); + this.dummyMain.setDeclaringArkClass(dummyMainClass); + const methodSubSignature = ArkSignatureBuilder.buildMethodSubSignatureFromMethodName('@dummyMain'); + const methodSignature = new MethodSignature(this.dummyMain.getDeclaringArkClass().getSignature(), methodSubSignature); + this.dummyMain.setImplementationSignature(methodSignature); + this.dummyMain.setLineCol(0); + checkAndUpdateMethod(this.dummyMain, dummyMainClass); + dummyMainClass.addMethod(this.dummyMain); + let defaultMethods = []; + for (const method of this.entryMethods) { + if (method.getDeclaringArkClass().isDefaultArkClass() || method.isStatic()) { + defaultMethods.push(method); + continue; + } + const declaringArkClass = method.getDeclaringArkClass(); + let newLocal = null; + for (const local of this.classLocalMap.values()) { + if ((local?.getType()).getClassSignature() === declaringArkClass.getSignature()) { + newLocal = local; + break; + } + } + if (!newLocal) { + newLocal = new Local('%' + this.tempLocalIndex, new ClassType(declaringArkClass.getSignature())); + this.tempLocalIndex++; + } + this.classLocalMap.set(method, newLocal); + } + for (const defaultMethod of defaultMethods) { + this.classLocalMap.set(defaultMethod, null); + } + const localSet = new Set(Array.from(this.classLocalMap.values()).filter((value) => value !== null)); + const dummyBody = new ArkBody(localSet, this.createDummyMainCfg()); + this.dummyMain.setBody(dummyBody); + this.addCfg2Stmt(); + this.scene.addToMethodsMap(this.dummyMain); + } + addStaticInit(dummyCfg, firstBlock) { + let isStartingStmt = true; + for (const method of this.scene.getStaticInitMethods()) { + const staticInvokeExpr = new ArkStaticInvokeExpr(method.getSignature(), []); + const invokeStmt = new ArkInvokeStmt(staticInvokeExpr); + if (isStartingStmt) { + dummyCfg.setStartingStmt(invokeStmt); + isStartingStmt = false; + } + firstBlock.addStmt(invokeStmt); + } + } + addClassInit(firstBlock) { + const locals = Array.from(new Set(this.classLocalMap.values())); + for (const local of locals) { + if (!local) { + continue; + } + let clsType = local.getType(); + let cls = this.scene.getClass(clsType.getClassSignature()); + const assStmt = new ArkAssignStmt(local, new ArkNewExpr(clsType)); + firstBlock.addStmt(assStmt); + local.setDeclaringStmt(assStmt); + let consMtd = cls.getMethodWithName(CONSTRUCTOR_NAME); + if (consMtd) { + let ivkExpr = new ArkInstanceInvokeExpr(local, consMtd.getSignature(), []); + let ivkStmt = new ArkInvokeStmt(ivkExpr); + firstBlock.addStmt(ivkStmt); + } + } + } + addParamInit(method, paramLocals, invokeBlock) { + let paramIdx = 0; + for (const param of method.getParameters()) { + let paramType = param.getType(); + // In ArkIR from abc scenario, param type is undefined in some cases + // Then try to get it from super class(SDK) + // TODO - need handle method overload to get the correct method + if (!paramType) { + let superCls = method.getDeclaringArkClass().getSuperClass(); + let methodInSuperCls = superCls?.getMethodWithName(method.getName()); + if (methodInSuperCls) { + paramType = methodInSuperCls.getParameters().at(paramIdx)?.getType(); + method = methodInSuperCls; + } + } + const paramLocal = new Local('%' + this.tempLocalIndex++, paramType); + paramLocals.push(paramLocal); + if (paramType instanceof ClassType) { + const assStmt = new ArkAssignStmt(paramLocal, new ArkNewExpr(paramType)); + paramLocal.setDeclaringStmt(assStmt); + invokeBlock.addStmt(assStmt); + } + paramIdx++; + } + } + addBranches(whileBlock, countLocal, dummyCfg) { + let lastBlocks = [whileBlock]; + let count = 0; + for (let method of this.entryMethods) { + count++; + const condition = new ArkConditionExpr(countLocal, new Constant(count.toString(), NumberType.getInstance()), exports.RelationalBinaryOperator.Equality); + const ifStmt = new ArkIfStmt(condition); + const ifBlock = new BasicBlock(); + ifBlock.addStmt(ifStmt); + dummyCfg.addBlock(ifBlock); + for (const block of lastBlocks) { + ifBlock.addPredecessorBlock(block); + block.addSuccessorBlock(ifBlock); + } + const invokeBlock = new BasicBlock(); + const paramLocals = []; + this.addParamInit(method, paramLocals, invokeBlock); + const local = this.classLocalMap.get(method); + let invokeExpr; + if (local) { + invokeExpr = new ArkInstanceInvokeExpr(local, method.getSignature(), paramLocals); + } + else { + invokeExpr = new ArkStaticInvokeExpr(method.getSignature(), paramLocals); + } + const invokeStmt = new ArkInvokeStmt(invokeExpr); + invokeBlock.addStmt(invokeStmt); + dummyCfg.addBlock(invokeBlock); + ifBlock.addSuccessorBlock(invokeBlock); + invokeBlock.addPredecessorBlock(ifBlock); + lastBlocks = [ifBlock, invokeBlock]; + } + for (const block of lastBlocks) { + block.addSuccessorBlock(whileBlock); + whileBlock.addPredecessorBlock(block); + } + } + createDummyMainCfg() { + const dummyCfg = new Cfg(); + dummyCfg.setDeclaringMethod(this.dummyMain); + const firstBlock = new BasicBlock(); + this.addStaticInit(dummyCfg, firstBlock); + this.addClassInit(firstBlock); + const countLocal = new Local('count', NumberType.getInstance()); + const zero = ValueUtil.getOrCreateNumberConst(0); + const countAssignStmt = new ArkAssignStmt(countLocal, zero); + const truE = ValueUtil.getBooleanConstant(true); + const conditionTrue = new ArkConditionExpr(truE, zero, exports.RelationalBinaryOperator.Equality); + const whileStmt = new ArkIfStmt(conditionTrue); + firstBlock.addStmt(countAssignStmt); + dummyCfg.addBlock(firstBlock); + dummyCfg.setStartingStmt(firstBlock.getStmts()[0]); + const whileBlock = new BasicBlock(); + whileBlock.addStmt(whileStmt); + dummyCfg.addBlock(whileBlock); + firstBlock.addSuccessorBlock(whileBlock); + whileBlock.addPredecessorBlock(firstBlock); + this.addBranches(whileBlock, countLocal, dummyCfg); + const returnStmt = new ArkReturnVoidStmt(); + const returnBlock = new BasicBlock(); + returnBlock.addStmt(returnStmt); + dummyCfg.addBlock(returnBlock); + whileBlock.addSuccessorBlock(returnBlock); + returnBlock.addPredecessorBlock(whileBlock); + return dummyCfg; + } + addCfg2Stmt() { + const cfg = this.dummyMain.getCfg(); + if (!cfg) { + return; + } + for (const block of cfg.getBlocks()) { + for (const stmt of block.getStmts()) { + stmt.setCfg(cfg); + } + } + } + getDummyMain() { + return this.dummyMain; + } + getEntryMethodsFromComponents() { + const COMPONENT_BASE_CLASSES = ['CustomComponent', 'ViewPU']; + let methods = []; + this.scene + .getClasses() + .filter(cls => { + if (COMPONENT_BASE_CLASSES.includes(cls.getSuperClassName())) { + return true; + } + if (cls.hasDecorator('Component')) { + return true; + } + return false; + }) + .forEach(cls => { + methods.push(...cls.getMethods().filter(mtd => COMPONENT_LIFECYCLE_METHOD_NAME.includes(mtd.getName()))); + }); + return methods; + } + classInheritsAbility(arkClass) { + const ABILITY_BASE_CLASSES = ['UIExtensionAbility', 'Ability', 'FormExtensionAbility', 'UIAbility', 'BackupExtensionAbility']; + if (ABILITY_BASE_CLASSES.includes(arkClass.getSuperClassName())) { + return true; + } + let superClass = arkClass.getSuperClass(); + while (superClass) { + if (ABILITY_BASE_CLASSES.includes(superClass.getSuperClassName())) { + return true; + } + superClass = superClass.getSuperClass(); + } + return false; + } + getMethodsFromAllAbilities() { + let methods = []; + this.scene + .getClasses() + .filter(cls => this.classInheritsAbility(cls)) + .forEach(cls => { + methods.push(...cls.getMethods().filter(mtd => LIFECYCLE_METHOD_NAME.includes(mtd.getName()))); + }); + return methods; + } + getCallbackMethods() { + const callbackMethods = []; + this.scene.getMethods().forEach(method => { + if (!method.getCfg()) { + return; + } + method + .getCfg() + .getStmts() + .forEach(stmt => { + const cbMethod = getCallbackMethodFromStmt(stmt, this.scene); + if (cbMethod && !callbackMethods.includes(cbMethod)) { + callbackMethods.push(cbMethod); + } + }); + }); + return callbackMethods; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$9 = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'PTA'); +class PointerAnalysis extends AbstractAnalysis { + pag; + pagBuilder; + ptd; + entries; + worklist; + // record all updated nodes + ptaStat; + typeDiffMap; + config; + constructor(p, cg, s, config) { + super(s, cg); + this.pag = p; + this.ptd = new DiffPTData(config.ptsCollectionCtor); + this.pagBuilder = new PagBuilder(this.pag, this.cg, s, config.kLimit, config.analysisScale); + this.cgBuilder = new CallGraphBuilder(this.cg, s); + this.ptaStat = new PTAStat(this); + this.config = config; + } + static pointerAnalysisForWholeProject(projectScene, config) { + let cg = new CallGraph(projectScene); + let cgBuilder = new CallGraphBuilder(cg, projectScene); + cgBuilder.buildDirectCallGraphForScene(); + let pag = new Pag(); + if (!config) { + config = PointerAnalysisConfig.create(1, 'out/', false, false); + } + const dummyMainCreator = new DummyMainCreater(projectScene); + dummyMainCreator.createDummyMain(); + const dummyMainMethod = dummyMainCreator.getDummyMain(); + cgBuilder.buildDirectCallGraph([dummyMainMethod]); + let dummyMainMethodID = cg.getCallGraphNodeByMethod(dummyMainMethod.getSignature()).getID(); + cg.setDummyMainFuncID(dummyMainMethodID); + let pta = new PointerAnalysis(pag, cg, projectScene, config); + pta.setEntries([dummyMainMethodID]); + pta.start(); + return pta; + } + static pointerAnalysisForMethod(s, method, config) { + let cg = new CallGraph(s); + let cgBuilder = new CallGraphBuilder(cg, s); + cgBuilder.buildDirectCallGraphForScene(); + let pag = new Pag(); + if (!config) { + config = PointerAnalysisConfig.create(1, 'out/', false, false); + } + let entryMethodID = cg.getCallGraphNodeByMethod(method.getSignature()).getID(); + let pta = new PointerAnalysis(pag, cg, s, config); + pta.setEntries([entryMethodID]); + pta.start(); + return pta; + } + init() { + logger$9.warn(`========== Init Pointer Analysis ==========`); + // start statistics + this.ptaStat.startStat(); + // build funcPag with entries + this.pagBuilder.buildForEntries(this.entries); + if (this.config.dotDump) { + this.pag.dump(path$1.join(this.config.outputDirectory, 'ptaInit_pag.dot')); + this.cg.dump(path$1.join(this.config.outputDirectory, 'cg_init.dot')); + } + } + start() { + this.init(); + this.solveConstraint(); + this.postProcess(); + } + postProcess() { + this.ptaStat.endStat(); + this.pagBuilder.doStat(); + this.cg.printStat(); + this.pagBuilder.printStat(); + this.ptaStat.printStat(); + if (this.config.dotDump) { + this.pag.dump(path$1.join(this.config.outputDirectory, 'ptaEnd_pag.dot')); + this.cg.dump(path$1.join(this.config.outputDirectory, 'cgEnd.dot')); + } + if (this.config.unhandledFuncDump) { + this.dumpUnhandledFunctions(); + } + } + getPTD() { + return this.ptd; + } + getStat() { + let ret = this.cg.getStat(); + ret += '\n' + this.pagBuilder.getStat(); + ret += '\n' + this.ptaStat.getStat(); + return ret; + } + preProcessMethod(funcID) { + // do nothing + return []; + } + setEntries(fIds) { + this.entries = fIds; + } + solveConstraint() { + this.worklist = []; + logger$9.warn(`========== Pointer Analysis Start ==========`); + this.initWorklist(); + let reanalyzer = true; + while (reanalyzer) { + this.ptaStat.iterTimes++; + logger$9.warn(`========== Pointer Analysis Round ${this.ptaStat.iterTimes} ==========`); + // do pointer transfer + this.solveWorklist(); + // process dynamic call + if (this.config.analysisScale === PtaAnalysisScale.WholeProgram || this.ptaStat.iterTimes === 1) { + reanalyzer = this.onTheFlyDynamicCallSolve(); + } + else { + reanalyzer = false; + } + if (this.config.dotDump) { + this.pag.dump(path$1.join(this.config.outputDirectory, `pta_pag_itor#${this.ptaStat.iterTimes}.dot`)); + } + } + } + /** + * get newly added Address Edge, and add them to initial WorkList + */ + initWorklist() { + let changed = false; + this.addToReanalyze(this.pagBuilder.getRetriggerNodes()); + for (let e of this.pag.getAddrEdges()) { + this.ptaStat.numProcessedAddr++; + let { src, dst } = e.getEndPoints(); + this.ptd.addPts(dst, src); + if (this.pag.getNode(src) instanceof PagGlobalThisNode) { + // readd globalThis heapObj into workList + this.ptd.addPts(src, src); + this.worklist.push(src); + } + this.worklist.push(dst); + changed = true; + } + this.pag.resetAddrEdges(); + return changed; + } + solveWorklist() { + while (this.worklist.length > 0) { + let node = this.worklist.shift(); + this.processNode(node); + } + return true; + } + processNode(nodeId) { + this.handleThis(nodeId); + this.handleLoadWrite(nodeId); + this.handleCopy(nodeId); + this.handlePt(nodeId); + this.detectTypeDiff(nodeId); + return true; + } + handleCopy(nodeID) { + let node = this.pag.getNode(nodeID); + node.getOutgoingCopyEdges()?.forEach(copyEdge => { + this.propagate(copyEdge); + this.ptaStat.numProcessedCopy++; + }); + return true; + } + handleLoadWrite(nodeID) { + let node = this.pag.getNode(nodeID); + let nodeValue = node.getValue(); + let diffPts = this.ptd.getDiffPts(nodeID); + if (!diffPts || diffPts.count() === 0) { + return false; + } + // get related field node with current node's value + let instanceFieldNodeMap = this.pag.getNodesByBaseValue(nodeValue) ?? new Map(); + // get intra procedural field node by exportMap + let intraProceduralFieldNodeMap = new Map(); + if (nodeValue instanceof Local) { + this.pagBuilder.getExportVariableMap(nodeValue).forEach(dst => { + let temp = this.pag.getNodesByBaseValue(dst) ?? new Map(); + intraProceduralFieldNodeMap = this.mergeInstanceFieldMap(instanceFieldNodeMap, temp); + }); + } + instanceFieldNodeMap.forEach((nodeIDs, cid) => { + // TODO: check cid + // cid === -1 will escape the check, mainly for globalThis + let baseCid = node.getCid(); + if (baseCid !== -1 && cid !== baseCid) { + return; + } + nodeIDs.forEach((nodeID) => { + // get abstract field node + let fieldNode = this.pag.getNode(nodeID); + this.handleFieldInEdges(fieldNode, diffPts); + this.handleFieldOutEdges(fieldNode, diffPts); + }); + }); + // without cid check, because closure and export is under different cid + intraProceduralFieldNodeMap.forEach(nodeIDs => { + nodeIDs.forEach((nodeID) => { + // get abstract field node + let fieldNode = this.pag.getNode(nodeID); + this.handleFieldInEdges(fieldNode, diffPts); + this.handleFieldOutEdges(fieldNode, diffPts); + }); + }); + return true; + } + handleFieldInEdges(fieldNode, diffPts) { + fieldNode.getIncomingEdge().forEach(edge => { + if (edge.getKind() !== exports.PagEdgeKind.Write) { + return; + } + let srcNode = edge.getSrcNode(); + this.ptaStat.numProcessedWrite++; + for (let pt of diffPts) { + // filter pt + // clone the real field node with abstract field node + let dstNode; + if (fieldNode instanceof PagArrayNode) { + dstNode = this.pag.getOrClonePagContainerFieldNode(pt, fieldNode); + } + else { + dstNode = this.pag.getOrClonePagFieldNode(fieldNode, pt); + } + if (!(dstNode && this.pag.addPagEdge(srcNode, dstNode, exports.PagEdgeKind.Copy))) { + continue; + } + this.ptaStat.numRealWrite++; + if (this.ptd.resetElem(srcNode.getID())) { + this.worklist.push(srcNode.getID()); + } + } + }); + } + handleFieldOutEdges(fieldNode, diffPts) { + fieldNode.getOutgoingEdges().forEach(edge => { + if (edge.getKind() !== exports.PagEdgeKind.Load) { + return; + } + let dstNode = edge.getDstNode(); + this.ptaStat.numProcessedLoad++; + for (let pt of diffPts) { + let srcNode; + if (fieldNode instanceof PagArrayNode) { + srcNode = this.pag.getOrClonePagContainerFieldNode(pt, fieldNode); + } + else { + srcNode = this.pag.getOrClonePagFieldNode(fieldNode, pt); + } + if (!(srcNode && this.pag.addPagEdge(srcNode, dstNode, exports.PagEdgeKind.Copy))) { + continue; + } + this.ptaStat.numRealLoad++; + // TODO: if field is used before initialzed, newSrc node has no diff pts + if (this.ptd.resetElem(srcNode.getID())) { + this.worklist.push(srcNode.getID()); + } + } + }); + } + /** + * If current node is a base of a called method, pointer in this node will be transfered into `this` Local in method + */ + handleThis(nodeID) { + let node = this.pag.getNode(nodeID); + node.getOutgoingThisEdges()?.forEach(thisEdge => { + this.propagate(thisEdge); + this.ptaStat.numProcessedThis++; + }); + return true; + } + handlePt(nodeID) { + let realDiff = this.ptd.calculateDiff(nodeID, nodeID); + if (realDiff.count() !== 0) { + // record the updated nodes + this.pagBuilder.addUpdatedNode(nodeID, realDiff); + } + this.ptd.flush(nodeID); + this.pagBuilder.setPtForNode(nodeID, this.ptd.getPropaPts(nodeID)); + } + propagate(edge) { + let changed = false; + let { src, dst } = edge.getEndPoints(); + let diffPts = this.ptd.getDiffPts(src); + if (!diffPts) { + return changed; + } + let realDiffPts = this.ptd.calculateDiff(src, dst); + for (let pt of realDiffPts) { + changed = this.ptd.addPts(dst, pt) || changed; + } + if (changed) { + this.worklist.push(dst); + } + return changed; + } + /** + * 1. 记录被更新的节点(记录cid, nodeid) + * 2. ( PAGLocalNode记录callsite(cid, value唯一)),通过1种的nodeID查询Node,拿到Callsite + * 3. 在addDynamicCall里对传入指针过滤(已处理指针和未处理指针) + */ + onTheFlyDynamicCallSolve() { + let changed = false; + let processedCallSites = new Set(); + this.pagBuilder.getUpdatedNodes().forEach((pts, nodeID) => { + let node = this.pag.getNode(nodeID); + if (!(node instanceof PagLocalNode)) { + logger$9.warn(`node ${nodeID} is not local node, value: ${node.getValue()}`); + return; + } + changed = this.processDynCallSite(node, pts, processedCallSites) || changed; + changed = this.processUnknownCallSite(node, pts) || changed; + }); + this.pagBuilder.resetUpdatedNodes(); + let srcNodes = this.pagBuilder.handleUnprocessedCallSites(processedCallSites); + changed = this.addToReanalyze(srcNodes) || changed; + changed = this.pagBuilder.handleReachable() || changed; + changed = this.initWorklist() || changed; + return changed; + } + processDynCallSite(node, pts, processedCallSites) { + let changed = false; + let dynCallSites = node.getRelatedDynCallSites(); + if (!dynCallSites && !node.isSdkParam()) { + logger$9.warn(`node ${node.getID()} has no related dynamic call site`); + return changed; + } + logger$9.info(`[process dynamic callsite] node ${node.getID()}`); + dynCallSites.forEach(dynCallsite => { + for (let pt of pts) { + let srcNodes = this.pagBuilder.addDynamicCallEdge(dynCallsite, pt, node.getCid()); + changed = this.addToReanalyze(srcNodes) || changed; + } + processedCallSites.add(dynCallsite); + }); + return changed; + } + processUnknownCallSite(node, pts) { + let changed = false; + let unknownCallSites = node.getRelatedUnknownCallSites(); + if (!unknownCallSites) { + logger$9.warn(`node ${node.getID()} has no related unknown call site`); + return changed; + } + logger$9.info(`[process unknown callsite] node ${node.getID()}`); + unknownCallSites.forEach(unknownCallSite => { + for (let pt of pts) { + let srcNodes = this.pagBuilder.addDynamicCallEdge(unknownCallSite, pt, node.getCid()); + changed = this.addToReanalyze(srcNodes) || changed; + } + }); + return changed; + } + addToReanalyze(startNodes) { + let flag = false; + for (let node of startNodes) { + if (!this.worklist.includes(node) && this.ptd.resetElem(node)) { + this.worklist.push(node); + flag = true; + } + } + return flag; + } + /** + * compare interface + */ + noAlias(leftValue, rightValue) { + let leftValueNodes = this.pag.getNodesByValue(leftValue)?.values(); + let rightValueNodes = this.pag.getNodesByValue(rightValue)?.values(); + let leftValuePts = new Set(); + let rightValuePts = new Set(); + for (let nodeID of leftValueNodes) { + let node = this.pag.getNode(nodeID); + for (let pt of node.getPointTo()) { + leftValuePts.add(pt); + } + } + for (let nodeID of rightValueNodes) { + let node = this.pag.getNode(nodeID); + for (let pt of node.getPointTo()) { + rightValuePts.add(pt); + } + } + if (leftValuePts.size > rightValuePts.size) { + [leftValuePts, rightValuePts] = [rightValuePts, leftValuePts]; + } + for (const elem of leftValuePts) { + if (rightValuePts.has(elem)) { + return false; + } + } + // no alias + return true; + } + mayAlias(leftValue, rightValue) { + return !this.noAlias(leftValue, rightValue); + } + getRelatedNodes(value) { + let valueNodes = this.pag.getNodesByValue(value); + let relatedAllNodes = new Set(); + let workListNodes = []; + let processedNodes = new Set(); + if (valueNodes) { + for (const nodeID of valueNodes.values()) { + workListNodes.push(nodeID); + } + } + while (workListNodes.length !== 0) { + let valueNodeID = workListNodes.shift(); + if (processedNodes.has(valueNodeID)) { + continue; + } + this.processRelatedNode(valueNodeID, workListNodes, processedNodes); + } + processedNodes.forEach(nodeID => { + let valueNode = this.pag.getNode(nodeID); + relatedAllNodes.add(valueNode.getValue()); + }); + return relatedAllNodes; + } + processRelatedNode(valueNodeID, workListNodes, processedNodes) { + let valueNode = this.pag.getNode(valueNodeID); + this.addIncomingEdgesToWorkList(valueNode, workListNodes, processedNodes); + this.addOutgoingEdgesToWorkList(valueNode, workListNodes, processedNodes); + processedNodes.add(valueNodeID); + } + addIncomingEdgesToWorkList(valueNode, workListNodes, processedNodes) { + let inCopyEdges = valueNode.getIncomingCopyEdges(); + let inThisEdges = valueNode.getIncomingThisEdges(); + let combinedEdges = new Set([...(inCopyEdges ?? []), ...(inThisEdges ?? [])]); + if (combinedEdges) { + combinedEdges.forEach(edge => { + let srcID = edge.getSrcID(); + if (!processedNodes.has(srcID)) { + workListNodes.push(srcID); + } + }); + } + } + addOutgoingEdgesToWorkList(valueNode, workListNodes, processedNodes) { + let outCopyEdges = valueNode.getOutgoingCopyEdges(); + let outThisEdges = valueNode.getOutgoingThisEdges(); + let combinedEdges = new Set([...(outCopyEdges ?? []), ...(outThisEdges ?? [])]); + if (combinedEdges) { + combinedEdges.forEach(edge => { + let dstID = edge.getDstID(); + if (!processedNodes.has(dstID)) { + workListNodes.push(dstID); + } + }); + } + } + detectTypeDiff(nodeId) { + if (this.config.detectTypeDiff === false) { + return; + } + this.typeDiffMap = this.typeDiffMap ?? new Map(); + let node = this.pag.getNode(nodeId); + let value = node.getValue(); + let origType = node.getValue().getType(); + // TODO: union type + if (!(origType instanceof ClassType || origType instanceof UnknownType)) { + return; + } + let findSameType = false; + let pts = node.getPointTo(); + if (pts.count() === 0) { + return; + } + for (let pt of pts) { + let ptNode = this.pag.getNode(pt); + let type = ptNode.getValue().getType(); + if (type.toString() !== origType.toString()) { + let diffSet = this.typeDiffMap.get(value) ?? new Set(); + this.typeDiffMap.set(value, diffSet); + if (!diffSet.has(type)) { + diffSet.add(type); + } + } + else { + findSameType = true; + } + } + // If find pts to original type, + // need add original type back since it is a correct type + let diffSet = this.typeDiffMap.get(value); + if (diffSet && findSameType) { + diffSet.add(origType); + } + } + getTypeDiffMap() { + return this.typeDiffMap ?? new Map(); + } + resolveCall(sourceMethod, invokeStmt) { + return []; + } + getUnhandledFuncs() { + return this.pagBuilder.getUnhandledFuncs(); + } + getHandledFuncs() { + return this.pagBuilder.getHandledFuncs(); + } + getPTAConfig() { + return this.config; + } + dumpUnhandledFunctions() { + const filePath = path$1.join(this.config.outputDirectory, 'PtaUnhandledFunctionList.txt'); + fs__namespace.access(filePath, fs__namespace.constants.F_OK, err => { + if (!err) { + fs__namespace.truncate(filePath, 0, err => { + err && logger$9.error('Error to truncate file ', err); + }); + } + let updatedContent = ''; + this.getUnhandledFuncs().forEach(funcID => { + let cgNode = this.cg.getNode(funcID); + if (cgNode.isSdkMethod()) { + return; + } + let f = this.cg.getArkMethodByFuncID(funcID); + if (f) { + updatedContent += f.getSignature().toString() + '\n'; + } + }); + fs__namespace.writeFile(filePath, updatedContent, 'utf8', err => { + if (err) { + logger$9.error('Error to write file', err); + } + }); + }); + } + mergeInstanceFieldMap(src, dst) { + dst.forEach((value, key) => { + if (src.has(key)) { + src.set(key, [...src.get(key), ...value]); + } + else { + src.set(key, value); + } + }); + return src; + } +} + +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Direct value flow graph + * Consist of stmt(node) and direct Def-Use edge + * Is basic of VFG. And VFG is building on DVFG + */ +class DVFG extends BaseExplicitGraph { + cg; + stmtToVFGMap; + constructor(cg) { + super(); + this.cg = cg; + this.stmtToVFGMap = new Map(); + } + getCG() { + return this.cg; + } + getGraphName() { + return 'Direct-VFG'; + } + getOrNewDVFGNode(stmt) { + let node = this.stmtToVFGMap.get(stmt); + if (node) { + return this.getNode(node); + } + let kind = DVFGNodeKind.normal; + if (stmt instanceof ArkAssignStmt) { + //TODO: split assign to copy, write, load + kind = DVFGNodeKind.assign; + } + return this.addDVFGNode(stmt, kind); + } + addDVFGNode(stmt, kind) { + let id = this.nodeNum; + let dvfgNode = new DVFGNode(id, kind, stmt); + this.addNode(dvfgNode); + this.stmtToVFGMap.set(stmt, dvfgNode.getID()); + return dvfgNode; + } + addDVFGEdge(src, dst) { + let kind = 0; //common kind + let edge = new DVFGEdge(src, dst, kind); + if (this.ifEdgeExisting(edge)) { + return false; + } + src.addOutgoingEdge(edge); + dst.addIncomingEdge(edge); + return true; + } + dump(name) { + let printer = new GraphPrinter(this); + PrinterBuilder.dump(printer, name); + } +} +var DVFGNodeKind; +(function (DVFGNodeKind) { + DVFGNodeKind[DVFGNodeKind["assign"] = 0] = "assign"; + DVFGNodeKind[DVFGNodeKind["copy"] = 1] = "copy"; + DVFGNodeKind[DVFGNodeKind["write"] = 2] = "write"; + DVFGNodeKind[DVFGNodeKind["load"] = 3] = "load"; + DVFGNodeKind[DVFGNodeKind["addr"] = 4] = "addr"; + DVFGNodeKind[DVFGNodeKind["if"] = 5] = "if"; + DVFGNodeKind[DVFGNodeKind["actualParm"] = 6] = "actualParm"; + DVFGNodeKind[DVFGNodeKind["formalParm"] = 7] = "formalParm"; + DVFGNodeKind[DVFGNodeKind["actualRet"] = 8] = "actualRet"; + DVFGNodeKind[DVFGNodeKind["formalRet"] = 9] = "formalRet"; + DVFGNodeKind[DVFGNodeKind["unary"] = 10] = "unary"; + DVFGNodeKind[DVFGNodeKind["binary"] = 11] = "binary"; + DVFGNodeKind[DVFGNodeKind["normal"] = 12] = "normal"; +})(DVFGNodeKind || (DVFGNodeKind = {})); +class DVFGNode extends BaseNode { + stmt; + constructor(i, k, s) { + super(i, k); + this.stmt = s; + } + getDotLabel() { + let label = 'ID: ' + this.getID() + '\n'; + label = label + this.stmt.toString(); + return label; + } + getStmt() { + return this.stmt; + } +} +class DVFGEdge extends BaseEdge { +} + +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Represents the result of a data flow analysis. + * Contains the in and out sets for each node, as well as the corresponding data flow problem. + * + * @template Node - The type of nodes in the graph. + * @template V - The type of data flow values. + */ +class Solution { + in; + out; + problem; + constructor(i, out, problem) { + this.in = i; + this.out = out; + this.problem = problem; + } +} +/** + * A solver for data flow analysis problems. + * Implements forward and backward data flow analysis using a worklist algorithm. + * The solver computes the Maximum Fixed Point (MFP) solution, which is a safe + * over-approximation of the ideal Meet-Over-All-Paths (MOP) solution. + */ +class MFPDataFlowSolver { + /** + * Computes the MFP solution for a forward data flow analysis problem. + * + * @template Node - The type of nodes in the graph. + * @template V - The type of data flow values. + * @param problem - The data flow problem to solve. + * @returns The solution containing the in and out sets for all nodes. + */ + calculateMopSolutionForwards(problem) { + let _out = problem.initOut; + let _in = problem.initIn; + let workList = problem.flowGraph.nodesInPostOrder; + let newEntries = new Set(); + while (workList.length > 0) { + newEntries.clear(); + workList.forEach(n => { + let inSet; + const predecessors = problem.flowGraph.pred(n); + if (predecessors && predecessors.length > 0) { + const predecessorOuts = predecessors.map(pred => _out.get(pred)); + inSet = predecessorOuts.reduce((acc, cur) => problem.meet(acc, cur), problem.empty); + } + else { + inSet = problem.empty; + } + _in.set(n, inSet); + let old = _out.get(n); + let newSet = problem.transferFunction.apply(n, inSet); + if (!old || old.count() === 0 || !old.equals(newSet)) { + _out.set(n, newSet); + problem.flowGraph.succ(n).forEach(succ => newEntries.add(succ)); + } + }); + workList = [...newEntries]; + } + return new Solution(_in, _out, problem); + } + /** + * Computes the MFP solution for a backward data flow analysis problem. + * + * @template Node - The type of nodes in the graph. + * @template V - The type of data flow values. + * @param problem - The data flow problem to solve. + * @returns The solution containing the in and out sets for all nodes. + */ + calculateMopSolutionBackwards(problem) { + let _out = problem.initOut; + let _in = problem.initIn; + let workList = problem.flowGraph.nodesInPostOrder; + let newEntries = new Set(); + while (workList.length > 0) { + newEntries.clear(); + workList.forEach(n => { + let outSet = problem.flowGraph.succ(n).reduce((acc, curr) => { + return problem.meet(acc, _in.get(curr)); + }, problem.empty); + _out.set(n, outSet); + let old = _in.get(n); + let newSet = problem.transferFunction.apply(n, outSet); + if (!old || !old.equals(newSet)) { + _in.set(n, newSet); + problem.flowGraph.pred(n).forEach(pred => newEntries.add(pred)); + } + }); + workList = [...newEntries]; + } + return new Solution(_in, _out, problem); + } +} + +/** + * BaseImplicitGraph is an abstract class that represents an implicit graph. + * An implicit graph is a graph representation where node and edge information is implicitly stored using maps. + * This class implements the GraphTraits interface and provides basic graph operations. + */ +class BaseImplicitGraph { + /** + * idToNodeMap is an optional map that maps node IDs (NodeID) to node objects (Node). + * If not initialized, calling related methods will throw an error. + */ + idToNodeMap; + /** + * nodeToIdMap is a map that maps node objects (Node) to node IDs (NodeID). + * This map must be initialized in the subclass. + */ + nodeToIdMap; + /** + * succMap is a map that stores the successors of each node. + * The key is a node ID (NodeID), and the value is an array of successor node IDs. + */ + succMap; + /** + * predMap is a map that stores the predecessors of each node. + * The key is a node ID (NodeID), and the value is an array of predecessor node IDs. + */ + predMap; + constructor() { } + /** + * Gets the number of nodes in the graph. + * @returns The number of nodes in the graph. + */ + getNodeNum() { + return this.nodeToIdMap.size; + } + /** + * Returns an iterator for all nodes in the graph. + * @returns An iterator for traversing all nodes in the graph. + */ + nodesItor() { + return this.nodeToIdMap.keys(); + } + /** + * Gets the node object corresponding to a given node ID. + * @param id The node ID. + * @returns The corresponding node object. + * @throws Throws an error if idToNodeMap is not initialized or if the node is not found. + */ + getNode(id) { + if (!this.idToNodeMap) { + throw new Error(`initialize this.idToNodeMap first`); + } + if (!this.idToNodeMap.has(id)) { + throw new Error(`Can find Node # ${id}`); + } + return this.idToNodeMap.get(id); + } + getNodeID(s) { + if (!this.nodeToIdMap.has(s)) { + throw new Error(`Can find Node # ${s}`); + } + return this.nodeToIdMap.get(s); + } + /** + * Checks whether the graph contains a specific node ID. + * @param id The node ID. + * @returns Returns true if the node ID exists in the graph; otherwise, returns false. + * @throws Throws an error if idToNodeMap is not initialized. + */ + hasNode(id) { + if (!this.idToNodeMap) { + throw new Error(`initialize this.idToNodeMap first`); + } + return this.idToNodeMap.has(id); + } + /** + * Gets the list of successor node IDs for a given node. + * @param id The node ID. + * @returns An array of successor node IDs. Returns an empty array if no successors are found. + */ + succ(id) { + return this.succMap.get(id) ?? []; + } + /** + * Gets the list of predecessor node IDs for a given node. + * @param id The node ID. + * @returns An array of predecessor node IDs. Returns an empty array if no predecessors are found. + */ + pred(id) { + return this.predMap.get(id) ?? []; + } + /** + * Gets the nodeToIdMap, which maps node objects to node IDs. + * @returns The nodeToIdMap. + */ + getNodeToIdMap() { + return this.nodeToIdMap; + } +} + +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Reaching Definitions Data Flow Analysis + * + * This module implements the Reaching Definitions data flow analysis algorithm. + * Reaching Definitions is a forward data flow analysis that determines, for each + * program point, the set of variable definitions (assignments) that may reach + * that point without being overwritten. + * + * Key Components: + * 1. **Transfer Function**: + * - Computes the out set for each node based on its in set. + * - Uses gen and kill sets to model the effects of assignments: + * - **gen**: The set of definitions generated by the current node. + * - **kill**: The set of definitions killed (overwritten) by the current node. + * + * 2. **Meet Operation**: + * - Combines data flow values from multiple paths (e.g., union for reaching definitions). + * - Ensures that the analysis is conservative (safe) by over-approximating the result. + * + * The analysis is forward, meaning it propagates information from predecessors to successors. + * + */ +let coCtor = SparseBitVector; +const BV_SIZE = 32; +class ReachingDefProblem { + flowGraph; + transferFunction; + meet; + initIn; + initOut; + forward; + empty = new coCtor(BV_SIZE); + constructor(method, forward = true) { + this.flowGraph = new ReachingDefFlowGraph(method); + this.transferFunction = new ReachingDefTransferFunction(this.flowGraph); + this.meet = (x, y) => { + let r = x.clone(); + r.unionWith(y); + return r; + }; + this.initIn = new Map(this.flowGraph.nodesInPostOrder.map(i => [i, new coCtor(BV_SIZE)])); + this.initOut = new Map(this.flowGraph.nodesInPostOrder.map(i => [i, new coCtor(BV_SIZE)])); + this.forward = forward; + } +} +/** + * Represents the control flow graph (CFG) for reaching definitions analysis. + * This class implements the FlowGraph interface and provides methods to retrieve + * successors and predecessors of nodes, as well as topological orderings of nodes. + */ +class ReachingDefFlowGraph extends BaseImplicitGraph { + nodesInPostOrder; + constructor(method) { + super(); + const cfg = method.getCfg(); + if (!cfg) { + throw new Error('CFG not found'); + } + const nodes = cfg.getStmts(); + this.nodeToIdMap = new Map(nodes.map((x, i) => [x, i])); + this.idToNodeMap = new Map(nodes.map((x, i) => [i, x])); + this.nodesInPostOrder = nodes.map((_, i) => i); + this.initSuccPred(nodes, cfg); + } + getGraphName() { + return 'Reaching Definition Flow Graph'; + } + dumpNodes() { + this.nodeToIdMap?.forEach((id, node) => console.log(id + ': ' + node.toString())); + } + initSuccPred(nodes, cfg) { + this.succMap = new Map(); + this.predMap = new Map(); + cfg.getBlocks().forEach(bb => { + let stmts = bb.getStmts(); + if (stmts.length === 0) { + return; + } + for (let i = 0; i < stmts.length - 1; i++) { + let c = this.nodeToIdMap.get(stmts[i]); + let n = this.nodeToIdMap.get(stmts[i + 1]); + if (c === undefined || n === undefined) { + continue; + } + this.succMap.set(c, [n]); + this.predMap.set(n, [c]); + } + let terminate = bb.getTail(); + if (!terminate) { + throw new Error('cfg has no terminal'); + } + let successors = bb.getSuccessors(); + // try...catch语句,catch所在的block在CFG表示里是没有前驱block的,需要在这里额外查找并将exceptionalSuccessorBlocks作为try块的后继块之一 + const exceptionalSuccessorBlocks = bb.getExceptionalSuccessorBlocks(); + if (exceptionalSuccessorBlocks !== undefined) { + successors.push(...exceptionalSuccessorBlocks); + } + successors.forEach(succBB => { + let head = succBB.getHead(); + if (!head) { + return; + } + let t = this.nodeToIdMap?.get(terminate); + let h = this.nodeToIdMap?.get(head); + if (t === undefined || h === undefined) { + return; + } + // Terminate's succ + let succ = this.succMap.get(t) ?? []; + succ.push(h); + this.succMap.set(t, succ); + // Head's pred + let pred = this.predMap.get(h) ?? []; + pred.push(t); + this.predMap.set(h, pred); + }); + }); + } +} +/** + * Represents the transfer function for reaching definitions analysis. + */ +class ReachingDefTransferFunction { + gen; + kill; + constructor(flowGraph) { + this.gen = new coCtor(BV_SIZE); + this.kill = new Map(); + this.initGenKill(flowGraph); + } + apply(n, x) { + const result = x.clone(); + if (this.gen.test(n)) { + result.set(n); + } + const killSet = this.kill.get(n); + if (killSet) { + for (const item of killSet) { + result.reset(item); + } + } + return result; + } + initGenKill(g) { + let genValue2Nodes = new Map(); + // Init Gen + g.getNodeToIdMap().forEach((id, node) => { + if (node instanceof ArkAssignStmt) { + let lop = node.getLeftOp(); + let genNodes = genValue2Nodes.get(lop) ?? new coCtor(BV_SIZE); + genNodes.set(id); + genValue2Nodes.set(lop, genNodes); + this.gen.set(id); + } + }); + // Init Kill + genValue2Nodes.forEach((defNodes, v) => { + for (const i of defNodes) { + const killSet = defNodes.clone(); + killSet.reset(i); + this.kill.set(i, killSet); + } + }); + } +} + +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class DVFGBuilder { + dvfg; + scene; + constructor(dvfg, s) { + this.dvfg = dvfg; + this.scene = s; + } + build() { + this.scene.getMethods().forEach(m => { + if (m.getCfg()) { + this.buildForSingleMethod(m); + } + }); + } + buildForSingleMethod(m) { + let problem = new ReachingDefProblem(m); + let solver = new MFPDataFlowSolver(); + let solution = solver.calculateMopSolutionForwards(problem); + let defMap = new Map(); + m.getCfg() + .getStmts() + .forEach(s => { + let def = s.getDef(); + if (def != null) { + if (def instanceof AbstractFieldRef) { + def = def.getFieldSignature(); + } + let defStmts = defMap.get(def) ?? new Set(); + defStmts.add(s); + defMap.set(def, defStmts); + } + }); + solution.in.forEach((defs, reach) => { + let addNewNodes = (defId, def, reach) => { + if (defs.test(defId)) { + let srcNode = this.dvfg.getOrNewDVFGNode(def); + let dstNode = this.dvfg.getOrNewDVFGNode(reach); + this.dvfg.addDVFGEdge(srcNode, dstNode); + } + }; + const reachStmt = problem.flowGraph.getNode(reach); + this.getStmtUsedValues(reachStmt).forEach(use => { + let target = use; + if (target instanceof AbstractFieldRef) { + target = target.getFieldSignature(); + } + defMap.get(target)?.forEach(defStmt => { + let defId = problem.flowGraph.getNodeID(defStmt); + addNewNodes(defId, defStmt, reachStmt); + }); + }); + }); + } + getStmtUsedValues(stmt) { + if (stmt instanceof ArkAssignStmt) { + return this.getUsedValues(stmt.getRightOp()); + } + else if (stmt instanceof ArkInvokeStmt) { + return this.getUsedValues(stmt.getInvokeExpr()); + } + else if (stmt instanceof ArkIfStmt) { + return this.getUsedValues(stmt.getConditionExpr()); + } + else if (stmt instanceof ArkReturnStmt) { + return this.getUsedValues(stmt.getOp()); + } + else if (stmt instanceof ArkThrowStmt) { + return this.getUsedValues(stmt.getOp()); + } + else if (stmt instanceof ArkReturnVoidStmt || stmt instanceof ArkAliasTypeDefineStmt || stmt instanceof DummyStmt) { + return []; + } + else { + throw new Error('unsupported stmt'); + } + } + getUsedValues(val) { + if (val instanceof AbstractExpr) { + if (val instanceof AbstractInvokeExpr) { + return val.getArgs().flatMap(current => { + return this.getUsedValues(current); + }, []); + } + else { + return val.getUses().flatMap(current => { + return this.getUsedValues(current); + }, []); + } + } + if (val instanceof Constant) { + return []; + } + return [val]; + } + getOrNewDVFGNode(stmt) { + return this.dvfg.getOrNewDVFGNode(stmt); + } + addDVFGNodes() { } + addDVFGEdges() { } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$8 = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'VisibleValue'); +class VisibleValue { + scopeChain; // 不包含currScope + currScope; + currVisibleValues; + constructor() { + // TODO:填充全局变量 + this.currScope = new Scope([], 0); + this.scopeChain = [this.currScope]; + this.currVisibleValues = [...this.currScope.values]; + } + /** get values that is visible in curr scope */ + getCurrVisibleValues() { + return this.currVisibleValues; + } + getScopeChain() { + return this.scopeChain; + } + /** udpate visible values after entered a scope, only support step by step */ + updateIntoScope(model) { + let name = ''; + if (model instanceof BasicBlock) { + name = 'block: ' + model.toString(); + } + else { + name = model.getName(); + } + logger$8.info('---- into scope:{', name, '}'); + // get values in this scope + let values = []; + if (model instanceof ArkFile || model instanceof ArkNamespace) { + values = this.getVisibleValuesIntoFileOrNameSpace(model); + } + else if (model instanceof ArkClass) { + values = this.getVisibleValuesIntoClass(model); + } + else if (model instanceof ArkMethod) { + values = this.getVisibleValuesIntoMethod(model); + } + else if (model instanceof BasicBlock) { + values = this.getVisibleValuesIntoBasicBlock(model); + } + // handle scope chain + const targetDepth = this.getTargetDepth(model); + this.addScope(values, targetDepth, model); + } + /** udpate visible values after left a scope, only support step by step */ + updateOutScope() { + const currModel = this.currScope.arkModel; + let name = ''; + if (currModel instanceof BasicBlock) { + name = 'block: ' + currModel.toString(); + } + else { + name = currModel.getName(); + } + logger$8.info('---- out scope:{', name, '}'); + let targetDepth = this.currScope.depth; + if (currModel instanceof BasicBlock) { + const successorsCnt = currModel.getSuccessors().length; + // if successorsCnt <= 0, unchange + if (successorsCnt > 1) { + targetDepth += 1; // goto inner scope + } + } + this.deleteScope(targetDepth); + } + /** clear up previous scope */ + deleteScope(targetDepth) { + const prevDepth = this.currScope.depth; + if (targetDepth > prevDepth) { + return; + } + let popScopeValuesCnt = 0; + let popScopeCnt = 0; + for (let i = this.scopeChain.length - 1; i >= 0; i--) { + if (this.scopeChain[i].depth < targetDepth) { + break; + } + popScopeCnt += 1; + popScopeValuesCnt += this.scopeChain[i].values.length; + } + this.scopeChain.splice(this.scopeChain.length - popScopeCnt, popScopeCnt)[0]; // popScopeCnt >= 1 + this.currScope = this.scopeChain[this.scopeChain.length - 1]; + const totalValuesCnt = this.currVisibleValues.length; + this.currVisibleValues.splice(totalValuesCnt - popScopeValuesCnt, popScopeValuesCnt); + } + /** add this scope to scope chain and update visible values */ + addScope(values, targetDepth, model) { + const newScope = new Scope(values, targetDepth, model); + this.currScope = newScope; + this.scopeChain.push(this.currScope); + this.currVisibleValues.push(...this.currScope.values); + } + // TODO:构造嵌套关系树 + getTargetDepth(model) { + const prevDepth = this.currScope.depth; + const prevModel = this.currScope.arkModel; + let targetDepth = prevDepth + 1; + if (model instanceof BasicBlock) { + const predecessorsCnt = model.getPredecessors().length; + if (predecessorsCnt <= 1) { + targetDepth = prevDepth + 1; + } + else { + targetDepth = prevDepth; + } + } + else if (model instanceof ArkFile && prevModel instanceof ArkFile) { + targetDepth = prevDepth; + } + else if (model instanceof ArkNamespace && prevModel instanceof ArkNamespace) { + targetDepth = prevDepth; + } + else if (model instanceof ArkClass && prevModel instanceof ArkClass) { + targetDepth = prevDepth; + } + else if (model instanceof ArkMethod && prevModel instanceof ArkMethod) { + targetDepth = prevDepth; + } + return targetDepth; + } + getVisibleValuesIntoFileOrNameSpace(fileOrNameSpace) { + let values = []; + return values; + } + getVisibleValuesIntoClass(cls) { + const values = []; + const fields = cls.getFields(); + const classSignature = cls.getSignature(); + for (const field of fields) { + if (field.isStatic()) { + const staticFieldRef = new ArkStaticFieldRef(field.getSignature()); + values.push(staticFieldRef); + } + else { + const instanceFieldRef = new ArkInstanceFieldRef(new Local('this', new ClassType(classSignature)), field.getSignature()); + values.push(instanceFieldRef); + } + } + return values; + } + getVisibleValuesIntoMethod(method) { + let visibleValues = []; + return visibleValues; + } + getVisibleValuesIntoBasicBlock(basiceBlock) { + const visibleValues = []; + for (const stmt of basiceBlock.getStmts()) { + if (stmt instanceof ArkAssignStmt) { + visibleValues.push(stmt.getLeftOp()); + } + } + return visibleValues; + } +} +class Scope { + values; + depth; + arkModel; + constructor(values, depth = -1, arkModel = null) { + this.values = values; + this.depth = depth; + this.arkModel = arkModel; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class DataflowProblem { +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class DataflowResult { + stmt2InFacts = new Map(); + stmt2OutFacts = new Map(); + //should we specifically keep global facts or just embedding them into the two maps above + globalFacts = new Set(); +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class PathEdgePoint { + node; + fact; + constructor(node, fact) { + this.node = node; + this.fact = fact; + } +} +class PathEdge { + edgeStart; + edgeEnd; + constructor(start, end) { + this.edgeStart = start; + this.edgeEnd = end; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +function getRecallMethodInParam(stmt) { + for (const param of stmt.getInvokeExpr().getArgs()) { + if (param.getType() instanceof FunctionType) { + const methodSignature = param.getType().getMethodSignature(); + const method = stmt.getCfg()?.getDeclaringMethod().getDeclaringArkClass().getMethod(methodSignature); + if (method) { + return method; + } + } + } + return null; +} +function LocalEqual(local1, local2) { + if (local1.getName() === 'this' && local2.getName() === 'this') { + return true; + } + const method1 = local1.getDeclaringStmt()?.getCfg()?.getDeclaringMethod(); + const method2 = local2.getDeclaringStmt()?.getCfg()?.getDeclaringMethod(); + const nameEqual = local1.getName() === local2.getName(); + return method1 === method2 && nameEqual; +} +function RefEqual(ref1, ref2) { + if (ref1 instanceof ArkStaticFieldRef && ref2 instanceof ArkStaticFieldRef) { + return ref1.getFieldSignature().toString() === ref2.getFieldSignature().toString(); + } + else if (ref1 instanceof ArkInstanceFieldRef && ref2 instanceof ArkInstanceFieldRef) { + return LocalEqual(ref1.getBase(), ref2.getBase()) && ref1.getFieldSignature().toString() === ref2.getFieldSignature().toString(); + } + return false; +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class DataflowSolver { + problem; + workList; + pathEdgeSet; + zeroFact; + inComing; + endSummary; + summaryEdge; // summaryEdge不是加速一个函数内多次调用同一个函数,而是加速多次调用同一个函数f时,f内的函数调用 + scene; + CHA; + stmtNexts; + laterEdges = new Set(); + constructor(problem, scene) { + this.problem = problem; + this.scene = scene; + scene.inferTypes(); + this.zeroFact = problem.createZeroValue(); + this.workList = new Array(); + this.pathEdgeSet = new Set(); + this.inComing = new Map(); + this.endSummary = new Map(); + this.summaryEdge = new Set(); + this.stmtNexts = new Map(); + } + solve() { + this.init(); + this.doSolve(); + } + computeResult(stmt, d) { + for (let pathEdge of this.pathEdgeSet) { + if (pathEdge.edgeEnd.node === stmt && pathEdge.edgeEnd.fact === d) { + return true; + } + } + return false; + } + getChildren(stmt) { + return Array.from(this.stmtNexts.get(stmt) || []); + } + init() { + let edgePoint = new PathEdgePoint(this.problem.getEntryPoint(), this.zeroFact); + let edge = new PathEdge(edgePoint, edgePoint); + this.workList.push(edge); + this.pathEdgeSet.add(edge); + // build CHA + let cg = new CallGraph(this.scene); + this.CHA = new ClassHierarchyAnalysis(this.scene, cg, new CallGraphBuilder(cg, this.scene)); + this.buildStmtMapInClass(); + this.setCfg4AllStmt(); + return; + } + buildStmtMapInClass() { + const methods = this.scene.getMethods(); + methods.push(this.problem.getEntryMethod()); + for (const method of methods) { + const cfg = method.getCfg(); + const blocks = []; + if (cfg) { + blocks.push(...cfg.getBlocks()); + } + for (const block of blocks) { + this.buildStmtMapInBlock(block); + } + } + } + buildStmtMapInBlock(block) { + const stmts = block.getStmts(); + for (let stmtIndex = 0; stmtIndex < stmts.length; stmtIndex++) { + const stmt = stmts[stmtIndex]; + if (stmtIndex !== stmts.length - 1) { + this.stmtNexts.set(stmt, new Set([stmts[stmtIndex + 1]])); + } + else { + const set = new Set(); + for (const successor of block.getSuccessors()) { + set.add(successor.getStmts()[0]); + } + this.stmtNexts.set(stmt, set); + } + } + } + setCfg4AllStmt() { + for (const cls of this.scene.getClasses()) { + for (const mtd of cls.getMethods(true)) { + addCfg2Stmt(mtd); + } + } + } + getAllCalleeMethods(callNode) { + const callSites = this.CHA.resolveCall(this.CHA.getCallGraph().getCallGraphNodeByMethod(this.problem.getEntryMethod().getSignature()).getID(), callNode); + const methods = new Set(); + for (const callSite of callSites) { + const method = this.scene.getMethod(this.CHA.getCallGraph().getMethodByFuncID(callSite.calleeFuncID)); + if (method) { + methods.add(method); + } + } + return methods; + } + getReturnSiteOfCall(call) { + return [...this.stmtNexts.get(call)][0]; + } + getStartOfCallerMethod(call) { + const cfg = call.getCfg(); + const paraNum = cfg.getDeclaringMethod().getParameters().length; + return [...cfg.getBlocks()][0].getStmts()[paraNum]; + } + pathEdgeSetHasEdge(edge) { + for (const path of this.pathEdgeSet) { + this.problem.factEqual(path.edgeEnd.fact, edge.edgeEnd.fact); + if (path.edgeEnd.node === edge.edgeEnd.node && + this.problem.factEqual(path.edgeEnd.fact, edge.edgeEnd.fact) && + path.edgeStart.node === edge.edgeStart.node && + this.problem.factEqual(path.edgeStart.fact, edge.edgeStart.fact)) { + return true; + } + } + return false; + } + propagate(edge) { + if (!this.pathEdgeSetHasEdge(edge)) { + let index = this.workList.length; + for (let i = 0; i < this.workList.length; i++) { + if (this.laterEdges.has(this.workList[i])) { + index = i; + break; + } + } + this.workList.splice(index, 0, edge); + this.pathEdgeSet.add(edge); + } + } + processExitNode(edge) { + let startEdgePoint = edge.edgeStart; + let exitEdgePoint = edge.edgeEnd; + const summary = this.endSummary.get(startEdgePoint); + if (summary === undefined) { + this.endSummary.set(startEdgePoint, new Set([exitEdgePoint])); + } + else { + summary.add(exitEdgePoint); + } + const callEdgePoints = this.inComing.get(startEdgePoint); + if (callEdgePoints === undefined) { + if (startEdgePoint.node.getCfg().getDeclaringMethod() === this.problem.getEntryMethod()) { + return; + } + throw new Error('incoming does not have ' + startEdgePoint.node.getCfg()?.getDeclaringMethod().toString()); + } + for (let callEdgePoint of callEdgePoints) { + let returnSite = this.getReturnSiteOfCall(callEdgePoint.node); + let returnFlowFunc = this.problem.getExitToReturnFlowFunction(exitEdgePoint.node, returnSite, callEdgePoint.node); + this.handleFacts(returnFlowFunc, returnSite, exitEdgePoint, callEdgePoint); + } + } + handleFacts(returnFlowFunc, returnSite, exitEdgePoint, callEdgePoint) { + for (let fact of returnFlowFunc.getDataFacts(exitEdgePoint.fact)) { + let returnSitePoint = new PathEdgePoint(returnSite, fact); + let cacheEdge = new PathEdge(callEdgePoint, returnSitePoint); + let summaryEdgeHasCacheEdge = false; + for (const sEdge of this.summaryEdge) { + if (sEdge.edgeStart === callEdgePoint && sEdge.edgeEnd.node === returnSite && sEdge.edgeEnd.fact === fact) { + summaryEdgeHasCacheEdge = true; + break; + } + } + if (summaryEdgeHasCacheEdge) { + continue; + } + this.summaryEdge.add(cacheEdge); + let startOfCaller = this.getStartOfCallerMethod(callEdgePoint.node); + for (let pathEdge of this.pathEdgeSet) { + if (pathEdge.edgeStart.node === startOfCaller && pathEdge.edgeEnd === callEdgePoint) { + this.propagate(new PathEdge(pathEdge.edgeStart, returnSitePoint)); + } + } + } + } + processNormalNode(edge) { + let start = edge.edgeStart; + let end = edge.edgeEnd; + let stmts = [...this.getChildren(end.node)].reverse(); + for (let stmt of stmts) { + let flowFunction = this.problem.getNormalFlowFunction(end.node, stmt); + let set = flowFunction.getDataFacts(end.fact); + for (let fact of set) { + let edgePoint = new PathEdgePoint(stmt, fact); + const edge = new PathEdge(start, edgePoint); + this.propagate(edge); + this.laterEdges.add(edge); + } + } + } + processCallNode(edge) { + let start = edge.edgeStart; + let callEdgePoint = edge.edgeEnd; + const invokeStmt = callEdgePoint.node; + let callees; + if (this.scene.getFile(invokeStmt.getInvokeExpr().getMethodSignature().getDeclaringClassSignature().getDeclaringFileSignature())) { + callees = this.getAllCalleeMethods(callEdgePoint.node); + } + else { + callees = new Set([getRecallMethodInParam(invokeStmt)]); + } + let returnSite = this.getReturnSiteOfCall(callEdgePoint.node); + for (let callee of callees) { + let callFlowFunc = this.problem.getCallFlowFunction(invokeStmt, callee); + if (!callee.getCfg()) { + continue; + } + let firstStmt = [...callee.getCfg().getBlocks()][0].getStmts()[callee.getParameters().length]; + let facts = callFlowFunc.getDataFacts(callEdgePoint.fact); + for (let fact of facts) { + this.callNodeFactPropagate(edge, firstStmt, fact, returnSite); + } + } + let callToReturnflowFunc = this.problem.getCallToReturnFlowFunction(edge.edgeEnd.node, returnSite); + let set = callToReturnflowFunc.getDataFacts(callEdgePoint.fact); + for (let fact of set) { + this.propagate(new PathEdge(start, new PathEdgePoint(returnSite, fact))); + } + for (let cacheEdge of this.summaryEdge) { + if (cacheEdge.edgeStart === edge.edgeEnd && cacheEdge.edgeEnd.node === returnSite) { + this.propagate(new PathEdge(start, cacheEdge.edgeEnd)); + } + } + } + callNodeFactPropagate(edge, firstStmt, fact, returnSite) { + let callEdgePoint = edge.edgeEnd; + // method start loop path edge + let startEdgePoint = new PathEdgePoint(firstStmt, fact); + this.propagate(new PathEdge(startEdgePoint, startEdgePoint)); + //add callEdgePoint in inComing.get(startEdgePoint) + let coming; + for (const incoming of this.inComing.keys()) { + if (incoming.fact === startEdgePoint.fact && incoming.node === startEdgePoint.node) { + coming = this.inComing.get(incoming); + break; + } + } + if (coming === undefined) { + this.inComing.set(startEdgePoint, new Set([callEdgePoint])); + } + else { + coming.add(callEdgePoint); + } + let exitEdgePoints = new Set(); + for (const end of Array.from(this.endSummary.keys())) { + if (end.fact === fact && end.node === firstStmt) { + exitEdgePoints = this.endSummary.get(end); + } + } + for (let exitEdgePoint of exitEdgePoints) { + let returnFlowFunc = this.problem.getExitToReturnFlowFunction(exitEdgePoint.node, returnSite, callEdgePoint.node); + for (let returnFact of returnFlowFunc.getDataFacts(exitEdgePoint.fact)) { + this.summaryEdge.add(new PathEdge(edge.edgeEnd, new PathEdgePoint(returnSite, returnFact))); + } + } + } + doSolve() { + while (this.workList.length !== 0) { + let pathEdge = this.workList.shift(); + if (this.laterEdges.has(pathEdge)) { + this.laterEdges.delete(pathEdge); + } + let targetStmt = pathEdge.edgeEnd.node; + if (this.isCallStatement(targetStmt)) { + this.processCallNode(pathEdge); + } + else if (this.isExitStatement(targetStmt)) { + this.processExitNode(pathEdge); + } + else { + this.processNormalNode(pathEdge); + } + } + } + isCallStatement(stmt) { + for (const expr of stmt.getExprs()) { + if (expr instanceof AbstractInvokeExpr) { + if (this.scene.getFile(expr.getMethodSignature().getDeclaringClassSignature().getDeclaringFileSignature())) { + return true; + } + if (stmt instanceof ArkInvokeStmt && getRecallMethodInParam(stmt)) { + return true; + } + } + } + return false; + } + isExitStatement(stmt) { + return stmt instanceof ArkReturnStmt || stmt instanceof ArkReturnVoidStmt; + } + getPathEdgeSet() { + return this.pathEdgeSet; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class Fact { + values = new Set(); + valueMap = new Map(); // 用最近的def代表value的值 +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$7 = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'Scene'); +class UndefinedVariableChecker extends DataflowProblem { + zeroValue = new Constant('undefined', UndefinedType.getInstance()); + entryPoint; + entryMethod; + scene; + classMap; + globalVariableMap; + outcomes = []; + constructor(stmt, method) { + super(); + this.entryPoint = stmt; + this.entryMethod = method; + this.scene = method.getDeclaringArkFile().getScene(); + this.classMap = this.scene.getClassMap(); + this.globalVariableMap = this.scene.getGlobalVariableMap(); + } + getEntryPoint() { + return this.entryPoint; + } + getEntryMethod() { + return this.entryMethod; + } + isUndefined(val) { + if (val instanceof Constant) { + let constant = val; + if (constant.getType() instanceof UndefinedType) { + return true; + } + } + return false; + } + getNormalFlowFunction(srcStmt, tgtStmt) { + let checkerInstance = this; + return new (class { + getDataFacts(dataFact) { + let ret = new Set(); + if (checkerInstance.getEntryPoint() === srcStmt && checkerInstance.getZeroValue() === dataFact) { + ret.add(checkerInstance.getZeroValue()); + return ret; + } + if (srcStmt instanceof ArkAssignStmt) { + checkerInstance.insideNormalFlowFunction(ret, srcStmt, dataFact); + } + return ret; + } + })(); + } + insideNormalFlowFunction(ret, srcStmt, dataFact) { + if (!this.factEqual(srcStmt.getDef(), dataFact)) { + if (!(dataFact instanceof Local && dataFact.getName() === srcStmt.getDef().toString())) { + ret.add(dataFact); + } + } + let ass = srcStmt; + let assigned = ass.getLeftOp(); + let rightOp = ass.getRightOp(); + if (this.getZeroValue() === dataFact) { + if (this.isUndefined(rightOp)) { + ret.add(assigned); + } + } + else if (this.factEqual(rightOp, dataFact) || rightOp.getType() instanceof UndefinedType) { + ret.add(assigned); + } + else if (rightOp instanceof ArkInstanceFieldRef) { + const base = rightOp.getBase(); + if (base === dataFact || (!base.getDeclaringStmt() && base.getName() === dataFact.toString())) { + this.outcomes.push(new Outcome(rightOp, ass)); + logger$7.info('undefined base'); + logger$7.info(srcStmt.toString()); + logger$7.info(srcStmt.getOriginPositionInfo().toString()); + } + } + else if (dataFact instanceof ArkInstanceFieldRef && rightOp === dataFact.getBase()) { + const field = new ArkInstanceFieldRef(srcStmt.getLeftOp(), dataFact.getFieldSignature()); + ret.add(field); + } + } + getCallFlowFunction(srcStmt, method) { + let checkerInstance = this; + return new (class { + getDataFacts(dataFact) { + const ret = new Set(); + if (checkerInstance.getZeroValue() === dataFact) { + checkerInstance.insideCallFlowFunction(ret, method); + } + else { + const callExpr = srcStmt.getExprs()[0]; + if (callExpr instanceof ArkInstanceInvokeExpr && + dataFact instanceof ArkInstanceFieldRef && + callExpr.getBase().getName() === dataFact.getBase().getName()) { + // todo:base转this + const thisRef = new ArkInstanceFieldRef(new Local('this', new ClassType(method.getDeclaringArkClass().getSignature())), dataFact.getFieldSignature()); + ret.add(thisRef); + } + else if (callExpr instanceof ArkStaticInvokeExpr && + dataFact instanceof ArkStaticFieldRef && + callExpr.getMethodSignature().getDeclaringClassSignature() === dataFact.getFieldSignature().getDeclaringSignature()) { + ret.add(dataFact); + } + } + checkerInstance.addParameters(srcStmt, dataFact, method, ret); + return ret; + } + })(); + } + insideCallFlowFunction(ret, method) { + ret.add(this.getZeroValue()); + // 加上调用函数能访问到的所有静态变量,如果不考虑多线程,加上所有变量,考虑则要统计之前已经处理过的变量并排除 + for (const field of method.getDeclaringArkClass().getStaticFields(this.classMap)) { + if (field.getInitializer() === undefined) { + ret.add(new ArkStaticFieldRef(field.getSignature())); + } + } + for (const local of method.getDeclaringArkClass().getGlobalVariable(this.globalVariableMap)) { + ret.add(local); + } + // 加上所有未定义初始值的属性 + if (method.getName() === INSTANCE_INIT_METHOD_NAME || method.getName() === STATIC_INIT_METHOD_NAME) { + for (const field of method.getDeclaringArkClass().getFields()) { + this.addUndefinedField(field, method, ret); + } + } + } + addUndefinedField(field, method, ret) { + let defined = false; + for (const stmt of method.getCfg().getStmts()) { + const def = stmt.getDef(); + if (def instanceof ArkInstanceFieldRef && def.getFieldSignature() === field.getSignature()) { + defined = true; + break; + } + } + if (!defined) { + const fieldRef = new ArkInstanceFieldRef(new Local('this', new ClassType(method.getDeclaringArkClass().getSignature())), field.getSignature()); + ret.add(fieldRef); + } + } + addParameters(srcStmt, dataFact, method, ret) { + const callStmt = srcStmt; + const args = callStmt.getInvokeExpr().getArgs(); + for (let i = 0; i < args.length; i++) { + if (args[i] === dataFact || (this.isUndefined(args[i]) && this.getZeroValue() === dataFact)) { + const realParameter = [...method.getCfg().getBlocks()][0].getStmts()[i].getDef(); + if (realParameter) { + ret.add(realParameter); + } + } + else if (dataFact instanceof ArkInstanceFieldRef && dataFact.getBase().getName() === args[i].toString()) { + const realParameter = [...method.getCfg().getBlocks()][0].getStmts()[i].getDef(); + if (realParameter) { + const retRef = new ArkInstanceFieldRef(realParameter, dataFact.getFieldSignature()); + ret.add(retRef); + } + } + } + } + getExitToReturnFlowFunction(srcStmt, tgtStmt, callStmt) { + let checkerInstance = this; + return new (class { + getDataFacts(dataFact) { + let ret = new Set(); + if (dataFact === checkerInstance.getZeroValue()) { + ret.add(checkerInstance.getZeroValue()); + } + return ret; + } + })(); + } + getCallToReturnFlowFunction(srcStmt, tgtStmt) { + let checkerInstance = this; + return new (class { + getDataFacts(dataFact) { + const ret = new Set(); + if (checkerInstance.getZeroValue() === dataFact) { + ret.add(checkerInstance.getZeroValue()); + } + const defValue = srcStmt.getDef(); + if (!(defValue && defValue === dataFact)) { + ret.add(dataFact); + } + return ret; + } + })(); + } + createZeroValue() { + return this.zeroValue; + } + getZeroValue() { + return this.zeroValue; + } + factEqual(d1, d2) { + if (d1 instanceof Constant && d2 instanceof Constant) { + return d1 === d2; + } + else if (d1 instanceof Local && d2 instanceof Local) { + return LocalEqual(d1, d2); + } + else if (d1 instanceof AbstractRef && d2 instanceof AbstractRef) { + return RefEqual(d1, d2); + } + return false; + } + getOutcomes() { + return this.outcomes; + } +} +class UndefinedVariableSolver extends DataflowSolver { + constructor(problem, scene) { + super(problem, scene); + } +} +class Outcome { + value; + stmt; + constructor(v, s) { + this.value = v; + this.stmt = s; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const DOT_FILE_HEADER$1 = `digraph G { + graph [nodesep=0.1] + node [shape=box] + edge [arrowhead=vee] +`; +class ArkUIViewTreePrinter extends Printer { + viewTree; + dupCnt; + constructor(viewTree) { + super(); + this.viewTree = viewTree; + this.dupCnt = 0; + } + dump() { + this.printer.clear(); + let root = this.viewTree.getRoot(); + if (!root) { + return this.printer.toString(); + } + this.printer.write(DOT_FILE_HEADER$1); + this.walk(root, root.parent); + this.printer.write('}'); + return this.printer.toString(); + } + walk(item, parent, map = new Map()) { + let skipChildren = this.writeNode(item, parent, map); + if (skipChildren) { + return; + } + for (const child of item.children) { + this.walk(child, item, map); + } + } + escapeDotLabel(content) { + const MAX_LABEL_LEN = 64; + const PRE_FIX_LEN = 5; + let label = content.join('|'); + if (label.length > MAX_LABEL_LEN) { + return label.substring(0, PRE_FIX_LEN) + '...' + label.substring(label.length - MAX_LABEL_LEN + PRE_FIX_LEN); + } + return label; + } + writeNode(item, parent, map) { + let id = `Node${map.size}`; + let hasSameNode = map.has(item) || map.has(item.signature); + // 根据 handler.type 设置节点颜色 + const nodeColor = item.handlers && item.handlers.length > 0 ? "orange" : "white"; // 根据 handler 类型设置颜色 + // 设置填充颜色和边框颜色,并确保框线可见 + if (hasSameNode) { + id = `${id}_${this.dupCnt++}`; + if (nodeColor == "orange") { + this.printer.write(` ${id} [label="${item.name}" style=filled color="orange"]\n`); + } + else { + this.printer.write(` ${id} [label="${item.name}" style=filled color="green"]\n`); + } + } + else { + if (nodeColor == "orange") { + this.printer.write(` ${id} [label="${item.name}" style=filled color="orange"]\n`); + } + else { + this.printer.write(` ${id} [label="${item.name}"]\n`); + } + } + // if(nodeColor == "orange"){ + // console.log("orange node: ",item); + // } + // if (hasSameNode) { + // id = `${id}_${this.dupCnt++}`; + // this.printer.write(` ${id} [label="${item.name}" style=filled color="green"]\n`); + // } else { + // this.printer.write(` ${id} [label="${item.name}"]\n`); + // } + if (parent) { + this.printer.write(` ${map.get(parent)} -> ${id}\n`); + } + this.writeNodeStateValues(item, id); + this.writeNodeAttributes(item, id); + this.writeNodeSignature(item, id); + this.writeNodeExtraProperties(item, id); + this.writeNodeXpathProperties(item, id); + if (map.get(item)) { + this.printer.write(` {rank="same"; ${id};${map.get(item)};}\n`); + this.printer.write(` ${id} -> ${map.get(item)}[style=dotted]\n`); + return true; + } + else if (map.get(item.signature)) { + this.printer.write(` {rank="same"; ${id};${map.get(item.signature)};}\n`); + this.printer.write(` ${id} -> ${map.get(item.signature)}[style=dotted]\n`); + return true; + } + map.set(item, id); + if (item.signature && !map.has(item.signature)) { + map.set(item.signature, id); + } + return false; + } + writeNodeStateValues(item, id) { + if (item.stateValues.size > 0) { + let stateValuesId = `${id}val`; + let content = []; + item.stateValues.forEach(value => { + content.push(value.getName()); + }); + this.printer.write(` ${stateValuesId} [shape=ellipse label="StateValues\n ${this.escapeDotLabel(content)}" fontsize=10 height=.1 style=filled color=".7 .3 1.0" ]\n`); + this.printer.write(` ${id} -> ${stateValuesId}\n`); + } + } + writeNodeAttributes(item, id) { + if (item.attributes.size > 0) { + let attributesId = `${id}attributes`; + let content = []; + for (const [key, _] of item.attributes) { + if (key !== COMPONENT_POP_FUNCTION) { + content.push(key); + } + } + if (content.length > 0) { + this.printer.write(` ${attributesId} [shape=ellipse label="property|Event\n${this.escapeDotLabel(content)}" fontsize=10 height=.1 style=filled color=".7 .3 1.0" ]\n`); + this.printer.write(` ${id} -> ${attributesId}\n`); + } + } + } + writeNodeSignature(item, id) { + if (item.signature) { + let signatureId = `${id}signature`; + let content = [item.signature.toString()]; + this.printer.write(` ${signatureId} [shape=ellipse label="signature\n${this.escapeDotLabel(content)}" fontsize=10 height=.1 style=filled color=".7 .3 1.0" ]\n`); + this.printer.write(` ${id} -> ${signatureId}\n`); + } + } + writeNodeExtraProperties(item, id) { + const extra = []; + // if ((item as any).xpath) { + // extra.push(`xpath: ${(item as any).xpath}`); + // } + if (item.text_content) { + extra.push(`text: ${item.text_content}`); + } + if (extra.length > 0) { + const extraId = `${id}extra`; + this.printer.write(` ${extraId} [shape=ellipse label="extra\\n${this.escapeDotLabel(extra)}" fontsize=10 height=.1 style=filled color=".7 .3 1.0"]\n`); + this.printer.write(` ${id} -> ${extraId}\n`); + } + } + writeNodeXpathProperties(item, id) { + const extra = []; + // if ((item as any).xpath) { + // extra.push(`xpath: ${(item as any).xpath}`); + // } + if (item.xpath) { + extra.push(`xpath: ${item.xpath}`); + } + if (extra.length > 0) { + const extraId = `${id}extra`; + this.printer.write(` ${extraId} [shape=ellipse label="extra\\n${this.escapeDotLabel(extra)}" fontsize=10 height=.1 style=filled color=".7 .3 1.0"]\n`); + this.printer.write(` ${id} -> ${extraId}\n`); + } + } +} + +const fs = require('fs'); +const path = require('path'); +class UIFuncGraphBuilder { + graph; + scene; + constructor(graph, s) { + this.graph = graph; + this.scene = s; + } + // 构建UI功能图 + // public buildUiFuncGraph(pages: { pageId: PageID, file: ArkFile}[], uiabilities: { from: PageID, to: PageID, uiabilityId: UiabilityId }[]): void { + // // 1. 添加页面节点 + // pages.forEach(({ pageId, file }) => { + // this.graph.addPage(pageId, file); // 传递file信息 + // }); + // // 2. 添加跳转关系 + // uiabilities.forEach(({ from, to, uiabilityId }) => { + // this.graph.addUiability(from, to, uiabilityId); + // }); + // // 3. 设置入口点 + // this.setEntries(); + // } + // 设置图的入口点,入口点是没有入边的节点 + //构建UI功能图 + InitNode() { + // 1. 添加页面节点 后续改成 page 的 ,目前是 file + let cnt = 1; + this.scene.getClasses().forEach(classes => { + //console.log("decorator: ",classes.getDecorators()); + if (classes.hasDecorator("Entry") || classes.getSuperClassName() == "UIAbility") { + this.graph.addClass(cnt, classes); + cnt++; + } + }); + if (GlobalOverlayTree.length > 0) { + for (let idx = 0; idx < GlobalOverlayTree.length; idx++) { + const overlayNode = GlobalOverlayTree[idx]; + console.log("==== Add Dialog : ", idx); + this.graph.addDialogNode(cnt, overlayNode); + index_2_pageId.set(idx, cnt); // 如需设置 pageId,可以在这里补充 + cnt++; + } + } + else { + console.log("==== No Global Overlay Tree ===="); + } + // 3. 设置入口点 + this.setEntries(); + this.graph.buildRouterMapFromNav(); + } + setEntries() { + const nodesIter = this.graph.getNodesIter(); + const entries = Array.from(nodesIter) + .filter(node => !node.hasIncomingEdges()) // 没有入边的节点 + .map(node => node.getID()); + this.graph.setEntries(entries); + } + buildUiFuncGraph() { + for (const arkfile of this.scene.getFiles()) { + for (const arkclass of ModelUtils.getAllClassesInFile(arkfile)) { + for (const arkmethod of arkclass.getMethods()) { + const body = arkmethod.getBody(); + if (body) { + this.analyzeRouterAndUIStatements(body.getCfg(), arkclass, arkfile, arkmethod, null, null); + } + } + } + } + // // 对目前的 边 进行 BFS 遍历 这里的逻辑还是有问题 + // let queue : Array = []; + // let vis : Map = new Map(); + // for(const edge of this.graph.getEd()){ + // queue.push(edge as UIFuncEdge); + // } + // // let defaultArkClass ; + // // let defaultArkMethod ; + // while(queue.length > 0){ + // const edge = queue.shift() as UIFuncEdge; + // if(vis.has(edge)){ + // continue; + // } + // vis.set(edge,true); + // const api_class = edge.getApiClass(); + // //defaultArkClass = api_class ; + // console.log("api_name: ",api_class.getName()); + // console.log("is_default ? :",api_class.checkIsDefualtExportClass()); + // const api_method = edge.getApiMethod(); + // //defaultArkMethod = api_method; + // console.log("current edge :",edge.getApiClass().getName()); + // //const api_method = edge.getApiMethod(); + // for(const file of this.scene.getFiles()){ + // if(api_class.getDeclaringArkFile() == file) continue; + // console.log("file : ",file.getName()," check : " ,api_class.getName() , " is :" ,file.checkIfImportInfoExists(api_class) ); + // console.log("api_file:",api_class.getDeclaringArkFile().getName()); + // if(file.checkIfImportInfoExists(api_class) == true){ + // let new_class_method_pair = file.findClassesUsingApiClass(api_class,api_method); // 这个文件有使用这个类 后面这个方法 —— 死代码检测 + // console.log("new_class_method_pair : ",new_class_method_pair.toString()); + // if(new_class_method_pair){ + // for(const class_method_pair of new_class_method_pair){ + // // 把 当前这个 file 的 Node 建立一条边 到 dst ,然后把这条边 加入到 queue 当中 + // let src_node = this.graph.getNodeByArkFile(file); + // let dst_node = edge.getDstNode() as UIFuncNode ; + // if(src_node && dst_node){ + // let new_edge : UIFuncEdge = new UIFuncEdge(src_node,dst_node,edge.getTransitionMethod(),class_method_pair[0],class_method_pair[1],edge.getAttribute(),edge.getNode()); + // console.log("Add NEW edge from " + src_node.getFile().getName() + " to " + dst_node.getFile().getName() + "api: "+ edge.getTransitionMethod()); + // //this.graph.addEdge(new_edge); + // queue.push(new_edge); + // } + // } + // } + // } + // } + // } + // for(const file of this.scene.getFiles()){ + // const import_files = this.scene.getImportFiles(file); + // for(const import_file of import_files){ + // let src_node = this.graph.getNodeByArkFile(file); + // let dst_node = this.graph.getNodeByArkFile(import_file); + // if(src_node && dst_node && defaultArkClass && defaultArkMethod){ + // let new_edge : UIFuncEdge = new UIFuncEdge(src_node,dst_node,"include",defaultArkClass,defaultArkMethod,null,null); + // if(src_node != dst_node){ + // this.graph.addEdge(new_edge); + // } + // } + // } + // } + } + analyzeRouterAndUIStatements(cfg, arkclass, arkfile, arkmethod, attribute, node) { + console.log("=============arkclass:", arkclass.getName()); + console.log("============method :", arkmethod.getName()); + for (const threeAddresStmt of cfg.getStmts()) { + const uses = threeAddresStmt.getUses(); + console.log("st_string:", threeAddresStmt.toString()); + console.log("st :", threeAddresStmt); + if (threeAddresStmt instanceof ArkInvokeStmt) { + for (const use of uses) { + if (use instanceof ArkInstanceInvokeExpr) { + const methodSubSignature = threeAddresStmt.getInvokeExpr()?.getMethodSignature()?.getMethodSubSignature(); + const api_name = methodSubSignature?.getMethodName() ?? ""; + let expr = threeAddresStmt.getInvokeExpr(); + let base = expr.getBase(); // 调用方法的对象 + // https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-router-V5 + if (api_name == "back" || api_name == "replaceUrl" || api_name == "pushUrl" || api_name == "pushNamedRoute" || api_name == "replaceNamedRoute" || api_name == "showAlertBeforeBackPage" + || api_name == "loadContent" || api_name == "loadContentByName" || api_name == "setUIContent") { + console.log(`base : ${base.toString()}`); + // console.log("router 相关的 stmt :",threeAddresStmt); + console.log(`router 相关的 stmt : ${threeAddresStmt.toString()}`); + console.log("所在类:", arkclass.getName() + ' 所在方法 ', arkmethod.getName()); + console.log("original code : ", threeAddresStmt.getOriginalText()); + // this.AnalyzeApi(arkclass,arkfile,arkmethod,threeAddresStmt,api_name); + } + if (base.getName() == "router" && (SystemRouterMethodsSet.has(api_name) || OhosRouterMethodsSet.has(api_name))) { + this.graph.solveSystemRouterMethod(arkclass, arkfile, arkmethod, threeAddresStmt, api_name, attribute, node, 'Router'); + this.graph.solveOhosRouterMethod(arkclass, arkfile, arkmethod, threeAddresStmt, api_name, attribute, node, 'Router'); + } + if (OhosWindowMethodsSet.has(api_name)) { + console.log("yes windows !"); + this.graph.solveOhosWindowMethod(arkclass, arkfile, arkmethod, threeAddresStmt, api_name, attribute, node, "UIAibility"); + } + if (NavigationMethodsSet.has(api_name)) { + console.log("navi: ", api_name); + console.log("navi stmt :", threeAddresStmt.toString()); + console.log("navi node: ", node?.xpath); + this.graph.solveNavigationMethod(arkclass, arkfile, arkmethod, threeAddresStmt, api_name, attribute, node, 'Navigation'); + } + } + } + const expr = threeAddresStmt.getInvokeExpr(); + const method_signature = expr.getMethodSignature(); + const class_signature = method_signature.getDeclaringClassSignature(); + const file_signature = class_signature.getDeclaringFileSignature(); + const file = this.scene.getFile(file_signature); + const clazz = file?.getClass(class_signature); + const method = clazz?.getMethod(method_signature); + method?.getCfg(); + } + } + } + // public AnalyzeApi(arkclass: ArkClass,arkfile: ArkFile,arkmethod: ArkMethod,threeAddresStmt: ArkInvokeStmt, api_name: string): void{ + // let expr = threeAddresStmt.getInvokeExpr(); + // let args = expr.getArgs(); + // if( args && args.length > 0){ // () 里面有参数的情况 + // const firstarg = args[0]; + // console.log("firstarg : ",firstarg); + // console.log("firstarg type: ",firstarg.getType().toString()); + // // 如果第一个参数是 Local 类型 (局部变量) + // if(firstarg instanceof Local){ + // if(firstarg.getType() instanceof ClassType){ //参数的类型是类 类型 + // let classSignature = (firstarg.getType() as ClassType).getClassSignature(); + // let classname = classSignature.getClassName(); + // let clazz = arkfile.getClassWithName(classname); + // if(clazz){ + // // replaceUrl pushUrl router.back() like .... = router.replaceUrl({url: 'pages/Profile' } + // let url = clazz.getFieldWithName("url"); // router.back({url:'pages/MainPage , params:{editTask: this.backIndexParams(),}}) + // if(url == null) url = clazz.getFieldWithName("name") ; // pushNamedRoute + // let initstmt = url?.getInitializer(); + // if(initstmt && initstmt.length > 0 && initstmt[0] instanceof ArkAssignStmt){ + // let rightop_type = (initstmt[0] as ArkAssignStmt).getRightOp(); + // if(rightop_type instanceof StringConstant){ // 字符串常量 + // let target_files = this.FindTargetFile(rightop_type,api_name); + // if(target_files){ + // //添加边 + // for(let target_file of target_files){ + // let src_node = this.graph.getNodeByArkFile(arkfile); + // let dst_node = this.graph.getNodeByArkFile(target_file); + // if (src_node !== undefined && dst_node !== undefined) { + // let edge: UIFuncEdge = new UIFuncEdge(src_node, dst_node, api_name,arkclass,arkmethod); + // console.log("add edge from " + src_node.getFile().getName() + " to " + dst_node.getFile().getName() + "api: "+ api_name); + // // 添加边到图中 + // this.graph.addEdge(edge); + // } else { + // // 处理 src_node 或 dst_node 为 undefined 的情况,例如抛出错误或返回 + // console.error('Either src_node or dst_node is undefined'); + // } + // } + // } + // } + // } + // } + // } + // }else if(firstarg instanceof StringConstant){ // 如果第一个参数是 StringConstan 字符串常量 + // const value = firstarg.getValue(); + // let load_page = this.scene.getFileByPathSubstring(value); + // if(load_page){ + // let src_node = this.graph.getNodeByArkFile(arkfile); + // let dst_node = this.graph.getNodeByArkFile(load_page); + // if(src_node && dst_node){ + // let edge: UIFuncEdge = new UIFuncEdge(src_node, dst_node, api_name,arkclass,arkmethod); + // // 添加边到图中 + // console.log("add edge from " + src_node.getFile().getName() + " to " + dst_node.getFile().getName() + "api: "+ api_name); + // this.graph.addEdge(edge); + // }else{ + // console.error('Either src_node or dst_node is undefined'); + // } + // }else{ + // console.error('load page is undefined'); + // } + // } + // }else{ // 没有参数的情况 router.back() + // console.log("no arg stmt: ",expr.toString()); + // } + // } + // public AnalyzeWindowsCreate(body: ArkBody , arkclass : ArkClass , arkfile:ArkFile , arkmethod : ArkMethod ,threeAddresStmt: ArkInvokeStmt,api_name: string) : void { + // let args = threeAddresStmt.getInvokeExpr()?.getArgs(); + // if(args && args.length > 0) { + // const firstArg = args[0]; + // if(firstArg instanceof StringConstant){ + // const value = firstArg.getValue(); + // let load_page = this.scene.getFileByPathSubstring(value); + // if(load_page){ + // let src_node = this.graph.getNodeByArkFile(arkfile); + // let dst_node = this.graph.getNodeByArkFile(load_page); + // if(src_node && dst_node){ + // let edge: UIFuncEdge = new UIFuncEdge(src_node, dst_node, api_name,arkclass,arkmethod); + // // 添加边到图中 + // console.log("add edge from " + src_node.getFile().getName() + " to " + dst_node.getFile().getName() + "api: "+ api_name); + // this.graph.addEdge(edge); + // }else{ + // console.error('Either src_node or dst_node is undefined'); + // } + // }else{ + // console.error('load page is undefined'); + // } + // } + // } + // } + FindTargetFile(rightop_type, api_name) { + let result = []; + if (api_name == "back" || api_name == "replaceUrl" || api_name == "pushUrl" || api_name == "showAlertBeforeBackPage") { + let target_url = rightop_type.getValue(); + let target_file = this.scene.getFileByPathSubstring(target_url); + if (target_file != null) { + result.push(target_file); + } + } + return result; + } + dosolveWindowMethod(cfg, arkclass, arkfile, arkmethod, attribute, node) { + for (const threeAddresStmt of cfg.getStmts()) { + const uses = threeAddresStmt.getUses(); + if (threeAddresStmt instanceof ArkInvokeStmt) { + for (const use of uses) { + if (use instanceof ArkInstanceInvokeExpr) { + const methodSubSignature = threeAddresStmt.getInvokeExpr()?.getMethodSignature()?.getMethodSubSignature(); + const api_name = methodSubSignature?.getMethodName() ?? ""; + //let expr = threeAddresStmt.getInvokeExpr() as ArkInstanceInvokeExpr; + //let base = expr.getBase(); // 调用方法的对象 + if (OhosWindowMethodsSet.has(api_name)) { + console.log("yes windows !"); + this.graph.solveOhosWindowMethod(arkclass, arkfile, arkmethod, threeAddresStmt, api_name, attribute, node, 'UIAibility'); + } + } + } + const expr = threeAddresStmt.getInvokeExpr(); + const method_signature = expr.getMethodSignature(); + const class_signature = method_signature.getDeclaringClassSignature(); + const file_signature = class_signature.getDeclaringFileSignature(); + const file = this.scene.getFile(file_signature); + const clazz = file?.getClass(class_signature); + const method = clazz?.getMethod(method_signature); + const cfg2 = method?.getCfg(); + if (cfg2) { + this.dosolveWindowMethod(cfg2, arkclass, arkfile, cfg.getDeclaringMethod(), attribute, node); + } + } + } + } + buildUiFuncGraphFromViewTree() { + // WindowMethod + for (const arkfile of this.scene.getFiles()) { + for (const arkclass of arkfile.getClasses()) { + for (const arkmethod of arkclass.getMethods()) { + const body = arkmethod.getBody(); + if (body) { + this.dosolveWindowMethod(body.getCfg(), arkclass, arkfile, arkmethod, "null", null); + } + } + } + } + for (const arkfile of this.scene.getFiles()) { + for (const arkclass of arkfile.getClasses()) { + let viewTree = arkclass.getArkUIViewTree(); + if (viewTree && viewTree.getRoot()) { + // 确保 viewTree.getRoot() 不是 null + arkclass.generateArkUIViewTreeXpath(viewTree.getRoot(), "root"); + } + //console.log("viewTree: ",viewTree); + if (viewTree) { + const walkTree = (node) => { + console.log("node: ", node.name); + console.log("arkclass : ", arkclass.getName()); + node.attributes.forEach((value, key) => { + //console.log("key: ",key); + const [stmt, details] = value; + console.log("stmt: ", stmt.toString()); + const cfg = stmt.getCfg(); + console.log("上面的"); + //this.analyzeRouterAndUIStatements(cfg,arkclass,arkfile,cfg.getDeclaringMethod(),key,node); + // 这个语句是属于哪个方法,沿着这个方法的CallGraph分析每个cfg + const stmt_method = cfg.getDeclaringMethod(); + let stmt_callGraph = new CallGraph(this.scene); + let stmt_callGraphBuilder = new CallGraphBuilder(stmt_callGraph, this.scene); + let stmt_entryPoints = []; + let stmt_method_signature = stmt_method?.getSignature(); + if (stmt_method_signature) { + stmt_entryPoints.push(stmt_method_signature); + } + stmt_callGraphBuilder.buildClassHierarchyCallGraph(stmt_entryPoints, false); + if (stmt_method) { + stmt_callGraphBuilder.buildDirectCallGraph([stmt_method]); + } + for (let call_node of stmt_callGraph.getNodesIter()) { + let stmt_node_method_signature = call_node.getMethod(); + const stmt_node_clazz = this.scene.getClass(stmt_node_method_signature.getDeclaringClassSignature()); + let stmt_node_method = stmt_node_clazz?.getMethod(stmt_node_method_signature); + console.log("method name :", stmt_node_method?.getName()); + const stmt_node_cfg = stmt_node_method?.getCfg(); + if (stmt_node_cfg) { + this.analyzeRouterAndUIStatements(stmt_node_cfg, stmt_node_cfg.getDeclaringMethod().getDeclaringArkClass(), arkfile, stmt_node_cfg.getDeclaringMethod(), key, node); + } + } + details.forEach((detail, index) => { + if (detail instanceof MethodSignature) { // 根据 CallGFraph 遍历与这个函数有关的所有Stmt + const clazz = this.scene.getClass(detail.getDeclaringClassSignature()); + const method = clazz?.getMethod(detail); + console.log("method name: ", method?.getName()); + const details_cfg = method?.getCfg(); + let callGraph = new CallGraph(this.scene); + let callGraphBuilder = new CallGraphBuilder(callGraph, this.scene); + let entryPoints = []; + let method_signature = method?.getSignature(); + if (method_signature) { + entryPoints.push(method_signature); + } + if (method) { + callGraphBuilder.buildDirectCallGraph([method]); + } + callGraphBuilder.buildClassHierarchyCallGraph(entryPoints, false); + if (details_cfg) { + console.log("下面的"); + this.analyzeRouterAndUIStatements(details_cfg, details_cfg.getDeclaringMethod().getDeclaringArkClass(), arkfile, details_cfg.getDeclaringMethod(), key, node); + } + for (let call_node of callGraph.getNodesIter()) { + let node_method_signature = call_node.getMethod(); + const node_clazz = this.scene.getClass(node_method_signature.getDeclaringClassSignature()); + let node_method = node_clazz?.getMethod(node_method_signature); + console.log("method name :", node_method?.getName()); + const node_cfg = node_method?.getCfg(); + if (node_cfg) { + this.analyzeRouterAndUIStatements(node_cfg, node_cfg.getDeclaringMethod().getDeclaringArkClass(), arkfile, node_cfg.getDeclaringMethod(), key, node); + } + } + } + }); + }); + node.children.forEach(walkTree); + }; + const rootNode = viewTree.getRoot(); + if (rootNode) { + walkTree(rootNode); + } + } + } + } + // // 对目前的 边 进行 BFS 遍历 这里的逻辑还是有问题 + // let queue : Array = []; + // let vis : Map = new Map(); + // for(const edge of this.graph.getEd()){ + // queue.push(edge as UIFuncEdge); + // } + // // let defaultArkClass = new ArkClass(); + // // let defaultArkMethod = new ArkMethod(); + // while(queue.length > 0){ + // const edge = queue.shift() as UIFuncEdge; + // if(vis.has(edge)){ + // continue; + // } + // vis.set(edge,true); + // const api_class = edge.getApiClass(); + // // defaultArkClass = api_class ; + // console.log("api_name: ",api_class.getName()); + // console.log("is_default ? :",api_class.checkIsDefualtExportClass()); + // const api_method = edge.getApiMethod(); + // // defaultArkMethod = api_method; + // console.log("current edge :",edge.getApiClass().getName()); + // //const api_method = edge.getApiMethod(); + // for(const file of this.scene.getFiles()){ + // if(api_class.getDeclaringArkFile() == file) continue; + // console.log("file : ",file.getName()," check : " ,api_class.getName() , " is :" ,file.checkIfImportInfoExists(api_class) ); + // console.log("api_file:",api_class.getDeclaringArkFile().getName()); + // if(file.checkIfImportInfoExists(api_class) == true){ + // let new_class_method_pair = file.findClassesUsingApiClass(api_class,api_method); // 这个文件有使用这个类 后面这个方法 —— 死代码检测 + // console.log("new_class_method_pair : ",new_class_method_pair.toString()); + // if(new_class_method_pair){ + // for(const class_method_pair of new_class_method_pair){ + // // 把 当前这个 file 的 Node 建立一条边 到 dst ,然后把这条边 加入到 queue 当中 + // let src_node = this.graph.getNodeByArkFile(file); + // let dst_node = edge.getDstNode() as UIFuncNode ; + // if(src_node && dst_node){ + // let new_edge : UIFuncEdge = new UIFuncEdge(src_node,dst_node,edge.getTransitionMethod(),class_method_pair[0],class_method_pair[1],edge.getAttribute(),edge.getNode()); + // console.log("Add NEW edge from " + src_node.getFile().getName() + " to " + dst_node.getFile().getName() + "api: "+ edge.getTransitionMethod()); + // this.graph.addEdge(new_edge); + // queue.push(new_edge); + // } + // } + // } + // } + // } + // } + // for(const file of this.scene.getFiles()){ + // const import_files = this.scene.getImportFiles(file); + // for(const import_file of import_files){ + // let src_node = this.graph.getNodeByArkFile(file); + // let dst_node = this.graph.getNodeByArkFile(import_file); + // if(src_node && dst_node && defaultArkClass && defaultArkMethod){ + // let new_edge : UIFuncEdge = new UIFuncEdge(src_node,dst_node,"include",defaultArkClass,defaultArkMethod,null,null); + // if(src_node != dst_node){ + // this.graph.addEdge(new_edge); + // } + // } + // } + // } + } + FindApiFromViewTree() { + { + for (const arkfile of this.scene.getFiles()) { + for (const arkclass of arkfile.getClasses()) { + if (arkclass.getName() != "Index") + continue; + for (const arkmethod of arkclass.getMethods()) { + if (arkmethod.getName() == "build") + continue; + if (!COMPONENT_LIFECYCLE_METHOD_NAME.includes(arkmethod.getName())) + continue; + console.log("arkmethod_name: ", arkmethod.getSignature().toString()); + console.log("arkmethod :", arkmethod); + const body = arkmethod.getBody(); + if (body) { + this.graph.AnalyzerArkCfg(body.getCfg(), null, arkclass, arkmethod, arkmethod.getName()); + } + } + } + } + } + } + FindApiFromViewTreeUseCallGraph() { + // 只分析生命周期函数? + { + for (const arkfile of this.scene.getFiles()) { + for (const arkclass of arkfile.getClasses()) { + //if(arkclass.getName() != "TaskDetail") continue; + //console.log("class: ",arkclass.getName()); + for (const arkmethod of arkclass.getMethods()) { + if (arkmethod.getName() == "build") + continue; + if (!COMPONENT_LIFECYCLE_METHOD_NAME.includes(arkmethod.getName()) && !LIFECYCLE_METHOD_NAME.includes(arkmethod.getName())) + continue; + //console.log("============== arkmethod_name: ",arkmethod.getSignature().toString()); + let callGraph = new CallGraph(this.scene); + let callGraphBuilder = new CallGraphBuilder(callGraph, this.scene); + let entryPoints = []; + entryPoints.push(arkmethod.getSignature()); + callGraphBuilder.buildClassHierarchyCallGraph(entryPoints, false); + for (const node of callGraph.getNodesIter()) { + const funcId = node.getID(); + const call_arkmethod = callGraph.getArkMethodByFuncID(funcId); + //console.log("call_arkmethod: ", call_arkmethod?.getSignature().toString()); + if (call_arkmethod) { + // for(const stmt of call_arkmethod.getBody()?.getCfg().getStmts() || []){ + // console.log("stmt: ", stmt.toString()); + // } + this.graph.AnalyzeArkMethod_NEW(call_arkmethod, null, arkclass, arkmethod.getName()); + } + } + } + } + } + } + { + for (const it of this.graph.pageToUIFuncNodeMap.values()) { + const arkclass = it.getClass(); + // if(arkclass.getName() != "Explore") continue; + // console.log("Processing class: ", arkclass.getName()); + let viewTree = arkclass.getArkUIViewTree(); + let folderName = this.scene.getProjectName(); + let dotFileName = `exam_out/${folderName}/component2handler_${arkclass.getName()}.dot`; // 在外面加上 exam_out 文件夹 + if (viewTree) { + let treePrinter = new ArkUIViewTreePrinter(viewTree); + let dotContent = treePrinter.dump(); // 获取 .dot 格式内容 + // 确保目录存在 + const dir = path.dirname(dotFileName); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); // 如果目录不存在,创建目录 + } + fs.writeFileSync(dotFileName, dotContent, 'utf8'); // 保存为 .dot 文件 + console.log(`Component tree for ${arkclass.getName()} has been written to ${dotFileName}`); + } + if (viewTree && viewTree.getRoot()) { + arkclass.generateArkUIViewTreeXpath(viewTree.getRoot(), "root"); + } + if (!arkclass.hasArkUIViewTree()) + continue; + if (viewTree) { + const walkTree = (node) => { + node.attributes.forEach((value, key) => { + if (!(LIFECYCLE_METHOD_NAME.includes(key) || CALLBACK_METHOD_NAME.includes(key) || COMPONENT_LIFECYCLE_METHOD_NAME.includes(key))) + return; + const [stmt, details] = value; + if (stmt instanceof ArkInvokeStmt) { + this.graph.AnalyzeArkInvokeStmt_NEW(stmt, node, arkclass, stmt.getCfg().getDeclaringMethod(), key); + } + details.forEach((detail, index) => { + // console.log("key : ", key); + // console.log("detail: ", detail); + if (detail instanceof MethodSignature) { // 根据 CallGFraph 遍历与这个函数有关的所有Stmt + let method = this.graph.getMethod(detail); + if (method) { + const methodSignatureStr = method.getSignature().toString().replace(/[^\w\-\.]/g, "_"); + //console.log("------- method: ", method.getSignature().toString()); + let callGraph = new CallGraph(this.scene); + let callGraphBuilder = new CallGraphBuilder(callGraph, this.scene); + let entryPoints = []; + entryPoints.push(method.getSignature()); + callGraphBuilder.buildRapidTypeCallGraph(entryPoints, false); + // console.log("node : ",node.name , "call sig: ",methodSignatureStr); + // callGraph.dump(`out/callgraph_${methodSignatureStr}.dot`); + callGraph.dump(path.resolve(__dirname, `out/callgraph_${methodSignatureStr}.dot`)); + for (const call_node of callGraph.getNodesIter()) { + const funcId = call_node.getID(); + const call_arkmethod = callGraph.getArkMethodByFuncID(funcId); + //console.log("--node_call_arkmethod: ", call_arkmethod?.getSignature().toString()); + // if(call_arkmethod?.getSignature().toString() == "@Healthy_life/entry/src/main/ets/common/utils/BroadCast.ets: BroadCast.emit(string, number|number[]|(@Healthy_life/entry/src/main/ets/viewmodel/TaskInfo.ets: TaskInfo|@Healthy_life/entry/src/main/ets/view/dialog/CustomDialogView.ets: CustomDialogCallback)[])"){ + // console.log("method: ",call_arkmethod); + // } + if (call_arkmethod) { + this.graph.AnalyzeArkMethod_NEW(call_arkmethod, node, arkclass, key); + } + } + } + } + }); + }); + node.children.forEach(walkTree); + }; + const rootNode = viewTree.getRoot(); + if (rootNode) { + walkTree(rootNode); + } + } + } + } + this.graph.doSolveBackEdge(); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class DominanceFinder { + blocks = []; + blockToIdx = new Map(); + idoms = []; + domFrontiers = []; + constructor(cfg) { + this.blocks = Array.from(cfg.getBlocks()); + for (let i = 0; i < this.blocks.length; i++) { + let block = this.blocks[i]; + this.blockToIdx.set(block, i); + } + const startingBlock = cfg.getStartingBlock(); + // calculate immediate dominator for each block + this.idoms = new Array(this.blocks.length); + this.idoms[0] = 0; + for (let i = 1; i < this.idoms.length; i++) { + this.idoms[i] = -1; + } + let isChanged = true; + while (isChanged) { + isChanged = false; + for (const block of this.blocks) { + if (block === startingBlock) { + continue; + } + let blockIdx = this.blockToIdx.get(block); + let preds = Array.from(block.getPredecessors()); + let newIdom = this.getFirstDefinedBlockPredIdx(preds); + if (preds.length <= 0 || newIdom === -1) { + continue; + } + for (const pred of preds) { + let predIdx = this.blockToIdx.get(pred); + this.idoms[predIdx] !== -1 ? (newIdom = this.intersect(newIdom, predIdx)) : null; + } + if (this.idoms[blockIdx] !== newIdom) { + this.idoms[blockIdx] = newIdom; + isChanged = true; + } + } + } + // calculate dominance frontiers for each block + this.domFrontiers = new Array(this.blocks.length); + for (let i = 0; i < this.domFrontiers.length; i++) { + this.domFrontiers[i] = new Array(); + } + for (const block of this.blocks) { + let preds = Array.from(block.getPredecessors()); + if (preds.length <= 1) { + continue; + } + let blockIdx = this.blockToIdx.get(block); + for (const pred of preds) { + let predIdx = this.blockToIdx.get(pred); + while (predIdx !== this.idoms[blockIdx]) { + this.domFrontiers[predIdx].push(blockIdx); + predIdx = this.idoms[predIdx]; + } + } + } + } + getDominanceFrontiers(block) { + if (!this.blockToIdx.has(block)) { + throw new Error('The given block: ' + block + ' is not in Cfg!'); + } + let idx = this.blockToIdx.get(block); + let dfs = new Set(); + let dfsIdx = this.domFrontiers[idx]; + for (const dfIdx of dfsIdx) { + dfs.add(this.blocks[dfIdx]); + } + return dfs; + } + getBlocks() { + return this.blocks; + } + getBlockToIdx() { + return this.blockToIdx; + } + getImmediateDominators() { + return this.idoms; + } + getFirstDefinedBlockPredIdx(preds) { + for (const block of preds) { + let idx = this.blockToIdx.get(block); + if (this.idoms[idx] !== -1) { + return idx; + } + } + return -1; + } + intersect(a, b) { + while (a !== b) { + if (a > b) { + a = this.idoms[a]; + } + else { + b = this.idoms[b]; + } + } + return a; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class DominanceTree { + blocks = []; + blockToIdx = new Map(); + children = []; + parents = []; + constructor(dominanceFinder) { + this.blocks = dominanceFinder.getBlocks(); + this.blockToIdx = dominanceFinder.getBlockToIdx(); + let idoms = dominanceFinder.getImmediateDominators(); + // build the tree + let treeSize = this.blocks.length; + this.children = new Array(treeSize); + this.parents = new Array(treeSize); + for (let i = 0; i < treeSize; i++) { + this.children[i] = []; + this.parents[i] = -1; + } + for (let i = 0; i < treeSize; i++) { + if (idoms[i] !== i) { + this.parents[i] = idoms[i]; + this.children[idoms[i]].push(i); + } + } + } + getAllNodesDFS() { + let dfsBlocks = new Array(); + let queue = new Array(); + queue.push(this.getRoot()); + while (queue.length !== 0) { + let curr = queue.splice(0, 1)[0]; + dfsBlocks.push(curr); + let childList = this.getChildren(curr); + if (childList.length !== 0) { + for (let i = childList.length - 1; i >= 0; i--) { + queue.splice(0, 0, childList[i]); + } + } + } + return dfsBlocks; + } + getChildren(block) { + let childList = new Array(); + let idx = this.blockToIdx.get(block); + for (const i of this.children[idx]) { + childList.push(this.blocks[i]); + } + return childList; + } + getRoot() { + return this.blocks[0]; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Basic SCC info for a single node + */ +class NodeSCCInfo { + _rep; + _subNodes; + constructor() { + this._rep = Number.MAX_SAFE_INTEGER; + this._subNodes = new Set(); + } + get rep() { + return this._rep; + } + set rep(n) { + this._rep = n; + } + addSubNodes(n) { + this._subNodes.add(n); + } + get subNodes() { + return this._subNodes; + } +} +/** + * Detect strongly connected components in a directed graph + * A topological graph is an extra product from this algorithm + * Advanced Nuutila’s algorithm which come from the following paper: + * Wave Propagation and Deep Propagation for pointer Analysis + * CGO 2009 + */ +class SCCDetection { + // graph G = (V, E) + _G; + // counter + _I; + // map of V to {1, . . . , |V |} ∪ ⊥, associates the + // nodes in V to the order in which they are visited by + // Nuutila’s algorithm. Initially, D(v) = ⊥. + _D; + // map of V to V , associates each node in a cycle to + // the representative of that cycle. Initially R(v) = v. + _R; + // stack of V, holds the nodes that are in a cycle, but + // have not yet been inserted into C. Initially empty + _S; + // stack of V , holds the nodes of V that are represenatives + // of strongly connected components. T keeps the + // nodes in topological order, that is, the top node has no + // predecessors. Initially empty + _T; + repNodes; + visitedNodes; + inSCCNodes; + constructor(GT) { + this._G = GT; + this._I = 0; + this._D = new Map(); + this._S = new Array(); + this._T = new Array(); + this.repNodes = new Set(); + this._R = new Map(); + this.visitedNodes = new Set(); + this.inSCCNodes = new Set(); + } + isVisited(n) { + return this.visitedNodes.has(n); + } + inSCC(n) { + return this.inSCCNodes.has(n); + } + setVisited(n) { + this.visitedNodes.add(n); + } + setInSCC(n) { + this.inSCCNodes.add(n); + } + setRep(n, r) { + let sccIn = this._R.get(n); + if (!sccIn) { + sccIn = new NodeSCCInfo(); + this._R.set(n, sccIn); + } + sccIn.rep = r; + let rInfo = this._R.get(r); + if (!rInfo) { + rInfo = new NodeSCCInfo(); + this._R.set(r, rInfo); + } + rInfo.addSubNodes(n); + if (n !== r) { + sccIn.subNodes.clear(); + this.repNodes.add(r); + } + } + getRep(n) { + let info = this._R.get(n); + if (!info) { + info = new NodeSCCInfo(); + this._R.set(n, info); + } + return info.rep; + } + getNode(id) { + let n = this._G.getNode(id); + if (!n) { + throw new Error('Node is not found'); + } + return n; + } + visit(v) { + this._I += 1; + this._D.set(v, this._I); + this.setRep(v, v); + this.setVisited(v); + let node = this.getNode(v); + node.getOutgoingEdges().forEach(e => { + let w = e.getDstID(); + if (!this.isVisited(w)) { + this.visit(w); + } + if (!this.inSCC(w)) { + let repV = this.getRep(v); + let repW = this.getRep(w); + if (!this._D.has(repV) || !this._D.has(repW)) { + throw new Error('Error happening in SCC detection'); + } + let rep = this._D.get(repV) < this._D.get(repW) ? repV : repW; + this.setRep(v, rep); + } + }); + if (this.getRep(v) === v) { + this.setInSCC(v); + while (this._S.length > 0) { + let w = this._S.at(this._S.length - 1); + if (this._D.get(w) <= this._D.get(v)) { + break; + } + else { + this._S.pop(); + this.setInSCC(w); + this.setRep(w, v); + } + } + this._T.push(v); + } + else { + this._S.push(v); + } + } + clear() { + this._R.clear(); + this._I = 0; + this._D.clear(); + this.repNodes.clear(); + this._S.length = 0; + this._T.length = 0; + this.inSCCNodes.clear(); + this.visitedNodes.clear(); + } + /** + * Get the rep node + * If not found return itself + */ + getRepNode(n) { + const it = this._R.get(n); + if (!it) { + throw new Error('scc rep not found'); + } + const rep = it.rep; + return rep !== Number.MAX_SAFE_INTEGER ? rep : n; + } + /** + * Start to detect and collapse SCC + */ + find() { + this.clear(); + let nodeIt = this._G.nodesItor(); + for (let node of nodeIt) { + const nodeId = node.getID(); + if (!this.isVisited(nodeId) && !this._D.has(nodeId)) { + this.visit(nodeId); + } + } + } + getTopoAndCollapsedNodeStack() { + return this._T; + } + getNode2SCCInfoMap() { + return this._R; + } + // whether the node is in a cycle + nodeIsInCycle(n) { + const rep = this.getRepNode(n); + const subNodesCount = this.getSubNodes(rep).size; + // multi-node cycle + if (subNodesCount > 1) { + return true; + } + // self-cycle: a call a + let repNode = this._G.getNode(rep); + for (const e of repNode?.getOutgoingEdges()) { + if (e.getDstID() === rep) { + return true; + } + } + return false; + } + getMySCCNodes(n) { + const rep = this.getRepNode(n); + return this.getSubNodes(rep); + } + // get all subnodes in one scc + getSubNodes(n) { + const it = this._R.get(n); + if (!it) { + throw new Error('sccInfo not found for a node'); + } + let sub = it.subNodes; + if (sub.size === 0) { + sub.add(n); + } + return sub; + } + // get all representative nodes + getRepNodes() { + return this.repNodes; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$6 = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'getAllFiles'); +/** + * 从指定目录中提取指定后缀名的所有文件 + * @param srcPath string 要提取文件的项目入口,相对或绝对路径都可 + * @param exts string[] 要提取的文件扩展名数组,每个扩展名需以点开头 + * @param filenameArr string[] 用来存放提取出的文件的原始路径的数组,可不传,默认为空数组 + * @param visited: Set 用来存放已经访问过的路径,避免递归栈溢出,可不传,默认为空数组 + * @return string[] 提取出的文件的原始路径数组 + */ +function getAllFiles(srcPath, exts, ignore = [], filenameArr = [], visited = new Set()) { + let ignoreFiles = new Set(ignore); + // 如果源目录不存在,直接结束程序 + if (!fs$1.existsSync(srcPath)) { + logger$6.error(`Input directory ${srcPath} is not exist, please check!`); + return filenameArr; + } + // 获取src的绝对路径 + const realSrc = fs$1.realpathSync(srcPath); + if (visited.has(realSrc)) { + return filenameArr; + } + visited.add(realSrc); + // 遍历src,判断文件类型 + fs$1.readdirSync(realSrc).forEach(filename => { + if (ignoreFiles.has(filename)) { + return; + } + // 拼接文件的绝对路径 + const realFile = path$1.resolve(realSrc, filename); + //TODO: 增加排除文件后缀和目录 + // 如果是目录,递归提取 + if (fs$1.statSync(realFile).isDirectory()) { + getAllFiles(realFile, exts, ignore, filenameArr, visited); + } + else { + // 如果是文件,则判断其扩展名是否在给定的扩展名数组中 + if (exts.includes(path$1.extname(filename))) { + filenameArr.push(realFile); + } + } + }); + return filenameArr; +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$5 = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'Config'); +const CONFIG_FILENAME = 'arkanalyzer.json'; +const DEFAULT_CONFIG_FILE = path$1.join(__dirname, '../config', CONFIG_FILENAME); +class SceneConfig { + targetProjectName = ''; + targetProjectDirectory = ''; + etsSdkPath = ''; + sdksObj = []; + sdkFiles = []; + sdkFilesMap = new Map(); + projectFiles = []; + fileLanguages = new Map(); + options; + constructor(options) { + this.options = { supportFileExts: ['.ets', '.ts'] }; + this.loadDefaultConfig(options); + } + getOptions() { + return this.options; + } + /** + * Set the scene's config, + * such as the target project's name, the used sdks and the full path. + * @param targetProjectName - the target project's name. + * @param targetProjectDirectory - the target project's directory. + * @param sdks - sdks used in this scene. + * @param fullFilePath - the full file path. + */ + buildConfig(targetProjectName, targetProjectDirectory, sdks, fullFilePath) { + this.targetProjectName = targetProjectName; + this.targetProjectDirectory = targetProjectDirectory; + this.projectFiles = getAllFiles(targetProjectDirectory, this.options.supportFileExts, this.options.ignoreFileNames); + this.sdksObj = sdks; + if (fullFilePath) { + this.projectFiles.push(...fullFilePath); + } + } + /** + * Create a sceneConfig object for a specified project path and set the target project directory to the + * targetProjectDirectory property of the sceneConfig object. + * @param targetProjectDirectory - the target project directory, such as xxx/xxx/xxx, started from project + * directory. + * @example + * 1. build a sceneConfig object. + ```typescript + const projectDir = 'xxx/xxx/xxx'; + const sceneConfig: SceneConfig = new SceneConfig(); + sceneConfig.buildFromProjectDir(projectDir); + ``` + */ + buildFromProjectDir(targetProjectDirectory) { + this.targetProjectDirectory = targetProjectDirectory; + this.targetProjectName = path$1.basename(targetProjectDirectory); + this.projectFiles = getAllFiles(targetProjectDirectory, this.options.supportFileExts, this.options.ignoreFileNames); + } + buildFromProjectFiles(projectName, projectDir, filesAndDirectorys, sdks, languageTags) { + if (sdks) { + this.sdksObj = sdks; + } + this.targetProjectDirectory = projectDir; + this.targetProjectName = projectName; + if (filesAndDirectorys.length === 0) { + logger$5.error('no files for build scene!'); + return; + } + filesAndDirectorys.forEach(fileOrDirectory => this.processFilePaths(fileOrDirectory, projectDir)); + languageTags?.forEach((languageTag, fileOrDirectory) => { + this.setLanguageTagForFiles(fileOrDirectory, projectDir, languageTag); + }); + } + processFilePaths(fileOrDirectory, projectDir) { + let absoluteFilePath = ''; + if (path$1.isAbsolute(fileOrDirectory)) { + absoluteFilePath = fileOrDirectory; + } + else { + absoluteFilePath = path$1.join(projectDir, fileOrDirectory); + } + if (fs$1.statSync(absoluteFilePath).isDirectory()) { + getAllFiles(absoluteFilePath, this.getOptions().supportFileExts, this.options.ignoreFileNames).forEach(filePath => { + if (!this.projectFiles.includes(filePath)) { + this.projectFiles.push(filePath); + } + }); + } + else { + this.projectFiles.push(absoluteFilePath); + } + } + setLanguageTagForFiles(fileOrDirectory, projectDir, languageTag) { + let absoluteFilePath = ''; + if (path$1.isAbsolute(fileOrDirectory)) { + absoluteFilePath = fileOrDirectory; + } + else { + absoluteFilePath = path$1.join(projectDir, fileOrDirectory); + } + if (fs$1.statSync(absoluteFilePath).isDirectory()) { + getAllFiles(absoluteFilePath, this.getOptions().supportFileExts, this.options.ignoreFileNames).forEach(filePath => { + this.fileLanguages.set(filePath, languageTag); + }); + } + else { + this.fileLanguages.set(absoluteFilePath, languageTag); + } + } + buildFromJson(configJsonPath) { + if (fs$1.existsSync(configJsonPath)) { + let configurationsText; + try { + configurationsText = fs$1.readFileSync(configJsonPath, 'utf-8'); + } + catch (error) { + logger$5.error(`Error reading file: ${error}`); + return; + } + logger$5.info(configurationsText); + let configurations; + try { + configurations = JSON.parse(configurationsText); + } + catch (error) { + logger$5.error(`Error parsing JSON: ${error}`); + return; + } + const targetProjectName = configurations.targetProjectName ? configurations.targetProjectName : ''; + const targetProjectDirectory = configurations.targetProjectDirectory ? path.resolve(__dirname, configurations.targetProjectDirectory) : ''; + const sdks = configurations.sdks ? configurations.sdks : []; + if (configurations.options) { + this.options = { ...this.options, ...configurations.options }; + } + this.buildConfig(targetProjectName, targetProjectDirectory, sdks); + } + else { + logger$5.error(`Your configJsonPath: "${configJsonPath}" is not exist.`); + } + } + getTargetProjectName() { + return this.targetProjectName; + } + getTargetProjectDirectory() { + return this.targetProjectDirectory; + } + getProjectFiles() { + return this.projectFiles; + } + getFileLanguages() { + return this.fileLanguages; + } + getSdkFiles() { + return this.sdkFiles; + } + getSdkFilesMap() { + return this.sdkFilesMap; + } + getEtsSdkPath() { + return this.etsSdkPath; + } + getSdksObj() { + return this.sdksObj; + } + getDefaultConfigPath() { + try { + const moduleRoot = path$1.dirname(path$1.dirname(require.resolve('arkanalyzer'))); + return path$1.join(moduleRoot, 'config', CONFIG_FILENAME); + } + catch (e) { + logger$5.info(`Failed to resolve default config file from dependency path with error: ${e}`); + let configFile = DEFAULT_CONFIG_FILE; + if (!fs$1.existsSync(configFile)) { + logger$5.debug(`default config file '${DEFAULT_CONFIG_FILE}' not found.`); + configFile = path$1.join(__dirname, 'config', CONFIG_FILENAME); + logger$5.debug(`use new config file '${configFile}'.`); + } + else { + logger$5.debug(`default config file '${DEFAULT_CONFIG_FILE}' found, use it.`); + } + return configFile; + } + } + loadDefaultConfig(options) { + const configFile = this.getDefaultConfigPath(); + logger$5.debug(`try to parse config file ${configFile}`); + try { + this.options = { ...this.options, ...JSON.parse(fs$1.readFileSync(configFile, 'utf-8')) }; + } + catch (error) { + logger$5.error(`Failed to parse config file with error: ${error}`); + } + if (options) { + this.options = { ...this.options, ...options }; + } + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +function buildImportInfo(node, sourceFile, arkFile) { + if (ts.isImportDeclaration(node)) { + return buildImportDeclarationNode(node, sourceFile, arkFile); + } + else if (ts.isImportEqualsDeclaration(node)) { + return buildImportEqualsDeclarationNode(node, sourceFile, arkFile); + } + return []; +} +function buildImportDeclarationNode(node, sourceFile, arkFile) { + const originTsPosition = LineColPosition.buildFromNode(node, sourceFile); + const tsSourceCode = node.getText(sourceFile); + let importInfos = []; + let importFrom = ''; + if (ts.isStringLiteral(node.moduleSpecifier)) { + importFrom = node.moduleSpecifier.text; + } + let modifiers = 0; + if (node.modifiers) { + modifiers = buildModifiers(node); + } + // just like: import '../xxx' + if (!node.importClause) { + let importClauseName = ''; + let importType = ''; + let importInfo = new ImportInfo(); + importInfo.build(importClauseName, importType, importFrom, originTsPosition, modifiers); + importInfo.setTsSourceCode(tsSourceCode); + IRUtils.setComments(importInfo, node, sourceFile, arkFile.getScene().getOptions()); + importInfos.push(importInfo); + } + //just like: import fs from 'fs' + if (node.importClause && node.importClause.name && ts.isIdentifier(node.importClause.name)) { + let importClauseName = node.importClause.name.text; + const pos = LineColPosition.buildFromNode(node.importClause.name, sourceFile); + let importType = 'Identifier'; + let importInfo = new ImportInfo(); + importInfo.build(importClauseName, importType, importFrom, pos, modifiers); + importInfo.setTsSourceCode(tsSourceCode); + IRUtils.setComments(importInfo, node, sourceFile, arkFile.getScene().getOptions()); + importInfos.push(importInfo); + } + // just like: import {xxx} from './yyy' + if (node.importClause && node.importClause.namedBindings && ts.isNamedImports(node.importClause.namedBindings)) { + let importType = 'NamedImports'; + if (node.importClause.namedBindings.elements) { + node.importClause.namedBindings.elements.forEach(element => { + if (element.name && ts.isIdentifier(element.name)) { + let importClauseName = element.name.text; + const pos = LineColPosition.buildFromNode(element, sourceFile); + if (element.propertyName && ts.isIdentifier(element.propertyName)) { + let importInfo = new ImportInfo(); + importInfo.build(importClauseName, importType, importFrom, pos, modifiers, element.propertyName.text); + importInfo.setTsSourceCode(tsSourceCode); + IRUtils.setComments(importInfo, node, sourceFile, arkFile.getScene().getOptions()); + importInfos.push(importInfo); + } + else { + let importInfo = new ImportInfo(); + importInfo.build(importClauseName, importType, importFrom, pos, modifiers); + importInfo.setTsSourceCode(tsSourceCode); + IRUtils.setComments(importInfo, node, sourceFile, arkFile.getScene().getOptions()); + importInfos.push(importInfo); + } + } + }); + } + } + // just like: import * as ts from 'ohos-typescript' + if (node.importClause && node.importClause.namedBindings && ts.isNamespaceImport(node.importClause.namedBindings)) { + let importType = 'NamespaceImport'; + if (node.importClause.namedBindings.name && ts.isIdentifier(node.importClause.namedBindings.name)) { + let importClauseName = node.importClause.namedBindings.name.text; + let importInfo = new ImportInfo(); + let nameBeforeAs = '*'; + const pos = LineColPosition.buildFromNode(node.importClause.namedBindings.name, sourceFile); + importInfo.build(importClauseName, importType, importFrom, pos, modifiers, nameBeforeAs); + importInfo.setTsSourceCode(tsSourceCode); + IRUtils.setComments(importInfo, node, sourceFile, arkFile.getScene().getOptions()); + importInfos.push(importInfo); + } + } + return importInfos; +} +function buildImportEqualsDeclarationNode(node, sourceFile, arkFile) { + const originTsPosition = LineColPosition.buildFromNode(node, sourceFile); + const tsSourceCode = node.getText(sourceFile); + let importInfos = []; + let importType = 'EqualsImport'; + let modifiers = 0; + if (node.modifiers) { + modifiers = buildModifiers(node); + } + if (node.moduleReference && + ts.isExternalModuleReference(node.moduleReference) && + node.moduleReference.expression && + ts.isStringLiteral(node.moduleReference.expression)) { + let importFrom = node.moduleReference.expression.text; + let importClauseName = node.name.text; + let importInfo = new ImportInfo(); + importInfo.build(importClauseName, importType, importFrom, originTsPosition, modifiers); + importInfo.setTsSourceCode(tsSourceCode); + IRUtils.setComments(importInfo, node, sourceFile, arkFile.getScene().getOptions()); + importInfos.push(importInfo); + } + return importInfos; +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$4 = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'ArkNamespaceBuilder'); +function buildArkNamespace(node, declaringInstance, ns, sourceFile) { + // modifiers + if (node.modifiers) { + ns.setModifiers(buildModifiers(node)); + ns.setDecorators(buildDecorators(node, sourceFile)); + } + if (declaringInstance instanceof ArkFile) { + ns.setDeclaringArkFile(declaringInstance); + } + else { + ns.setDeclaringArkNamespace(declaringInstance); + ns.setDeclaringArkFile(declaringInstance.getDeclaringArkFile()); + } + ns.setDeclaringInstance(declaringInstance); + const namespaceName = node.name.text; + const namespaceSignature = new NamespaceSignature(namespaceName, ns.getDeclaringArkFile().getFileSignature(), ns.getDeclaringArkNamespace()?.getSignature() || null); + ns.setSignature(namespaceSignature); + // TODO: whether needed? + ns.setCode(node.getText(sourceFile)); + // set line and column + const { line, character } = ts.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)); + ns.setLine(line + 1); + ns.setColumn(character + 1); + genDefaultArkClass$1(ns, node, sourceFile); + // build ns member + if (node.body) { + if (ts.isModuleBlock(node.body)) { + buildNamespaceMembers(node.body, ns, sourceFile); + } + // NamespaceDeclaration extends ModuleDeclaration + //TODO: Check + else if (ts.isModuleDeclaration(node.body)) { + logger$4.trace('This ModuleBody is an NamespaceDeclaration.'); + let childNs = new ArkNamespace(); + buildArkNamespace(node.body, ns, childNs, sourceFile); + ns.addNamespace(childNs); + } + else if (ts.isIdentifier(node.body)) { + logger$4.warn('ModuleBody is Identifier'); + } + else { + logger$4.warn('JSDocNamespaceDeclaration found.'); + } + } + else { + logger$4.warn('JSDocNamespaceDeclaration found.'); + } + IRUtils.setComments(ns, node, sourceFile, ns.getDeclaringArkFile().getScene().getOptions()); +} +// TODO: check and update +function buildNamespaceMembers(node, namespace, sourceFile) { + const statements = node.statements; + const nestedNamespaces = []; + statements.forEach(child => { + if (ts.isModuleDeclaration(child)) { + let childNs = new ArkNamespace(); + childNs.setDeclaringArkNamespace(namespace); + childNs.setDeclaringArkFile(namespace.getDeclaringArkFile()); + buildArkNamespace(child, namespace, childNs, sourceFile); + nestedNamespaces.push(childNs); + } + else if (ts.isClassDeclaration(child) || ts.isInterfaceDeclaration(child) || ts.isEnumDeclaration(child) || ts.isStructDeclaration(child)) { + let cls = new ArkClass(); + buildNormalArkClassFromArkNamespace(child, namespace, cls, sourceFile); + namespace.addArkClass(cls); + if (cls.isExported()) { + namespace.addExportInfo(buildExportInfo(cls, namespace.getDeclaringArkFile(), LineColPosition.buildFromNode(child, sourceFile))); + } + } + // TODO: Check + else if (ts.isMethodDeclaration(child)) { + logger$4.trace('This is a MethodDeclaration in ArkNamespace.'); + let mthd = new ArkMethod(); + buildArkMethodFromArkClass(child, namespace.getDefaultClass(), mthd, sourceFile); + if (mthd.isExported()) { + namespace.addExportInfo(buildExportInfo(mthd, namespace.getDeclaringArkFile(), LineColPosition.buildFromNode(child, sourceFile))); + } + } + else if (ts.isFunctionDeclaration(child)) { + let mthd = new ArkMethod(); + buildArkMethodFromArkClass(child, namespace.getDefaultClass(), mthd, sourceFile); + if (mthd.isExported()) { + namespace.addExportInfo(buildExportInfo(mthd, namespace.getDeclaringArkFile(), LineColPosition.buildFromNode(child, sourceFile))); + } + } + else if (ts.isExportDeclaration(child)) { + buildExportDeclaration(child, sourceFile, namespace.getDeclaringArkFile()).forEach(item => namespace.addExportInfo(item)); + } + else if (ts.isExportAssignment(child)) { + buildExportAssignment(child, sourceFile, namespace.getDeclaringArkFile()).forEach(item => namespace.addExportInfo(item)); + } + else if (ts.isVariableStatement(child) && isExported(child.modifiers)) { + buildExportVariableStatement(child, sourceFile, namespace.getDeclaringArkFile(), namespace).forEach(item => namespace.addExportInfo(item)); + } + else { + logger$4.trace('Child joined default method of arkFile: ', ts.SyntaxKind[child.kind]); + // join default method + } + }); + const nestedMergedNameSpaces = mergeNameSpaces(nestedNamespaces); + nestedMergedNameSpaces.forEach(nestedNameSpace => { + namespace.addNamespace(nestedNameSpace); + if (nestedNameSpace.isExport()) { + const linCol = new LineColPosition(nestedNameSpace.getLine(), nestedNameSpace.getColumn()); + namespace.addExportInfo(buildExportInfo(nestedNameSpace, namespace.getDeclaringArkFile(), linCol)); + } + }); +} +function genDefaultArkClass$1(ns, node, sourceFile) { + let defaultClass = new ArkClass(); + buildDefaultArkClassFromArkNamespace(ns, defaultClass, node, sourceFile); + ns.setDefaultClass(defaultClass); + ns.addArkClass(defaultClass); +} +function mergeNameSpaces(arkNamespaces) { + const namespaceMap = new Map(); + for (let i = 0; i < arkNamespaces.length; i++) { + const currNamespace = arkNamespaces[i]; + const currName = currNamespace.getName(); + if (namespaceMap.has(currName)) { + const prevNamespace = namespaceMap.get(currName); + const nestedPrevNamespaces = prevNamespace.getNamespaces(); + const nestedCurrNamespaces = currNamespace.getNamespaces(); + const nestedMergedNameSpaces = mergeNameSpaces([...nestedPrevNamespaces, ...nestedCurrNamespaces]); + nestedMergedNameSpaces.forEach(nestedNameSpace => { + prevNamespace.addNamespace(nestedNameSpace); + }); + const classes = currNamespace.getClasses(); + classes.forEach(cls => { + prevNamespace.addArkClass(cls); + }); + const preSourceCodes = prevNamespace.getCodes(); + const currSourceCodes = currNamespace.getCodes(); + prevNamespace.setCodes([...preSourceCodes, ...currSourceCodes]); + const prevLineColPairs = prevNamespace.getLineColPairs(); + const currLineColPairs = currNamespace.getLineColPairs(); + prevNamespace.setLineCols([...prevLineColPairs, ...currLineColPairs]); + } + else { + namespaceMap.set(currName, currNamespace); + } + } + return [...namespaceMap.values()]; +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$3 = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'ArkFileBuilder'); +/** + * Entry of building ArkFile instance + * + * @param arkFile + * @returns + */ +function buildArkFileFromFile(absoluteFilePath, projectDir, arkFile, projectName) { + arkFile.setFilePath(absoluteFilePath); + arkFile.setProjectDir(projectDir); + const fileSignature = new FileSignature(projectName, path$1.relative(projectDir, absoluteFilePath)); + arkFile.setFileSignature(fileSignature); + arkFile.setCode(fs$1.readFileSync(arkFile.getFilePath(), 'utf8')); + const sourceFile = ts.createSourceFile(arkFile.getName(), arkFile.getCode(), ts.ScriptTarget.Latest, true, undefined, ETS_COMPILER_OPTIONS); + genDefaultArkClass(arkFile, sourceFile); + buildArkFile(arkFile, sourceFile); +} +/** + * Building ArkFile instance + * + * @param arkFile + * @param astRoot + * @returns + */ +function buildArkFile(arkFile, astRoot) { + const statements = astRoot.statements; + const namespaces = []; + statements.forEach(child => { + if (ts.isModuleDeclaration(child)) { + let ns = new ArkNamespace(); + ns.setDeclaringArkFile(arkFile); + buildArkNamespace(child, arkFile, ns, astRoot); + namespaces.push(ns); + if (ns.isExported()) { + arkFile.addExportInfo(buildExportInfo(ns, arkFile, LineColPosition.buildFromNode(child, astRoot))); + } + } + else if (ts.isClassDeclaration(child) || ts.isInterfaceDeclaration(child) || ts.isEnumDeclaration(child) || ts.isStructDeclaration(child)) { + let cls = new ArkClass(); + buildNormalArkClassFromArkFile(child, arkFile, cls, astRoot); + arkFile.addArkClass(cls); + if (cls.isExported()) { + arkFile.addExportInfo(buildExportInfo(cls, arkFile, LineColPosition.buildFromNode(child, astRoot))); + } + } + // TODO: Check + else if (ts.isMethodDeclaration(child)) { + logger$3.trace('This is a MethodDeclaration in ArkFile.'); + let mthd = new ArkMethod(); + buildArkMethodFromArkClass(child, arkFile.getDefaultClass(), mthd, astRoot); + if (mthd.isExported()) { + arkFile.addExportInfo(buildExportInfo(mthd, arkFile, LineColPosition.buildFromNode(child, astRoot))); + } + } + else if (ts.isFunctionDeclaration(child)) { + let mthd = new ArkMethod(); + buildArkMethodFromArkClass(child, arkFile.getDefaultClass(), mthd, astRoot); + if (mthd.isExported()) { + arkFile.addExportInfo(buildExportInfo(mthd, arkFile, LineColPosition.buildFromNode(child, astRoot))); + } + } + else if (ts.isImportEqualsDeclaration(child) || ts.isImportDeclaration(child)) { + let importInfos = buildImportInfo(child, astRoot, arkFile); + importInfos?.forEach(element => { + element.setDeclaringArkFile(arkFile); + arkFile.addImportInfo(element); + }); + } + else if (ts.isExportDeclaration(child)) { + buildExportDeclaration(child, astRoot, arkFile).forEach(item => arkFile.addExportInfo(item)); + } + else if (ts.isExportAssignment(child)) { + buildExportAssignment(child, astRoot, arkFile).forEach(item => arkFile.addExportInfo(item)); + } + else if (ts.isVariableStatement(child) && isExported(child.modifiers)) { + buildExportVariableStatement(child, astRoot, arkFile).forEach(item => arkFile.addExportInfo(item)); + } + else if (ts.isTypeAliasDeclaration(child) && isExported(child.modifiers)) { + buildExportTypeAliasDeclaration(child, astRoot, arkFile).forEach(item => arkFile.addExportInfo(item)); + } + else if (ts.isExpressionStatement(child) && ts.isStringLiteral(child.expression)) { + child.expression.text.trim() === ARKTS_STATIC_MARK && arkFile.setLanguage(Language.ARKTS1_2); + } + else { + logger$3.trace('Child joined default method of arkFile: ', ts.SyntaxKind[child.kind]); + } + }); + const mergedNameSpaces = mergeNameSpaces(namespaces); + mergedNameSpaces.forEach(mergedNameSpace => { + arkFile.addNamespace(mergedNameSpace); + if (mergedNameSpace.isExport()) { + const linCol = new LineColPosition(mergedNameSpace.getLine(), mergedNameSpace.getColumn()); + arkFile.addExportInfo(buildExportInfo(mergedNameSpace, arkFile, linCol)); + } + }); +} +function genDefaultArkClass(arkFile, astRoot) { + let defaultClass = new ArkClass(); + buildDefaultArkClassFromArkFile(arkFile, defaultClass, astRoot); + arkFile.setDefaultClass(defaultClass); + arkFile.addArkClass(defaultClass); +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$2 = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'json5parser'); +function fetchDependenciesFromFile(filePath) { + if (!fs__namespace.existsSync(filePath)) { + return {}; + } + let configurationsText; + try { + configurationsText = fs__namespace.readFileSync(filePath, 'utf-8'); + } + catch (error) { + logger$2.error(`Error reading file: ${error}`); + return {}; + } + const file = parseJsonText(configurationsText); + return file; +} +function parseJsonText(text) { + let file; + try { + file = ts__namespace.parseJsonText('', text); + } + catch (error) { + logger$2.error(`Error parsing file: ${error}`); + return {}; + } + const rootObjectLiteralExpression = getRootObjectLiteral(file); + if (!rootObjectLiteralExpression) { + logger$2.error('The JSON5 file format is incorrect, rootObjectLiteralExpression is null.'); + return {}; + } + return parseObjectLiteralExpression(rootObjectLiteralExpression, file); +} +function getRootObjectLiteral(file) { + if (!file || !file.statements || !file.statements.length) { + logger$2.error('The JSON5 file format is incorrect, the root node statements is empty.'); + return undefined; + } + const expressionStatement = file.statements[0]; + if (expressionStatement.kind !== ts__namespace.SyntaxKind.ExpressionStatement) { + logger$2.error(`The JSON5 file format is incorrect, the first child node is not ExpressionStatement. kind: ${expressionStatement.kind}`); + return undefined; + } + const rootObjectLiteralExpression = expressionStatement.expression; + if (!rootObjectLiteralExpression) { + logger$2.error('The JSON5 file format is incorrect, the first child node is empty.'); + return undefined; + } + if (rootObjectLiteralExpression.kind === ts__namespace.SyntaxKind.ObjectLiteralExpression) { + return rootObjectLiteralExpression; + } + if (rootObjectLiteralExpression.kind === ts__namespace.SyntaxKind.ArrayLiteralExpression) { + const elements = rootObjectLiteralExpression.elements; + if (elements && elements.length && elements[0].kind === ts__namespace.SyntaxKind.ObjectLiteralExpression) { + return elements[0]; + } + logger$2.error('The JSON5 file format is incorrect, the node ArrayLiteralExpression first element is not ObjectLiteralExpression.'); + } + logger$2.error('The JSON5 file format is incorrect.'); + return undefined; +} +function parsePropertyInitializer(node, file) { + if (node.kind === ts__namespace.SyntaxKind.StringLiteral) { + return node.text; + } + else if (node.kind === ts__namespace.SyntaxKind.NumericLiteral) { + return node.text; + } + else if (node.kind === ts__namespace.SyntaxKind.PrefixUnaryExpression) { + return node.getText(file); + } + else if (node.kind === ts__namespace.SyntaxKind.ArrayLiteralExpression) { + return parseArrayLiteral(node, file); + } + else if (node.kind === ts__namespace.SyntaxKind.ObjectLiteralExpression) { + return parseObjectLiteralExpression(node, file); + } + else if (node.kind === ts__namespace.SyntaxKind.TrueKeyword) { + return true; + } + else if (node.kind === ts__namespace.SyntaxKind.FalseKeyword) { + return false; + } + return undefined; +} +function parseArrayLiteral(node, file) { + const res = []; + node.elements.forEach(n => { + res.push(parsePropertyInitializer(n, file)); + }); + return res; +} +function parseObjectLiteralExpression(ObjectLiteralExpression, file) { + const res = {}; + ObjectLiteralExpression.properties.forEach(node => { + const propNode = node; + const key = propNode.name.text; + const value = parsePropertyInitializer(propNode.initializer, file); + res[key] = value; + }); + return res; +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger$1 = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'Scene'); +var SceneBuildStage; +(function (SceneBuildStage) { + SceneBuildStage[SceneBuildStage["BUILD_INIT"] = 0] = "BUILD_INIT"; + SceneBuildStage[SceneBuildStage["CLASS_DONE"] = 1] = "CLASS_DONE"; + SceneBuildStage[SceneBuildStage["METHOD_DONE"] = 2] = "METHOD_DONE"; + SceneBuildStage[SceneBuildStage["CLASS_COLLECTED"] = 3] = "CLASS_COLLECTED"; + SceneBuildStage[SceneBuildStage["METHOD_COLLECTED"] = 4] = "METHOD_COLLECTED"; + SceneBuildStage[SceneBuildStage["SDK_INFERRED"] = 5] = "SDK_INFERRED"; + SceneBuildStage[SceneBuildStage["TYPE_INFERRED"] = 6] = "TYPE_INFERRED"; +})(SceneBuildStage || (SceneBuildStage = {})); +/** + * The Scene class includes everything in the analyzed project. + * We should be able to re-generate the project's code based on this class. + */ +class Scene { + projectName = ''; + projectFiles = []; + realProjectDir = ''; + moduleScenesMap = new Map(); + modulePath2NameMap = new Map(); + moduleSdkMap = new Map(); + projectSdkMap = new Map(); + // values that are visible in curr scope + visibleValue = new VisibleValue(); + // signature string to model + filesMap = new Map(); + namespacesMap = new Map(); + classesMap = new Map(); + methodsMap = new Map(); + // TODO: type of key should be signature object + sdkArkFilesMap = new Map(); + sdkGlobalMap = new Map(); + ohPkgContentMap = new Map(); + ohPkgFilePath = ''; + ohPkgContent = {}; + overRides = new Map(); + overRideDependencyMap = new Map(); + globalModule2PathMapping; + baseUrl; + buildStage = SceneBuildStage.BUILD_INIT; + fileLanguages = new Map(); + options; + indexPathArray = ['Index.ets', 'Index.ts', 'Index.d.ets', 'Index.d.ts', 'index.ets', 'index.ts', 'index.d.ets', 'index.d.ts']; + unhandledFilePaths = []; + unhandledSdkFilePaths = []; + routeNameMap = new Map(); //命名路由的路由表 + constructor() { } + getOptions() { + return this.options; + } + getOverRides() { + return this.overRides; + } + getOverRideDependencyMap() { + return this.overRideDependencyMap; + } + clear() { + this.projectFiles = []; + this.moduleScenesMap.clear(); + this.modulePath2NameMap.clear(); + this.moduleSdkMap.clear(); + this.projectSdkMap.clear(); + this.filesMap.clear(); + this.namespacesMap.clear(); + this.classesMap.clear(); + this.methodsMap.clear(); + this.sdkArkFilesMap.clear(); + this.sdkGlobalMap.clear(); + this.ohPkgContentMap.clear(); + this.ohPkgContent = {}; + } + getStage() { + return this.buildStage; + } + /** + * Build scene object according to the {@link SceneConfig}. This API implements 3 functions. + * First is to build scene object from {@link SceneConfig}, second is to generate {@link ArkFile}s, + * and the last is to collect project import infomation. + * @param sceneConfig - a sceneConfig object, which is usally defined by user or Json file. + * @example + * 1. Build Scene object from scene config + + ```typescript + // build config + const projectDir = ... ...; + const sceneConfig = new SceneConfig(); + sceneConfig.buildFromProjectDir(projectDir); + + // build scene + const scene = new Scene(); + scene.buildSceneFromProjectDir(sceneConfig); + ``` + */ + buildSceneFromProjectDir(sceneConfig) { + this.buildBasicInfo(sceneConfig); + this.genArkFiles(); + } + buildSceneFromFiles(sceneConfig) { + this.buildBasicInfo(sceneConfig); + this.buildOhPkgContentMap(); + initModulePathMap(this.ohPkgContentMap); + this.getFilesOrderByDependency(); + } + /** + * Set the basic information of the scene using a config, + * such as the project's name, real path and files. + * @param sceneConfig - the config used to set the basic information of scene. + */ + buildBasicInfo(sceneConfig) { + this.options = sceneConfig.getOptions(); + this.projectName = sceneConfig.getTargetProjectName(); + this.realProjectDir = fs$1.realpathSync(sceneConfig.getTargetProjectDirectory()); + this.projectFiles = sceneConfig.getProjectFiles(); + this.parseBuildProfile(); + this.parseOhPackage(); + let tsConfigFilePath; + if (this.options.tsconfig) { + tsConfigFilePath = path$1.join(sceneConfig.getTargetProjectDirectory(), this.options.tsconfig); + } + else { + tsConfigFilePath = path$1.join(sceneConfig.getTargetProjectDirectory(), TSCONFIG_JSON); + } + if (fs$1.existsSync(tsConfigFilePath)) { + const tsConfigObj = fetchDependenciesFromFile(tsConfigFilePath); + this.findTsConfigInfoDeeply(tsConfigObj, tsConfigFilePath); + } + else { + logger$1.warn('This project has no tsconfig.json!'); + } + // handle sdks + sceneConfig.getSdksObj()?.forEach(sdk => { + if (!sdk.moduleName) { + this.buildSdk(sdk.name, sdk.path); + this.projectSdkMap.set(sdk.name, sdk); + } + else { + let moduleSdks = this.moduleSdkMap.get(sdk.moduleName); + if (moduleSdks) { + moduleSdks.push(sdk); + } + else { + this.moduleSdkMap.set(sdk.moduleName, [sdk]); + } + } + }); + this.fileLanguages = sceneConfig.getFileLanguages(); + } + parseBuildProfile() { + const buildProfile = path$1.join(this.realProjectDir, BUILD_PROFILE_JSON5); + if (fs$1.existsSync(buildProfile)) { + let configurationsText; + try { + configurationsText = fs$1.readFileSync(buildProfile, 'utf-8'); + } + catch (error) { + logger$1.error(`Error reading file: ${error}`); + return; + } + const buildProfileJson = parseJsonText(configurationsText); + const modules = buildProfileJson.modules; + if (modules instanceof Array) { + modules.forEach(module => { + this.modulePath2NameMap.set(path$1.resolve(this.realProjectDir, path$1.join(module.srcPath)), module.name); + }); + } + } + else { + logger$1.warn('There is no build-profile.json5 for this project.'); + } + } + parseOhPackage() { + const OhPkgFilePath = path$1.join(this.realProjectDir, OH_PACKAGE_JSON5); + if (fs$1.existsSync(OhPkgFilePath)) { + this.ohPkgFilePath = OhPkgFilePath; + this.ohPkgContent = fetchDependenciesFromFile(this.ohPkgFilePath); + this.ohPkgContentMap.set(OhPkgFilePath, this.ohPkgContent); + if (this.ohPkgContent.overrides) { + let overRides = this.ohPkgContent.overrides; + for (const [key, value] of Object.entries(overRides)) { + this.overRides.set(key, value); + } + } + if (this.ohPkgContent.overrideDependencyMap) { + let globalOverRideDependencyMap = this.ohPkgContent.overrideDependencyMap; + for (const [key, value] of Object.entries(globalOverRideDependencyMap)) { + let globalDependency = fetchDependenciesFromFile(value); + this.overRideDependencyMap.set(key, globalDependency); + } + } + } + else { + logger$1.warn('This project has no oh-package.json5!'); + } + } + findTsConfigInfoDeeply(tsConfigObj, tsConfigFilePath) { + if (tsConfigObj.extends) { + const extTsConfigObj = fetchDependenciesFromFile(path$1.join(path$1.dirname(tsConfigFilePath), tsConfigObj.extends)); + this.findTsConfigInfoDeeply(extTsConfigObj, tsConfigFilePath); + if (!this.baseUrl && !this.globalModule2PathMapping) { + this.addTsConfigInfo(extTsConfigObj); + } + } + if (!this.baseUrl && !this.globalModule2PathMapping) { + this.addTsConfigInfo(tsConfigObj); + } + } + addTsConfigInfo(tsConfigObj) { + if (tsConfigObj.compilerOptions && tsConfigObj.compilerOptions.paths) { + const paths = tsConfigObj.compilerOptions.paths; + if (paths) { + this.globalModule2PathMapping = paths; + } + } + if (tsConfigObj.compilerOptions && tsConfigObj.compilerOptions.baseUrl) { + this.baseUrl = tsConfigObj.compilerOptions.baseUrl; + } + } + addDefaultConstructors() { + for (const file of this.getFiles()) { + for (const cls of ModelUtils.getAllClassesInFile(file)) { + buildDefaultConstructor(cls); + const constructor = cls.getMethodWithName(CONSTRUCTOR_NAME); + if (constructor !== null) { + addInitInConstructor(constructor); + } + } + } + } + buildAllMethodBody() { + this.buildStage = SceneBuildStage.CLASS_DONE; + const methods = []; + for (const file of this.getFiles()) { + for (const cls of file.getClasses()) { + for (const method of cls.getMethods(true)) { + methods.push(method); + } + } + } + for (const namespace of this.getNamespacesMap().values()) { + for (const cls of namespace.getClasses()) { + for (const method of cls.getMethods(true)) { + methods.push(method); + } + } + } + for (const method of methods) { + try { + method.buildBody(); + } + catch (error) { + logger$1.error('Error building body:', method.getSignature(), error); + } + finally { + method.freeBodyBuilder(); + } + } + this.buildStage = SceneBuildStage.METHOD_DONE; + } + genArkFiles() { + this.projectFiles.forEach(file => { + logger$1.trace('=== parse file:', file); + try { + const arkFile = new ArkFile(FileUtils.getFileLanguage(file, this.fileLanguages)); + arkFile.setScene(this); + buildArkFileFromFile(file, this.realProjectDir, arkFile, this.projectName); + this.filesMap.set(arkFile.getFileSignature().toMapKey(), arkFile); + } + catch (error) { + logger$1.error('Error parsing file:', file, error); + this.unhandledFilePaths.push(file); + return; + } + }); + this.buildAllMethodBody(); + this.addDefaultConstructors(); + } + getFilesOrderByDependency() { + for (const projectFile of this.projectFiles) { + this.getDependencyFilesDeeply(projectFile); + } + this.buildAllMethodBody(); + this.addDefaultConstructors(); + } + getDependencyFilesDeeply(projectFile) { + if (!this.options.supportFileExts.includes(path$1.extname(projectFile))) { + return; + } + const fileSignature = new FileSignature(this.getProjectName(), path$1.relative(this.getRealProjectDir(), projectFile)); + if (this.filesMap.has(fileSignature.toMapKey()) || this.isRepeatBuildFile(projectFile)) { + return; + } + try { + const arkFile = new ArkFile(FileUtils.getFileLanguage(projectFile, this.fileLanguages)); + arkFile.setScene(this); + buildArkFileFromFile(projectFile, this.getRealProjectDir(), arkFile, this.getProjectName()); + for (const [modulePath, moduleName] of this.modulePath2NameMap) { + if (arkFile.getFilePath().startsWith(modulePath)) { + this.addArkFile2ModuleScene(modulePath, moduleName, arkFile); + break; + } + } + this.filesMap.set(arkFile.getFileSignature().toMapKey(), arkFile); + const importInfos = arkFile.getImportInfos(); + const repeatFroms = []; + this.findDependencyFiles(importInfos, arkFile, repeatFroms); + const exportInfos = arkFile.getExportInfos(); + this.findDependencyFiles(exportInfos, arkFile, repeatFroms); + } + catch (error) { + logger$1.error('Error parsing file:', projectFile, error); + this.unhandledFilePaths.push(projectFile); + return; + } + } + isRepeatBuildFile(projectFile) { + for (const [key, file] of this.filesMap) { + if (key && file.getFilePath().toLowerCase() === projectFile.toLowerCase()) { + return true; + } + } + return false; + } + addArkFile2ModuleScene(modulePath, moduleName, arkFile) { + if (this.moduleScenesMap.has(moduleName)) { + let curModuleScene = this.moduleScenesMap.get(moduleName); + if (curModuleScene) { + curModuleScene.addArkFile(arkFile); + arkFile.setModuleScene(curModuleScene); + } + } + else { + let moduleScene = new ModuleScene(this); + moduleScene.ModuleScenePartiallyBuilder(moduleName, modulePath); + moduleScene.addArkFile(arkFile); + this.moduleScenesMap.set(moduleName, moduleScene); + arkFile.setModuleScene(moduleScene); + } + } + findDependencyFiles(importOrExportInfos, arkFile, repeatFroms) { + for (const importOrExportInfo of importOrExportInfos) { + const from = importOrExportInfo.getFrom(); + if (from && !repeatFroms.includes(from)) { + console.log("[debug] :", from + "-----> ", arkFile.getName()); + this.parseFrom(from, arkFile); + repeatFroms.push(from); + } + } + } + parseFrom(from, arkFile) { + if (/^@[a-z|\-]+?\/?/.test(from)) { + for (const [ohPkgContentPath, ohPkgContent] of this.ohPkgContentMap) { + this.findDependenciesByOhPkg(ohPkgContentPath, ohPkgContent, from, arkFile); + } + } + else if (/^([^@]*\/)([^\/]*)$/.test(from) || /^[\.\./|\.\.]+$/.test(from)) { + this.findRelativeDependenciesByOhPkg(from, arkFile); + } + else if (/^[@a-zA-Z0-9]+(\/[a-zA-Z0-9]+)*$/.test(from)) { + this.findDependenciesByTsConfig(from, arkFile); + } + } + findDependenciesByTsConfig(from, arkFile) { + if (this.globalModule2PathMapping) { + const paths = this.globalModule2PathMapping; + Object.keys(paths).forEach(key => this.parseTsConfigParms(paths, key, from, arkFile)); + } + } + parseTsConfigParms(paths, key, from, arkFile) { + const module2pathMapping = paths[key]; + if (key.includes(ALL)) { + this.processFuzzyMapping(key, from, module2pathMapping, arkFile); + } + else if (from.startsWith(key)) { + let tail = from.substring(key.length, from.length); + module2pathMapping.forEach(pathMapping => { + let originPath = path$1.join(this.getRealProjectDir(), pathMapping, tail); + if (this.baseUrl) { + originPath = path$1.resolve(this.baseUrl, originPath); + } + this.findDependenciesByRule(originPath, arkFile); + }); + } + } + processFuzzyMapping(key, from, module2pathMapping, arkFile) { + key = key.substring(0, key.indexOf(ALL) - 1); + if (from.substring(0, key.indexOf(ALL) - 1) === key) { + let tail = from.substring(key.indexOf(ALL) - 1, from.length); + module2pathMapping.forEach(pathMapping => { + pathMapping = pathMapping.substring(0, pathMapping.indexOf(ALL) - 1); + let originPath = path$1.join(this.getRealProjectDir(), pathMapping, tail); + if (this.baseUrl) { + originPath = path$1.join(this.baseUrl, originPath); + } + this.findDependenciesByRule(originPath, arkFile); + }); + } + } + findDependenciesByRule(originPath, arkFile) { + if (!this.findFilesByPathArray(originPath, this.indexPathArray, arkFile) && + !this.findFilesByExtNameArray(originPath, this.options.supportFileExts, arkFile)) { + logger$1.trace(originPath + 'module mapperInfo is not found!'); + } + } + findFilesByPathArray(originPath, pathArray, arkFile) { + for (const pathInfo of pathArray) { + const curPath = path$1.join(originPath, pathInfo); + if (fs$1.existsSync(curPath) && !this.isRepeatBuildFile(curPath)) { + this.addFileNode2DependencyGrap(curPath, arkFile); + return true; + } + } + return false; + } + findFilesByExtNameArray(originPath, pathArray, arkFile) { + for (const pathInfo of pathArray) { + const curPath = originPath + pathInfo; + if (fs$1.existsSync(curPath) && !this.isRepeatBuildFile(curPath)) { + this.addFileNode2DependencyGrap(curPath, arkFile); + return true; + } + } + return false; + } + findRelativeDependenciesByOhPkg(from, arkFile) { + //relative path ../from ./from + //order + //1. ../from/oh-package.json5 -> [[name]] -> overRides/overRideDependencyMap? -> + //[[main]] -> file path ->dependencies(priority)+devDependencies? dynamicDependencies(not support) -> + //key overRides/overRideDependencyMap? + //2. ../from/index.ets(ts) + //3. ../from/index.d.ets(ts) + //4. ../from.ets(ts) + //5. ../from.d.ets(ts) + //2.3.4.5 random order + let originPath = this.getOriginPath(from, arkFile); + if (fs$1.existsSync(path$1.join(originPath, OH_PACKAGE_JSON5))) { + for (const [ohPkgContentPath, ohPkgContent] of this.ohPkgContentMap) { + this.findDependenciesByOhPkg(ohPkgContentPath, ohPkgContent, from, arkFile); + } + } + this.findDependenciesByRule(originPath, arkFile); + } + findDependenciesByOhPkg(ohPkgContentPath, ohPkgContentInfo, from, arkFile) { + //module name @ohos/from + const ohPkgContent = ohPkgContentInfo; + //module main name is must be + if (ohPkgContent && ohPkgContent.name && from.startsWith(ohPkgContent.name.toString())) { + let originPath = ohPkgContentPath.toString().replace(OH_PACKAGE_JSON5, ''); + if (ohPkgContent.main) { + originPath = path$1.join(ohPkgContentPath.toString().replace(OH_PACKAGE_JSON5, ''), ohPkgContent.main.toString()); + if (ohPkgContent.dependencies) { + this.getDependenciesMapping(ohPkgContent.dependencies, ohPkgContentPath, from, arkFile); + } + else if (ohPkgContent.devDependencies) { + this.getDependenciesMapping(ohPkgContent.devDependencies, ohPkgContentPath, from, arkFile); + } + else if (ohPkgContent.dynamicDependencies) ; + this.addFileNode2DependencyGrap(originPath, arkFile); + } + if (!this.findFilesByPathArray(originPath, this.indexPathArray, arkFile)) { + logger$1.trace(originPath + 'module mapperInfo is not found!'); + } + } + } + getDependenciesMapping(dependencies, ohPkgContentPath, from, arkFile) { + for (let [moduleName, modulePath] of Object.entries(dependencies)) { + logger$1.debug('dependencies:' + moduleName); + if (modulePath.startsWith('file:')) { + modulePath = modulePath.replace(/^file:/, ''); + } + const innerOhpackagePath = path$1.join(ohPkgContentPath.replace(OH_PACKAGE_JSON5, ''), modulePath.toString(), OH_PACKAGE_JSON5); + if (!this.ohPkgContentMap.has(innerOhpackagePath)) { + const innerModuleOhPkgContent = fetchDependenciesFromFile(innerOhpackagePath); + this.findDependenciesByOhPkg(innerOhpackagePath, innerModuleOhPkgContent, from, arkFile); + } + } + } + getOriginPath(from, arkFile) { + const parentPath = /^\.{1,2}\//.test(from) ? path$1.dirname(arkFile.getFilePath()) : arkFile.getProjectDir(); + return path$1.resolve(parentPath, from); + } + addFileNode2DependencyGrap(filePath, arkFile) { + this.getDependencyFilesDeeply(filePath); + this.filesMap.set(arkFile.getFileSignature().toMapKey(), arkFile); + } + buildSdk(sdkName, sdkPath) { + console.log("sdkName: ", sdkName, " sdkPath: ", sdkPath); + const allFiles = getAllFiles(sdkPath, this.options.supportFileExts, this.options.ignoreFileNames); + allFiles.forEach(file => { + logger$1.trace('=== parse sdk file:', file); + //console.log("=== parse sdk file: ",file); + try { + const arkFile = new ArkFile(FileUtils.getFileLanguage(file, this.fileLanguages)); + arkFile.setScene(this); + buildArkFileFromFile(file, path$1.normalize(sdkPath), arkFile, sdkName); + ModelUtils.getAllClassesInFile(arkFile).forEach(cls => { + cls.getDefaultArkMethod()?.buildBody(); + cls.getDefaultArkMethod()?.freeBodyBuilder(); + }); + const fileSig = arkFile.getFileSignature().toMapKey(); + this.sdkArkFilesMap.set(fileSig, arkFile); + SdkUtils.buildSdkImportMap(arkFile); + SdkUtils.buildGlobalMap(arkFile, this.sdkGlobalMap); + } + catch (error) { + logger$1.error('Error parsing file:', file, error); + this.unhandledSdkFilePaths.push(file); + return; + } + }); + } + /** + * Build the scene for harmony project. It resolves the file path of the project first, and then fetches + * dependencies from this file. Next, build a `ModuleScene` for this project to generate {@link ArkFile}. Finally, + * it build bodies of all methods, generate extended classes, and add DefaultConstructors. + */ + buildScene4HarmonyProject() { + this.buildOhPkgContentMap(); + this.modulePath2NameMap.forEach((value, key) => { + let moduleScene = new ModuleScene(this); + moduleScene.ModuleSceneBuilder(value, key, this.options.supportFileExts); + this.moduleScenesMap.set(value, moduleScene); + }); + initModulePathMap(this.ohPkgContentMap); + this.buildAllMethodBody(); + this.addDefaultConstructors(); + this.getModuleRouteMap(); + this.buildRouteNameMap(); + // this.buildWindowViewTree() + } + buildOhPkgContentMap() { + this.modulePath2NameMap.forEach((value, key) => { + const moduleOhPkgFilePath = path$1.resolve(key, OH_PACKAGE_JSON5); + if (fs$1.existsSync(moduleOhPkgFilePath)) { + const moduleOhPkgContent = fetchDependenciesFromFile(moduleOhPkgFilePath); + this.ohPkgContentMap.set(moduleOhPkgFilePath, moduleOhPkgContent); + } + }); + } + buildModuleScene(moduleName, modulePath, supportFileExts) { + if (this.moduleScenesMap.get(moduleName)) { + return; + } + // get oh-package.json5 + const moduleOhPkgFilePath = path$1.resolve(this.realProjectDir, path$1.join(modulePath, OH_PACKAGE_JSON5)); + if (fs$1.existsSync(moduleOhPkgFilePath)) { + const moduleOhPkgContent = fetchDependenciesFromFile(moduleOhPkgFilePath); + this.ohPkgContentMap.set(moduleOhPkgFilePath, moduleOhPkgContent); + } + else { + logger$1.warn('Module: ', moduleName, 'has no oh-package.json5.'); + } + // parse moduleOhPkgContent, get dependencies and build dependent module + const moduleOhPkgContent = this.ohPkgContentMap.get(moduleOhPkgFilePath); + if (moduleOhPkgContent) { + if (moduleOhPkgContent.dependencies instanceof Object) { + this.processModuleOhPkgContent(moduleOhPkgContent.dependencies, moduleOhPkgFilePath, supportFileExts); + } + } + let moduleScene = new ModuleScene(this); + moduleScene.ModuleSceneBuilder(moduleName, modulePath, supportFileExts); + this.moduleScenesMap.set(moduleName, moduleScene); + this.buildAllMethodBody(); + } + processModuleOhPkgContent(dependencies, moduleOhPkgFilePath, supportFileExts) { + Object.entries(dependencies).forEach(([k, v]) => { + const pattern = new RegExp('^(\\.\\.\\/\|\\.\\/)'); + if (typeof v === 'string') { + let dependencyModulePath = ''; + if (pattern.test(v)) { + dependencyModulePath = path$1.join(moduleOhPkgFilePath, v); + } + else if (v.startsWith('file:')) { + const dependencyFilePath = path$1.join(moduleOhPkgFilePath, v.replace(/^file:/, '')); + const dependencyOhPkgPath = getFileRecursively(path$1.dirname(dependencyFilePath), OH_PACKAGE_JSON5); + dependencyModulePath = path$1.dirname(dependencyOhPkgPath); + } + const dependencyModuleName = this.modulePath2NameMap.get(dependencyModulePath); + if (dependencyModuleName) { + this.buildModuleScene(dependencyModuleName, dependencyModulePath, supportFileExts); + } + } + }); + } + /** + * Get the absolute path of current project. + * @returns The real project's directiory. + * @example + * 1. get real project directory, such as: + ```typescript + let projectDir = projectScene.getRealProjectDir(); + ``` + */ + getRealProjectDir() { + return this.realProjectDir; + } + /** + * Returns the **string** name of the project. + * @returns The name of the project. + */ + getProjectName() { + return this.projectName; + } + getProjectFiles() { + return this.projectFiles; + } + getSdkGlobal(globalName) { + return this.sdkGlobalMap.get(globalName) || null; + } + /** + * Returns the file based on its signature. + * If no file can be found according to the input signature, **null** will be returned. + * A typical {@link ArkFile} contains: file's name (i.e., its relative path), project's name, + * project's dir, file's signature etc. + * @param fileSignature - the signature of file. + * @returns a file defined by ArkAnalyzer. **null** will be returned if no file could be found. + * @example + * 1. get ArkFile based on file signature. + + ```typescript + if (...) { + const fromSignature = new FileSignature(); + fromSignature.setProjectName(im.getDeclaringArkFile().getProjectName()); + fromSignature.setFileName(fileName); + return scene.getFile(fromSignature); + } + ``` + */ + getFile(fileSignature) { + if (this.projectName === fileSignature.getProjectName()) { + return this.filesMap.get(fileSignature.toMapKey()) || null; + } + else { + return this.sdkArkFilesMap.get(fileSignature.toMapKey()) || null; + } + } + /* + * Returns the absolute file paths that cannot be handled currently. + */ + getUnhandledFilePaths() { + return this.unhandledFilePaths; + } + /* + * Returns the absolute sdk file paths that cannot be handled currently. + */ + getUnhandledSdkFilePaths() { + return this.unhandledSdkFilePaths; + } + setFile(file) { + this.filesMap.set(file.getFileSignature().toMapKey(), file); + } + hasSdkFile(fileSignature) { + return this.sdkArkFilesMap.has(fileSignature.toMapKey()); + } + /** + * Get files of a {@link Scene}. Generally, a project includes several ets/ts files that define the different + * class. We need to generate {@link ArkFile} objects from these ets/ts files. + * @returns The array of {@link ArkFile} from `scene.filesMap.values()`. + * @example + * 1. In inferSimpleTypes() to check arkClass and arkMethod. + * ```typescript + * public inferSimpleTypes(): void { + * for (let arkFile of this.getFiles()) { + * for (let arkClass of arkFile.getClasses()) { + * for (let arkMethod of arkClass.getMethods()) { + * // ... ...; + * } + * } + * } + * } + * ``` + * 2. To iterate each method + * ```typescript + * for (const file of this.getFiles()) { + * for (const cls of file.getClasses()) { + * for (const method of cls.getMethods()) { + * // ... ... + * } + * } + * } + *``` + */ + getFiles() { + return Array.from(this.filesMap.values()); + } + getFileLanguages() { + return this.fileLanguages; + } + getSdkArkFiles() { + return Array.from(this.sdkArkFilesMap.values()); + } + getModuleSdkMap() { + return this.moduleSdkMap; + } + getProjectSdkMap() { + return this.projectSdkMap; + } + getNamespace(namespaceSignature) { + const isProject = this.projectName === namespaceSignature.getDeclaringFileSignature().getProjectName(); + let namespace; + if (isProject) { + namespace = this.namespacesMap.get(namespaceSignature.toMapKey()); + } + if (namespace) { + return namespace; + } + namespace = this.getNamespaceBySignature(namespaceSignature); + if (isProject && namespace) { + this.namespacesMap.set(namespaceSignature.toMapKey(), namespace); + } + return namespace || null; + } + getNamespaceBySignature(signature) { + const parentSignature = signature.getDeclaringNamespaceSignature(); + if (parentSignature) { + const parentNamespace = this.getNamespaceBySignature(parentSignature); + return parentNamespace?.getNamespace(signature) || null; + } + else { + const arkFile = this.getFile(signature.getDeclaringFileSignature()); + return arkFile?.getNamespace(signature) || null; + } + } + getNamespacesMap() { + if (this.buildStage === SceneBuildStage.CLASS_DONE) { + for (const file of this.getFiles()) { + ModelUtils.getAllNamespacesInFile(file).forEach(namespace => { + this.namespacesMap.set(namespace.getNamespaceSignature().toMapKey(), namespace); + }); + } + } + return this.namespacesMap; + } + getNamespaces() { + return Array.from(this.getNamespacesMap().values()); + } + /** + * Returns the class according to the input class signature. + * @param classSignature - signature of the class to be obtained. + * @returns A class. + */ + getClass(classSignature) { + const isProject = this.projectName === classSignature.getDeclaringFileSignature().getProjectName(); + let arkClass; + if (isProject) { + arkClass = this.classesMap.get(classSignature.toMapKey()); + } + if (arkClass) { + return arkClass; + } + const namespaceSignature = classSignature.getDeclaringNamespaceSignature(); + if (namespaceSignature) { + arkClass = this.getNamespaceBySignature(namespaceSignature)?.getClass(classSignature) || null; + } + else { + const arkFile = this.getFile(classSignature.getDeclaringFileSignature()); + arkClass = arkFile?.getClass(classSignature); + } + if (isProject && arkClass) { + this.classesMap.set(classSignature.toMapKey(), arkClass); + } + return arkClass || null; + } + getClassesMap(refresh) { + if (refresh || this.buildStage === SceneBuildStage.METHOD_DONE) { + this.classesMap.clear(); + for (const file of this.getFiles()) { + for (const cls of file.getClasses()) { + this.classesMap.set(cls.getSignature().toMapKey(), cls); + } + } + for (const namespace of this.getNamespacesMap().values()) { + for (const cls of namespace.getClasses()) { + this.classesMap.set(cls.getSignature().toMapKey(), cls); + } + } + if (this.buildStage < SceneBuildStage.CLASS_COLLECTED) { + this.buildStage = SceneBuildStage.CLASS_COLLECTED; + } + } + return this.classesMap; + } + getClasses() { + return Array.from(this.getClassesMap().values()); + } + getMethod(methodSignature, refresh) { + const isProject = this.projectName === methodSignature.getDeclaringClassSignature().getDeclaringFileSignature().getProjectName(); + let arkMethod; + if (isProject) { + arkMethod = this.methodsMap.get(methodSignature.toMapKey()); + } + if (arkMethod) { + return arkMethod; + } + arkMethod = this.getClass(methodSignature.getDeclaringClassSignature())?.getMethod(methodSignature); + if (isProject && arkMethod) { + this.methodsMap.set(methodSignature.toMapKey(), arkMethod); + } + return arkMethod || null; + } + getMethodsMap(refresh) { + if (refresh || (this.buildStage >= SceneBuildStage.METHOD_DONE && this.buildStage < SceneBuildStage.METHOD_COLLECTED)) { + this.methodsMap.clear(); + for (const cls of this.getClassesMap(refresh).values()) { + for (const method of cls.getMethods(true)) { + this.methodsMap.set(method.getSignature().toMapKey(), method); + } + } + if (this.buildStage < SceneBuildStage.METHOD_COLLECTED) { + this.buildStage = SceneBuildStage.METHOD_COLLECTED; + } + } + return this.methodsMap; + } + /** + * Returns the method associated with the method signature. + * If no method is associated with this signature, **null** will be returned. + * An {@link ArkMethod} includes: + * - Name: the **string** name of method. + * - Code: the **string** code of the method. + * - Line: a **number** indicating the line location, initialized as -1. + * - Column: a **number** indicating the column location, initialized as -1. + * - Parameters & Types of parameters: the parameters of method and their types. + * - View tree: the view tree of the method. + * - ... + * + * @param methodSignature - the signature of method. + * @returns The method associated with the method signature. + * @example + * 1. get method from getMethod. + + ```typescript + const methodSignatures = this.CHA.resolveCall(xxx, yyy); + for (const methodSignature of methodSignatures) { + const method = this.scene.getMethod(methodSignature); + ... ... + } + ``` + */ + getMethods() { + return Array.from(this.getMethodsMap().values()); + } + addToMethodsMap(method) { + this.methodsMap.set(method.getSignature().toMapKey(), method); + } + removeMethod(method) { + return this.methodsMap.delete(method.getSignature().toMapKey()); + } + removeClass(arkClass) { + return this.classesMap.delete(arkClass.getSignature().toMapKey()); + } + removeNamespace(namespace) { + return this.namespacesMap.delete(namespace.getSignature().toMapKey()); + } + removeFile(file) { + return this.filesMap.delete(file.getFileSignature().toMapKey()); + } + hasMainMethod() { + return false; + } + //Get the set of entry points that are used to build the call graph. + getEntryPoints() { + return []; + } + /** get values that is visible in curr scope */ + getVisibleValue() { + return this.visibleValue; + } + getOhPkgContent() { + return this.ohPkgContent; + } + getOhPkgContentMap() { + return this.ohPkgContentMap; + } + getOhPkgFilePath() { + return this.ohPkgFilePath; + } + makeCallGraphCHA(entryPoints) { + let callGraph = new CallGraph(this); + let callGraphBuilder = new CallGraphBuilder(callGraph, this); + callGraphBuilder.buildClassHierarchyCallGraph(entryPoints); + return callGraph; + } + makeCallGraphRTA(entryPoints) { + let callGraph = new CallGraph(this); + let callGraphBuilder = new CallGraphBuilder(callGraph, this); + callGraphBuilder.buildRapidTypeCallGraph(entryPoints); + return callGraph; + } + /** + * Infer type for each non-default method. It infers the type of each field/local/reference. + * For example, the statement `let b = 5;`, the type of local `b` is `NumberType`; and for the statement `let s = + * 'hello';`, the type of local `s` is `StringType`. The detailed types are defined in the Type.ts file. + * @example + * 1. Infer the type of each class field and method field. + ```typescript + const scene = new Scene(); + scene.buildSceneFromProjectDir(sceneConfig); + scene.inferTypes(); + ``` + */ + inferTypes() { + if (this.buildStage < SceneBuildStage.SDK_INFERRED) { + this.sdkArkFilesMap.forEach(file => { + try { + IRInference.inferFile(file); + } + catch (error) { + logger$1.error('Error inferring types of sdk file:', file.getFileSignature(), error); + } + }); + this.buildStage = SceneBuildStage.SDK_INFERRED; + } + this.filesMap.forEach(file => { + try { + IRInference.inferFile(file); + } + catch (error) { + logger$1.error('Error inferring types of project file:', file.getFileSignature(), error); + } + }); + if (this.buildStage < SceneBuildStage.TYPE_INFERRED) { + this.getMethodsMap(true); + this.buildStage = SceneBuildStage.TYPE_INFERRED; + } + } + /** + * Iterate all assignment statements in methods, + * and set the type of left operand based on the type of right operand + * if the left operand is a local variable as well as an unknown. + * @Deprecated + * @example + * 1. Infer simple type when scene building. + + ```typescript + let scene = new Scene(); + scene.buildSceneFromProjectDir(config); + scene.inferSimpleTypes(); + ``` + */ + inferSimpleTypes() { + for (let arkFile of this.getFiles()) { + for (let arkClass of arkFile.getClasses()) { + for (let arkMethod of arkClass.getMethods()) { + TypeInference.inferSimpleTypeInMethod(arkMethod); + } + } + } + } + addNSClasses(namespaceStack, finalNamespaces, classMap, parentMap) { + while (namespaceStack.length > 0) { + const ns = namespaceStack.shift(); + const nsClass = []; + for (const arkClass of ns.getClasses()) { + nsClass.push(arkClass); + } + classMap.set(ns.getNamespaceSignature(), nsClass); + if (ns.getNamespaces().length === 0) { + finalNamespaces.push(ns); + } + else { + for (const nsns of ns.getNamespaces()) { + namespaceStack.push(nsns); + parentMap.set(nsns, ns); + } + } + } + } + addNSExportedClasses(finalNamespaces, classMap, parentMap) { + while (finalNamespaces.length > 0) { + const finalNS = finalNamespaces.shift(); + const exportClass = []; + for (const arkClass of finalNS.getClasses()) { + if (arkClass.isExported()) { + exportClass.push(arkClass); + } + } + const parent = parentMap.get(finalNS); + if (parent instanceof ArkNamespace) { + classMap.get(parent.getNamespaceSignature())?.push(...exportClass); + } + else if (parent instanceof ArkFile) { + classMap.get(parent.getFileSignature())?.push(...exportClass); + } + let p = finalNS; + while (!(parentMap.get(p) instanceof ArkFile) && p.isExported()) { + const grandParent = parentMap.get(parentMap.get(p)); + if (grandParent instanceof ArkNamespace) { + classMap.get(grandParent.getNamespaceSignature())?.push(...exportClass); + p = parentMap.get(p); + } + else if (grandParent instanceof ArkFile) { + classMap.get(grandParent.getFileSignature())?.push(...exportClass); + break; + } + } + if (parent instanceof ArkNamespace && !finalNamespaces.includes(parent)) { + finalNamespaces.push(parent); + } + } + } + addFileImportedClasses(file, classMap) { + const importClasses = []; + const importNameSpaces = []; + for (const importInfo of file.getImportInfos()) { + const importClass = ModelUtils.getClassInImportInfoWithName(importInfo.getImportClauseName(), file); + if (importClass && !importClasses.includes(importClass)) { + importClasses.push(importClass); + continue; + } + const importNameSpace = ModelUtils.getNamespaceInImportInfoWithName(importInfo.getImportClauseName(), file); + if (importNameSpace && !importNameSpaces.includes(importNameSpace)) { + try { + // 遗留问题:只统计了项目文件的namespace,没统计sdk文件内部的引入 + const importNameSpaceClasses = classMap.get(importNameSpace.getNamespaceSignature()); + importClasses.push(...importNameSpaceClasses.filter(c => !importClasses.includes(c) && c.getName() !== DEFAULT_ARK_CLASS_NAME)); + } + catch { } + } + } + const fileClasses = classMap.get(file.getFileSignature()); + fileClasses.push(...importClasses.filter(c => !fileClasses.includes(c))); + // 子节点加上父节点的class + const namespaceStack = [...file.getNamespaces()]; + for (const ns of namespaceStack) { + const nsClasses = classMap.get(ns.getNamespaceSignature()); + nsClasses.push(...fileClasses.filter(c => !nsClasses.includes(c) && c.getName() !== DEFAULT_ARK_CLASS_NAME)); + } + while (namespaceStack.length > 0) { + const ns = namespaceStack.shift(); + const nsClasses = classMap.get(ns.getNamespaceSignature()); + for (const nsns of ns.getNamespaces()) { + const nsnsClasses = classMap.get(nsns.getNamespaceSignature()); + nsnsClasses.push(...nsClasses.filter(c => !nsnsClasses.includes(c) && c.getName() !== DEFAULT_ARK_CLASS_NAME)); + namespaceStack.push(nsns); + } + } + } + getClassMap() { + const classMap = new Map(); + for (const file of this.getFiles()) { + const fileClass = []; + const namespaceStack = []; + const parentMap = new Map(); + const finalNamespaces = []; + for (const arkClass of file.getClasses()) { + fileClass.push(arkClass); + } + for (const ns of file.getNamespaces()) { + namespaceStack.push(ns); + parentMap.set(ns, file); + } + classMap.set(file.getFileSignature(), fileClass); + // 第一轮遍历,加上每个namespace自己的class + this.addNSClasses(namespaceStack, finalNamespaces, classMap, parentMap); + // 第二轮遍历,父节点加上子节点的export的class + this.addNSExportedClasses(finalNamespaces, classMap, parentMap); + } + for (const file of this.getFiles()) { + // 文件加上import的class,包括ns的 + this.addFileImportedClasses(file, classMap); + } + return classMap; + } + addNSLocals(namespaceStack, finalNamespaces, parentMap, globalVariableMap) { + while (namespaceStack.length > 0) { + const ns = namespaceStack.shift(); + const nsGlobalLocals = []; + ns + .getDefaultClass() + .getDefaultArkMethod() + .getBody() + ?.getLocals() + .forEach(local => { + if (local.getDeclaringStmt() && local.getName() !== 'this' && local.getName()[0] !== '$') { + nsGlobalLocals.push(local); + } + }); + globalVariableMap.set(ns.getNamespaceSignature(), nsGlobalLocals); + if (ns.getNamespaces().length === 0) { + finalNamespaces.push(ns); + } + else { + for (const nsns of ns.getNamespaces()) { + namespaceStack.push(nsns); + parentMap.set(nsns, ns); + } + } + } + } + addNSExportedLocals(finalNamespaces, globalVariableMap, parentMap) { + while (finalNamespaces.length > 0) { + const finalNS = finalNamespaces.shift(); + const exportLocal = []; + for (const exportInfo of finalNS.getExportInfos()) { + if (exportInfo.getExportClauseType() === ExportType.LOCAL && exportInfo.getArkExport()) { + exportLocal.push(exportInfo.getArkExport()); + } + } + const parent = parentMap.get(finalNS); + if (parent instanceof ArkNamespace) { + globalVariableMap.get(parent.getNamespaceSignature())?.push(...exportLocal); + } + else if (parent instanceof ArkFile) { + globalVariableMap.get(parent.getFileSignature())?.push(...exportLocal); + } + let p = finalNS; + while (!(parentMap.get(p) instanceof ArkFile) && p.isExported()) { + const grandParent = parentMap.get(parentMap.get(p)); + if (grandParent instanceof ArkNamespace) { + globalVariableMap.get(grandParent.getNamespaceSignature())?.push(...exportLocal); + p = parentMap.get(p); + } + else if (grandParent instanceof ArkFile) { + globalVariableMap.get(grandParent.getFileSignature())?.push(...exportLocal); + break; + } + } + if (parent instanceof ArkNamespace && !finalNamespaces.includes(parent)) { + finalNamespaces.push(parent); + } + } + } + addFileImportLocals(file, globalVariableMap) { + const importLocals = []; + const importNameSpaces = []; + for (const importInfo of file.getImportInfos()) { + const importLocal = ModelUtils.getLocalInImportInfoWithName(importInfo.getImportClauseName(), file); + if (importLocal && !importLocals.includes(importLocal)) { + importLocals.push(importLocal); + } + const importNameSpace = ModelUtils.getNamespaceInImportInfoWithName(importInfo.getImportClauseName(), file); + if (importNameSpace && !importNameSpaces.includes(importNameSpace)) { + try { + // 遗留问题:只统计了项目文件,没统计sdk文件内部的引入 + const importNameSpaceClasses = globalVariableMap.get(importNameSpace.getNamespaceSignature()); + importLocals.push(...importNameSpaceClasses.filter(c => !importLocals.includes(c) && c.getName() !== DEFAULT_ARK_CLASS_NAME)); + } + catch { } + } + } + const fileLocals = globalVariableMap.get(file.getFileSignature()); + fileLocals.push(...importLocals.filter(c => !fileLocals.includes(c))); + // 子节点加上父节点的local + const namespaceStack = [...file.getNamespaces()]; + for (const ns of namespaceStack) { + const nsLocals = globalVariableMap.get(ns.getNamespaceSignature()); + const nsLocalNameSet = new Set(nsLocals.map(item => item.getName())); + for (const local of fileLocals) { + if (!nsLocalNameSet.has(local.getName())) { + nsLocals.push(local); + } + } + } + while (namespaceStack.length > 0) { + const ns = namespaceStack.shift(); + const nsLocals = globalVariableMap.get(ns.getNamespaceSignature()); + for (const nsns of ns.getNamespaces()) { + this.handleNestedNSLocals(nsns, nsLocals, globalVariableMap); + namespaceStack.push(nsns); + } + } + } + handleNestedNSLocals(nsns, nsLocals, globalVariableMap) { + const nsnsLocals = globalVariableMap.get(nsns.getNamespaceSignature()); + const nsnsLocalNameSet = new Set(nsnsLocals.map(item => item.getName())); + for (const local of nsLocals) { + if (!nsnsLocalNameSet.has(local.getName())) { + nsnsLocals.push(local); + } + } + } + getGlobalVariableMap() { + const globalVariableMap = new Map(); + for (const file of this.getFiles()) { + const namespaceStack = []; + const parentMap = new Map(); + const finalNamespaces = []; + const globalLocals = []; + file + .getDefaultClass() + ?.getDefaultArkMethod() + .getBody() + ?.getLocals() + .forEach(local => { + if (local.getDeclaringStmt() && local.getName() !== 'this' && local.getName()[0] !== '$') { + globalLocals.push(local); + } + }); + globalVariableMap.set(file.getFileSignature(), globalLocals); + for (const ns of file.getNamespaces()) { + namespaceStack.push(ns); + parentMap.set(ns, file); + } + // 第一轮遍历,加上每个namespace自己的local + this.addNSLocals(namespaceStack, finalNamespaces, parentMap, globalVariableMap); + // 第二轮遍历,父节点加上子节点的export的local + this.addNSExportedLocals(finalNamespaces, globalVariableMap, parentMap); + } + for (const file of this.getFiles()) { + // 文件加上import的local,包括ns的 + this.addFileImportLocals(file, globalVariableMap); + } + return globalVariableMap; + } + getStaticInitMethods() { + const staticInitMethods = []; + for (const method of Array.from(this.getMethodsMap(true).values())) { + if (method.getName() === STATIC_INIT_METHOD_NAME) { + staticInitMethods.push(method); + } + } + return staticInitMethods; + } + buildClassDone() { + return this.buildStage >= SceneBuildStage.CLASS_DONE; + } + getModuleScene(moduleName) { + return this.moduleScenesMap.get(moduleName); + } + getModuleSceneMap() { + return this.moduleScenesMap; + } + getGlobalModule2PathMapping() { + return this.globalModule2PathMapping; + } + getbaseUrl() { + return this.baseUrl; + } + getImportFiles(arkfile) { + let ans; + let callGraph = new CallGraph(this); + let callGraphBuilder = new CallGraphBuilder(callGraph, this); + let entryPoints2 = []; + // 只处理目标文件 + // 遍历类 + for (const arkClass of arkfile.getClasses()) { + // 只处理目标类 + // 遍历类中的方法 + for (const arkMethod of arkClass.getMethods()) { + // 只处理目标方法 + // 将符合条件的方法添加到 entryPoints 数组 + entryPoints2.push(arkMethod.getSignature()); + } + } + callGraphBuilder.buildClassHierarchyCallGraph(entryPoints2, false); + const filesSet = new Set(); + for (let node of callGraph.getNodesIter()) { + let method_signature = node.getMethod(); + let arkfile_signature = method_signature.getDeclaringClassSignature().getDeclaringFileSignature(); + let file = this.getFile(arkfile_signature); + if (file) { + filesSet.add(file); + } + } + ans = Array.from(filesSet); + const mp = arkfile.getImportInfos(); + for (const import_info of mp) { + //console.log("import_info: ",import_info); + const lazyExportInfo = import_info.getLazyExportInfo(); + const declearingfile = lazyExportInfo?.getDeclaringArkFile(); + if (declearingfile) { + ans.push(declearingfile); + } + } + return ans; + } + getClassByPathSubstring(substring) { + // Iterate over the filesMap and check if the absoluteFilePath contains the substring + for (const arkFile of this.getFiles()) { + // Assuming ArkFile has a property `absoluteFilePath` which is a string + if (arkFile.getFilePath().includes(substring)) { + for (const arkclass of arkFile.getClasses()) { + if (arkclass.hasDecorator("Entry")) { + return arkclass; + } + } + } + } + // If no file matches, return undefined + return undefined; + } + getFileByPathSubstring(substring) { + // Iterate over the filesMap and check if the absoluteFilePath contains the substring + for (const arkFile of this.getFiles()) { + // Assuming ArkFile has a property `absoluteFilePath` which is a string + if (arkFile.getFilePath().includes(substring)) { + return arkFile; + } + } + // If no file matches, return undefined + return undefined; + } + getModuleRouteMap() { + let mp = this.getModuleSceneMap(); + mp.forEach((value, key) => { + value.getRouterMap(); + }); + } + buildRouteNameMap() { + for (const arkfile of this.getFiles()) { + for (const arkclass of arkfile.getClasses()) { + const decorators = arkclass.getDecorators(); + if (decorators && decorators.length > 0) { + const content = decorators[0].content; + if (content) { + const match = content.match(/routeName:\s*'([^']+)'/); + if (match && match[1]) { + const routeName = match[1]; + console.log("routename: ", routeName + "------------> " + arkclass.getName()); + this.routeNameMap.set(routeName, arkclass); + } + } + } + } + } + } + getRouteNameMap() { + return this.routeNameMap; + } +} +class ModuleScene { + projectScene; + moduleName = ''; + modulePath = ''; + moduleFileMap = new Map(); + moduleOhPkgFilePath = ''; + ohPkgContent = {}; + routerMapPath = ''; + routerMapPathContent = {}; + routerMap = new Map(); + constructor(projectScene) { + this.projectScene = projectScene; + } + ModuleSceneBuilder(moduleName, modulePath, supportFileExts, recursively = false) { + this.moduleName = moduleName; + this.modulePath = modulePath; + this.getModuleOhPkgFilePath(); + if (this.moduleOhPkgFilePath) { + this.ohPkgContent = fetchDependenciesFromFile(this.moduleOhPkgFilePath); + this.getRouterMapPath(this.ohPkgContent); + } + else { + logger$1.warn('This module has no oh-package.json5!'); + } + if (this.routerMapPath) { + this.routerMapPathContent = fetchDependenciesFromFile(this.routerMapPath); + } + this.genArkFiles(supportFileExts); + } + ModuleScenePartiallyBuilder(moduleName, modulePath) { + this.moduleName = moduleName; + this.modulePath = modulePath; + if (this.moduleOhPkgFilePath) { + this.ohPkgContent = fetchDependenciesFromFile(this.moduleOhPkgFilePath); + } + else { + logger$1.warn('This module has no oh-package.json5!'); + } + } + /** + * get oh-package.json5 + */ + getModuleOhPkgFilePath() { + const moduleOhPkgFilePath = path$1.resolve(this.projectScene.getRealProjectDir(), path$1.join(this.modulePath, OH_PACKAGE_JSON5)); + if (fs$1.existsSync(moduleOhPkgFilePath)) { + this.moduleOhPkgFilePath = moduleOhPkgFilePath; + } + } + getRouterMapPath(ohPkgContent) { + const routerMapConfig = ohPkgContent?.routerMap; + if (!routerMapConfig || typeof routerMapConfig !== 'string') { + console.warn('模块配置中未找到有效的routerMap定义'); + return; + } + // 解析$profile:xxx格式的路径(提取文件名部分) + const profilePrefix = '$profile:'; + if (routerMapConfig.startsWith(profilePrefix)) { + // 提取文件名(如从"$profile:route_map"得到"route_map.json") + const fileName = `${routerMapConfig.slice(profilePrefix.length)}.json`; + // 拼接完整路径:项目根目录 + 模块路径 + profile目录 + 文件名 + const routerMapPath = path$1.resolve(this.projectScene.getRealProjectDir(), this.modulePath, 'src/main/resources/base/profile', fileName); + // 检查文件是否存在 + if (fs$1.existsSync(routerMapPath)) { + this.routerMapPath = routerMapPath; + console.log(`找到路由配置文件: ${routerMapPath}`); + } + else { + console.error(`路由配置文件不存在: ${routerMapPath}`); + } + } + else { + console.error(`routerMap配置格式错误,需以"${profilePrefix}"开头,实际为: ${routerMapConfig}`); + } + } + getRouterMap() { + let routerMapArray = this.routerMapPathContent.routerMap; + if (typeof routerMapArray === 'string') { + routerMapArray = JSON.parse(routerMapArray); + } + if (Array.isArray(routerMapArray)) { + routerMapArray.forEach(item => { + const { name, pageSourceFile, buildFunction } = item; + if (name && pageSourceFile && buildFunction) { + // 这里可以调整你想要存放的值(比如 ArkFile 是你自己定义的类) + let arkfile = this.projectScene.getFileByPathSubstring(pageSourceFile); + let arkclass = arkfile?.getClassWithName(buildFunction); + if (arkclass && arkfile) { + console.log(name + "------------>", arkclass.getName()); + this.routerMap.set(name, arkclass); + } + } + }); + } + } + /** + * get nodule name + * @returns return module name + */ + getModuleName() { + return this.moduleName; + } + getModulePath() { + return this.modulePath; + } + getOhPkgFilePath() { + return this.moduleOhPkgFilePath; + } + getOhPkgContent() { + return this.ohPkgContent; + } + getModuleFilesMap() { + return this.moduleFileMap; + } + addArkFile(arkFile) { + this.moduleFileMap.set(arkFile.getFileSignature().toMapKey(), arkFile); + } + genArkFiles(supportFileExts) { + getAllFiles(this.modulePath, supportFileExts, this.projectScene.getOptions().ignoreFileNames).forEach(file => { + logger$1.trace('=== parse file:', file); + try { + const arkFile = new ArkFile(FileUtils.getFileLanguage(file, this.projectScene.getFileLanguages())); + arkFile.setScene(this.projectScene); + arkFile.setModuleScene(this); + buildArkFileFromFile(file, this.projectScene.getRealProjectDir(), arkFile, this.projectScene.getProjectName()); + this.projectScene.setFile(arkFile); + } + catch (error) { + logger$1.error('Error parsing file:', file, error); + this.projectScene.getUnhandledFilePaths().push(file); + return; + } + }); + } + returnRouteMap() { + return this.routerMap; + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const DOT_FILE_HEADER = `digraph G { + graph [nodesep=0.1] + node [shape=box] + edge [arrowhead=vee] +`; +class ViewTreePrinter extends Printer { + viewTree; + dupCnt; + constructor(viewTree) { + super(); + this.viewTree = viewTree; + this.dupCnt = 0; + } + dump() { + this.printer.clear(); + let root = this.viewTree.getRoot(); + if (!root) { + return this.printer.toString(); + } + this.printer.write(DOT_FILE_HEADER); + this.walk(root, root.parent); + this.printer.write('}'); + return this.printer.toString(); + } + walk(item, parent, map = new Map()) { + let skipChildren = this.writeNode(item, parent, map); + if (skipChildren) { + return; + } + for (const child of item.children) { + this.walk(child, item, map); + } + } + escapeDotLabel(content) { + const MAX_LABEL_LEN = 64; + const PRE_FIX_LEN = 5; + let label = content.join('|'); + if (label.length > MAX_LABEL_LEN) { + return label.substring(0, PRE_FIX_LEN) + '...' + label.substring(label.length - MAX_LABEL_LEN + PRE_FIX_LEN); + } + return label; + } + writeNode(item, parent, map) { + let id = `Node${map.size}`; + let hasSameNode = map.has(item) || map.has(item.signature); + if (hasSameNode) { + id = `${id}_${this.dupCnt++}`; + this.printer.write(` ${id} [label="${item.name}" style=filled color="green"]\n`); + } + else { + this.printer.write(` ${id} [label="${item.name}"]\n`); + } + if (parent) { + this.printer.write(` ${map.get(parent)} -> ${id}\n`); + } + this.writeNodeStateValues(item, id); + this.writeNodeAttributes(item, id); + this.writeNodeSignature(item, id); + if (map.get(item)) { + this.printer.write(` {rank="same"; ${id};${map.get(item)};}\n`); + this.printer.write(` ${id} -> ${map.get(item)}[style=dotted]\n`); + return true; + } + else if (map.get(item.signature)) { + this.printer.write(` {rank="same"; ${id};${map.get(item.signature)};}\n`); + this.printer.write(` ${id} -> ${map.get(item.signature)}[style=dotted]\n`); + return true; + } + map.set(item, id); + if (item.signature && !map.has(item.signature)) { + map.set(item.signature, id); + } + return false; + } + writeNodeStateValues(item, id) { + if (item.stateValues.size > 0) { + let stateValuesId = `${id}val`; + let content = []; + item.stateValues.forEach(value => { + content.push(value.getName()); + }); + this.printer.write(` ${stateValuesId} [shape=ellipse label="StateValues\n ${this.escapeDotLabel(content)}" fontsize=10 height=.1 style=filled color=".7 .3 1.0" ]\n`); + this.printer.write(` ${id} -> ${stateValuesId}\n`); + } + } + writeNodeAttributes(item, id) { + if (item.attributes.size > 0) { + let attributesId = `${id}attributes`; + let content = []; + for (const [key, _] of item.attributes) { + if (key !== COMPONENT_POP_FUNCTION) { + content.push(key); + } + } + if (content.length > 0) { + this.printer.write(` ${attributesId} [shape=ellipse label="property|Event\n${this.escapeDotLabel(content)}" fontsize=10 height=.1 style=filled color=".7 .3 1.0" ]\n`); + this.printer.write(` ${id} -> ${attributesId}\n`); + } + } + } + writeNodeSignature(item, id) { + if (item.signature) { + let signatureId = `${id}signature`; + let content = [item.signature.toString()]; + this.printer.write(` ${signatureId} [shape=ellipse label="signature\n${this.escapeDotLabel(content)}" fontsize=10 height=.1 style=filled color=".7 .3 1.0" ]\n`); + this.printer.write(` ${id} -> ${signatureId}\n`); + } + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class StaticSingleAssignmentFormer { + transformBody(body) { + let cfg = body.getCfg(); + let blockToDefs = new Map(); + let localToBlocks = new Map(); + for (const block of cfg.getBlocks()) { + let defs = new Set(); + for (const stmt of block.getStmts()) { + this.transformStmt(stmt, defs, localToBlocks, block); + } + blockToDefs.set(block, defs); + } + let dominanceFinder = new DominanceFinder(cfg); + let blockToPhiStmts = this.decideBlockToPhiStmts(body, dominanceFinder, blockToDefs, localToBlocks); + this.addPhiStmts(blockToPhiStmts, cfg, blockToDefs); + let dominanceTree = new DominanceTree(dominanceFinder); + this.renameLocals(body, dominanceTree, blockToPhiStmts); + } + transformStmt(stmt, defs, localToBlocks, block) { + if (stmt.getDef() != null && stmt.getDef() instanceof Local) { + let local = stmt.getDef(); + defs.add(local); + if (localToBlocks.has(local)) { + localToBlocks.get(local)?.add(block); + } + else { + let blcoks = new Set(); + blcoks.add(block); + localToBlocks.set(local, blcoks); + } + } + } + decideBlockToPhiStmts(body, dominanceFinder, blockToDefs, localToBlocks) { + let blockToPhiStmts = new Map(); + let blockToPhiLocals = new Map(); + let localToPhiBlock = new Map(); + for (const [_, local] of body.getLocals()) { + localToPhiBlock.set(local, new Set()); + let phiBlocks = localToPhiBlock.get(local); + let blocks = Array.from(localToBlocks.get(local)); + while (blocks.length !== 0) { + let block = blocks.splice(0, 1).at(0); + let dfs = dominanceFinder.getDominanceFrontiers(block); + for (const df of dfs) { + this.handleDf(blockToPhiStmts, blockToPhiLocals, phiBlocks, df, local, blockToDefs, blocks); + } + } + } + return blockToPhiStmts; + } + handleDf(blockToPhiStmts, blockToPhiLocals, phiBlocks, df, local, blockToDefs, blocks) { + if (!phiBlocks.has(df)) { + phiBlocks.add(df); + let phiStmt = this.createEmptyPhiStmt(local); + if (blockToPhiStmts.has(df)) { + blockToPhiStmts.get(df)?.add(phiStmt); + blockToPhiLocals.get(df)?.add(local); + } + else { + let phiStmts = new Set(); + phiStmts.add(phiStmt); + blockToPhiStmts.set(df, phiStmts); + let phiLocals = new Set(); + phiLocals.add(local); + blockToPhiLocals.set(df, phiLocals); + } + blockToDefs.get(df)?.add(local); + if (!blockToDefs.get(df)?.has(local)) { + blocks.push(df); + } + } + } + handleBlockWithSucc(blockToPhiStmts, succ, blockToDefs, block, phiArgsNum) { + for (const phi of blockToPhiStmts.get(succ)) { + let local = phi.getDef(); + if (blockToDefs.get(block)?.has(local)) { + if (phiArgsNum.has(phi)) { + let num = phiArgsNum.get(phi); + phiArgsNum.set(phi, num + 1); + } + else { + phiArgsNum.set(phi, 1); + } + } + } + } + addPhiStmts(blockToPhiStmts, cfg, blockToDefs) { + let phiArgsNum = new Map(); + for (const block of cfg.getBlocks()) { + let succs = Array.from(block.getSuccessors()); + for (const succ of succs) { + if (blockToPhiStmts.has(succ)) { + this.handleBlockWithSucc(blockToPhiStmts, succ, blockToDefs, block, phiArgsNum); + } + } + } + for (const block of blockToPhiStmts.keys()) { + let phis = blockToPhiStmts.get(block); + let phisTocheck = new Set(phis); + for (const phi of phisTocheck) { + if (phiArgsNum.get(phi) < 2) { + phis.delete(phi); + } + } + for (const phi of phis) { + cfg.insertBefore(phi, block.getHead()); + } + } + } + renameUseAndDef(stmt, localToNameStack, nextFreeIdx, newLocals, newPhiStmts) { + let uses = stmt.getUses(); + if (uses.length > 0 && !this.constainsPhiExpr(stmt)) { + for (const use of uses) { + if (use instanceof Local) { + let nameStack = localToNameStack.get(use); + let newUse = nameStack[nameStack.length - 1]; + stmt.replaceUse(use, newUse); + } + } + } + // rename def + let def = stmt.getDef(); + if (def != null && def instanceof Local) { + let newName = def.getName() + '#' + nextFreeIdx; + nextFreeIdx++; + let newDef = new Local(newName); + newDef.setOriginalValue(def); + newLocals.add(newDef); + localToNameStack.get(def)?.push(newDef); + stmt.setLeftOp(newDef); + if (this.constainsPhiExpr(stmt)) { + newPhiStmts.add(stmt); + } + } + return nextFreeIdx; + } + renameLocals(body, dominanceTree, blockToPhiStmts) { + let newLocals = new Set(body.getLocals().values()); + let localToNameStack = new Map(); + for (const local of newLocals) { + localToNameStack.set(local, new Array()); + } + let blockStack = new Array(); + let visited = new Set(); + let dfsBlocks = dominanceTree.getAllNodesDFS(); + let nextFreeIdx = 0; + for (const block of dfsBlocks) { + let newPhiStmts = new Set(); + for (const stmt of block.getStmts()) { + // rename uses and def + nextFreeIdx = this.renameUseAndDef(stmt, localToNameStack, nextFreeIdx, newLocals, newPhiStmts); + } + visited.add(block); + blockStack.push(block); + if (blockToPhiStmts.has(block)) { + blockToPhiStmts.set(block, newPhiStmts); + } + // rename phiStmts' args + let succs = Array.from(block.getSuccessors()); + for (const succ of succs) { + if (!blockToPhiStmts.has(succ)) { + continue; + } + let phiStmts = blockToPhiStmts.get(succ); + for (const phiStmt of phiStmts) { + let def = phiStmt.getDef(); + let oriDef = this.getOriginalLocal(def, new Set(localToNameStack.keys())); + let nameStack = localToNameStack.get(oriDef); + let arg = nameStack[nameStack.length - 1]; + this.addNewArgToPhi(phiStmt, arg, block); + } + } + // if a block's children in dominance tree are visited, remove it + this.removeVisitedTree(blockStack, dominanceTree, visited, localToNameStack); + } + body.setLocals(newLocals); + } + removeVisitedTree(blockStack, dominanceTree, visited, localToNameStack) { + let top = blockStack[blockStack.length - 1]; + let children = dominanceTree.getChildren(top); + while (this.containsAllChildren(visited, children)) { + blockStack.pop(); + for (const stmt of top.getStmts()) { + let def = stmt.getDef(); + if (def != null && def instanceof Local) { + let oriDef = this.getOriginalLocal(def, new Set(localToNameStack.keys())); + localToNameStack.get(oriDef)?.pop(); + } + } + // next block to check + if (blockStack.length > 0) { + top = blockStack[blockStack.length - 1]; + children = dominanceTree.getChildren(top); + } + else { + break; + } + } + } + constainsPhiExpr(stmt) { + if (stmt instanceof ArkAssignStmt && stmt.getUses().length > 0) { + for (const use of stmt.getUses()) { + if (use instanceof ArkPhiExpr) { + return true; + } + } + } + return false; + } + getOriginalLocal(local, locals) { + if (locals.has(local)) { + return local; + } + let hashPos = local.getName().indexOf('#'); + let oriName = local.getName().substring(0, hashPos); + for (const oriLocal of locals) { + if (oriLocal.getName() === oriName) { + return oriLocal; + } + } + return null; + } + addNewArgToPhi(phiStmt, arg, block) { + for (let use of phiStmt.getUses()) { + if (use instanceof ArkPhiExpr) { + let phiExpr = use; + let args = phiExpr.getArgs(); + let argToBlock = phiExpr.getArgToBlock(); + args.push(arg); + argToBlock.set(arg, block); + phiExpr.setArgs(args); + phiExpr.setArgToBlock(argToBlock); + break; + } + } + } + containsAllChildren(blockSet, children) { + for (const child of children) { + if (!blockSet.has(child)) { + return false; + } + } + return true; + } + createEmptyPhiStmt(local) { + let phiExpr = new ArkPhiExpr(); + return new ArkAssignStmt(local, phiExpr); + } +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const logger = ConsoleLogger.getLogger(exports.LOG_MODULE_TYPE.ARKANALYZER, 'callGraphUtils'); +class MethodSignatureManager { + _workList = []; + _processedList = []; + get workList() { + return this._workList; + } + set workList(list) { + this._workList = list; + } + get processedList() { + return this._processedList; + } + set processedList(list) { + this._processedList = list; + } + findInWorkList(signature) { + return this.workList.find(item => item === signature); + } + findInProcessedList(signature) { + let result = this.processedList.find(item => item.toString() === signature.toString()); + return typeof result !== 'undefined'; + } + addToWorkList(signature) { + if (!isItemRegistered(signature, this.workList, (a, b) => a.toString() === b.toString())) { + this.workList.push(signature); + } + } + addToProcessedList(signature) { + if (!isItemRegistered(signature, this.processedList, (a, b) => a === b)) { + this.processedList.push(signature); + } + } + removeFromWorkList(signature) { + this.workList = this.workList.filter(item => item !== signature); + } + removeFromProcessedList(signature) { + this.processedList = this.processedList.filter(item => item.toString() !== signature.toString()); + } +} +class SceneManager { + _scene; + get scene() { + return this._scene; + } + set scene(value) { + this._scene = value; + } + getMethod(method) { + let targetMethod = this._scene.getMethod(method); + if (targetMethod != null) { + return targetMethod; + } + // 支持SDK调用解析 + let file = this._scene.getFile(method.getDeclaringClassSignature().getDeclaringFileSignature()); + if (file) { + const methods = ModelUtils.getAllMethodsInFile(file); + for (let methodUnderFile of methods) { + if (method.toString() === methodUnderFile.getSignature().toString()) { + return methodUnderFile; + } + } + } + return targetMethod; + } + getClass(arkClass) { + if (typeof arkClass.getClassName() === 'undefined') { + return null; + } + let classInstance = this._scene.getClass(arkClass); + if (classInstance != null) { + return classInstance; + } + let sdkOrTargetProjectFile = this._scene.getFile(arkClass.getDeclaringFileSignature()); + // TODO: support get sdk class, targetProject class waiting to be supported + if (sdkOrTargetProjectFile != null) { + for (let classUnderFile of ModelUtils.getAllClassesInFile(sdkOrTargetProjectFile)) { + if (classUnderFile.getSignature().toString() === arkClass.toString()) { + return classUnderFile; + } + } + } + return classInstance; + } + getExtendedClasses(arkClass) { + let sourceClass = this.getClass(arkClass); + let classList = [sourceClass]; // 待处理类 + let extendedClasses = []; // 已经处理的类 + while (classList.length > 0) { + let tempClass = classList.shift(); + if (tempClass == null) { + continue; + } + let firstLevelSubclasses = Array.from(tempClass.getExtendedClasses().values()); + if (!firstLevelSubclasses) { + continue; + } + for (let subclass of firstLevelSubclasses) { + if (!isItemRegistered(subclass, extendedClasses, (a, b) => a.getSignature().toString() === b.getSignature().toString())) { + // 子类未处理,加入到classList + classList.push(subclass); + } + } + // 当前类处理完毕,标记为已处理 + if (!isItemRegistered(tempClass, extendedClasses, (a, b) => a.getSignature().toString() === b.getSignature().toString())) { + extendedClasses.push(tempClass); + } + } + return extendedClasses; + } +} +function isItemRegistered(item, array, compareFunc) { + for (let tempItem of array) { + if (compareFunc(tempItem, item)) { + return true; + } + } + return false; +} +function splitStringWithRegex(input) { + // 正则表达式匹配 "a.b.c()" 并捕获 "a" "b" "c" + const regex = /^(\w+)\.(\w+)\.(\w+)\(\)$/; + const match = input.match(regex); + if (match) { + // 返回捕获的部分,忽略整个匹配结果 + return match.slice(1); + } + else { + // 如果输入不匹配,返回空数组 + return []; + } +} +function printCallGraphDetails(methods, calls, rootDir) { + // 打印 Methods + logger.info('Call Graph:\n'); + logger.info('\tMethods:'); + methods.forEach(method => { + logger.info(`\t\t${method}`); + }); + // 打印 Calls + logger.info('\tCalls:'); + const arrow = '->'; + calls.forEach((calledMethods, method) => { + // 对于每个调用源,只打印一次调用源和第一个目标方法 + const modifiedMethodName = `<${method}`; + logger.info(`\t\t${modifiedMethodName.padEnd(4)} ${arrow}`); + for (let i = 0; i < calledMethods.length; i++) { + const modifiedCalledMethod = `\t\t<${calledMethods[i]}`; + logger.info(`\t\t${modifiedCalledMethod}`); + } + logger.info('\n'); + }); +} +function extractLastBracketContent(input) { + // 正则表达式匹配最后一个尖括号内的内容,直到遇到左圆括号 + const match = input.match(/<([^<>]*)\(\)>$/); + if (match && match[1]) { + return match[1].trim(); + } + return ''; +} + +/* + * Copyright (c) 2024-2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const sourceFileCache = new Map(); +class AstTreeUtils { + /** + * get source file from code segment + * @param fileName source file name + * @param code source code + * @returns ts.SourceFile + */ + static getASTNode(fileName, code) { + const key = this.getKeyFromCode(code); + let sourceFile = sourceFileCache.get(key); + if (sourceFile) { + return sourceFile; + } + sourceFile = this.createSourceFile(fileName, code); + sourceFileCache.set(key, sourceFile); + return sourceFile; + } + /** + * get source file from ArkFile + * @param arkFile ArkFile + * @returns ts.SourceFile + */ + static getSourceFileFromArkFile(arkFile) { + const signature = arkFile.getFileSignature().toString(); + const key = this.getKeyFromCode(signature); + let sourceFile = sourceFileCache.get(key); + if (sourceFile) { + return sourceFile; + } + sourceFile = this.createSourceFile(arkFile.getName(), arkFile.getCode()); + sourceFileCache.set(key, sourceFile); + return sourceFile; + } + static createSourceFile(fileName, code) { + return ts.createSourceFile(fileName, code, ts.ScriptTarget.Latest, true, undefined, ETS_COMPILER_OPTIONS); + } + /** + * convert source code to hash string + * @param code source code + * @returns string + */ + static getKeyFromCode(code) { + return crypto__namespace.createHash('sha256').update(code).digest('hex'); + } +} + +exports.ts = ts; +exports.ALL = ALL; +exports.ANONYMOUS_CLASS_DELIMITER = ANONYMOUS_CLASS_DELIMITER; +exports.ANONYMOUS_CLASS_PREFIX = ANONYMOUS_CLASS_PREFIX; +exports.ANONYMOUS_METHOD_PREFIX = ANONYMOUS_METHOD_PREFIX; +exports.ANY_KEYWORD = ANY_KEYWORD; +exports.ARKTS_STATIC_MARK = ARKTS_STATIC_MARK; +exports.AbstractAnalysis = AbstractAnalysis; +exports.AbstractBinopExpr = AbstractBinopExpr; +exports.AbstractExpr = AbstractExpr; +exports.AbstractFieldRef = AbstractFieldRef; +exports.AbstractInvokeExpr = AbstractInvokeExpr; +exports.AbstractRef = AbstractRef; +exports.AddrPagEdge = AddrPagEdge; +exports.AliasClassSignature = AliasClassSignature; +exports.AliasType = AliasType; +exports.AliasTypeExpr = AliasTypeExpr; +exports.AliasTypeSignature = AliasTypeSignature; +exports.AnnotationNamespaceType = AnnotationNamespaceType; +exports.AnnotationType = AnnotationType; +exports.AnnotationTypeQueryType = AnnotationTypeQueryType; +exports.AnyType = AnyType; +exports.ArkAliasTypeDefineStmt = ArkAliasTypeDefineStmt; +exports.ArkArrayRef = ArkArrayRef; +exports.ArkAssignStmt = ArkAssignStmt; +exports.ArkAwaitExpr = ArkAwaitExpr; +exports.ArkBody = ArkBody; +exports.ArkCastExpr = ArkCastExpr; +exports.ArkCaughtExceptionRef = ArkCaughtExceptionRef; +exports.ArkClass = ArkClass; +exports.ArkConditionExpr = ArkConditionExpr; +exports.ArkDeleteExpr = ArkDeleteExpr; +exports.ArkField = ArkField; +exports.ArkFile = ArkFile; +exports.ArkIfStmt = ArkIfStmt; +exports.ArkInstanceFieldRef = ArkInstanceFieldRef; +exports.ArkInstanceInvokeExpr = ArkInstanceInvokeExpr; +exports.ArkInstanceOfExpr = ArkInstanceOfExpr; +exports.ArkInvokeStmt = ArkInvokeStmt; +exports.ArkMethod = ArkMethod; +exports.ArkNamespace = ArkNamespace; +exports.ArkNewArrayExpr = ArkNewArrayExpr; +exports.ArkNewExpr = ArkNewExpr; +exports.ArkNormalBinopExpr = ArkNormalBinopExpr; +exports.ArkParameterRef = ArkParameterRef; +exports.ArkPhiExpr = ArkPhiExpr; +exports.ArkPtrInvokeExpr = ArkPtrInvokeExpr; +exports.ArkReturnStmt = ArkReturnStmt; +exports.ArkReturnVoidStmt = ArkReturnVoidStmt; +exports.ArkSignatureBuilder = ArkSignatureBuilder; +exports.ArkStaticFieldRef = ArkStaticFieldRef; +exports.ArkStaticInvokeExpr = ArkStaticInvokeExpr; +exports.ArkThisRef = ArkThisRef; +exports.ArkThrowStmt = ArkThrowStmt; +exports.ArkTypeOfExpr = ArkTypeOfExpr; +exports.ArkUIViewTreeImpl = ArkUIViewTreeImpl; +exports.ArkUIViewTreePrinter = ArkUIViewTreePrinter; +exports.ArkUnopExpr = ArkUnopExpr; +exports.ArkYieldExpr = ArkYieldExpr; +exports.ArrayType = ArrayType; +exports.AstTreeUtils = AstTreeUtils; +exports.BIGINT_KEYWORD = BIGINT_KEYWORD; +exports.BOOLEAN_KEYWORD = BOOLEAN_KEYWORD; +exports.BUILDER_DECORATOR = BUILDER_DECORATOR; +exports.BUILDER_PARAM_DECORATOR = BUILDER_PARAM_DECORATOR; +exports.BUILDIN_ATOMIC_COMPONENT = BUILDIN_ATOMIC_COMPONENT; +exports.BUILDIN_SYSTEM_COMPONENT = BUILDIN_SYSTEM_COMPONENT; +exports.BUILD_PROFILE_JSON5 = BUILD_PROFILE_JSON5; +exports.BaseEdge = BaseEdge; +exports.BaseExplicitGraph = BaseExplicitGraph; +exports.BaseNode = BaseNode; +exports.BasicBlock = BasicBlock; +exports.BigIntType = BigIntType; +exports.BooleanType = BooleanType; +exports.CALLBACK_METHOD_NAME = CALLBACK_METHOD_NAME; +exports.CALL_BACK = CALL_BACK; +exports.CALL_SIGNATURE_NAME = CALL_SIGNATURE_NAME; +exports.CGStat = CGStat; +exports.COMPONENT_ATTRIBUTE = COMPONENT_ATTRIBUTE; +exports.COMPONENT_BINDCONTENT = COMPONENT_BINDCONTENT; +exports.COMPONENT_BINDSHEET = COMPONENT_BINDSHEET; +exports.COMPONENT_BRANCH_FUNCTION = COMPONENT_BRANCH_FUNCTION; +exports.COMPONENT_BUILD_FUNCTION = COMPONENT_BUILD_FUNCTION; +exports.COMPONENT_COMMON = COMPONENT_COMMON; +exports.COMPONENT_CREATE_FUNCTION = COMPONENT_CREATE_FUNCTION; +exports.COMPONENT_CUSTOMVIEW = COMPONENT_CUSTOMVIEW; +exports.COMPONENT_DECORATOR = COMPONENT_DECORATOR; +exports.COMPONENT_DIALOG = COMPONENT_DIALOG; +exports.COMPONENT_FOR_EACH = COMPONENT_FOR_EACH; +exports.COMPONENT_IF = COMPONENT_IF; +exports.COMPONENT_IF_BRANCH = COMPONENT_IF_BRANCH; +exports.COMPONENT_INSTANCE = COMPONENT_INSTANCE; +exports.COMPONENT_LAZY_FOR_EACH = COMPONENT_LAZY_FOR_EACH; +exports.COMPONENT_LIFECYCLE_METHOD_NAME = COMPONENT_LIFECYCLE_METHOD_NAME; +exports.COMPONENT_MENU = COMPONENT_MENU; +exports.COMPONENT_MENUWRAPPER = COMPONENT_MENUWRAPPER; +exports.COMPONENT_POPUP = COMPONENT_POPUP; +exports.COMPONENT_POP_FUNCTION = COMPONENT_POP_FUNCTION; +exports.COMPONENT_REPEAT = COMPONENT_REPEAT; +exports.COMPONENT_TABBAR = COMPONENT_TABBAR; +exports.COMPONENT_TOAST = COMPONENT_TOAST; +exports.CONSTRUCTOR_NAME = CONSTRUCTOR_NAME; +exports.CSFuncID = CSFuncID; +exports.CallGraph = CallGraph; +exports.CallGraphBuilder = CallGraphBuilder; +exports.CallGraphEdge = CallGraphEdge; +exports.CallGraphNode = CallGraphNode; +exports.CallSite = CallSite; +exports.Cfg = Cfg; +exports.ClassHierarchyAnalysis = ClassHierarchyAnalysis; +exports.ClassSignature = ClassSignature; +exports.ClassType = ClassType; +exports.ClosureFieldRef = ClosureFieldRef; +exports.ClosureType = ClosureType; +exports.Constant = Constant; +exports.CopyPagEdge = CopyPagEdge; +exports.DECLARE_KEYWORD = DECLARE_KEYWORD; +exports.DEFAULT = DEFAULT; +exports.DEFAULT_ARK_CLASS_NAME = DEFAULT_ARK_CLASS_NAME; +exports.DEFAULT_ARK_METHOD_NAME = DEFAULT_ARK_METHOD_NAME; +exports.DEFAULT_NAME = DEFAULT_NAME; +exports.DFSMatching = DFSMatching; +exports.DVFG = DVFG; +exports.DVFGBuilder = DVFGBuilder; +exports.DataflowProblem = DataflowProblem; +exports.DataflowResult = DataflowResult; +exports.DataflowSolver = DataflowSolver; +exports.Decorator = Decorator; +exports.DefUseChain = DefUseChain; +exports.DiffPTData = DiffPTData; +exports.DominanceFinder = DominanceFinder; +exports.DominanceTree = DominanceTree; +exports.DotClassPrinter = DotClassPrinter; +exports.DotFilePrinter = DotFilePrinter; +exports.DotMethodPrinter = DotMethodPrinter; +exports.DotNamespacePrinter = DotNamespacePrinter; +exports.DummyCallCreator = DummyCallCreator; +exports.DummyMainCreater = DummyMainCreater; +exports.DynCallSite = DynCallSite; +exports.ENTRY_DECORATOR = ENTRY_DECORATOR; +exports.ETS_COMPILER_OPTIONS = ETS_COMPILER_OPTIONS; +exports.EnumValueType = EnumValueType; +exports.ExportInfo = ExportInfo; +exports.ExprUseReplacer = ExprUseReplacer; +exports.FUNCTION = FUNCTION; +exports.Fact = Fact; +exports.FieldSignature = FieldSignature; +exports.FileSignature = FileSignature; +exports.FileUtils = FileUtils; +exports.FullPosition = FullPosition; +exports.FuncPag = FuncPag; +exports.FunctionType = FunctionType; +exports.GLOBAL_THIS_NAME = GLOBAL_THIS_NAME; +exports.GenericType = GenericType; +exports.GlobalOverlayTree = GlobalOverlayTree; +exports.GlobalRef = GlobalRef; +exports.GraphPrinter = GraphPrinter; +exports.Handler = Handler; +exports.IMPORT = IMPORT; +exports.INSTANCE_INIT_METHOD_NAME = INSTANCE_INIT_METHOD_NAME; +exports.IRUtils = IRUtils; +exports.ImportInfo = ImportInfo; +exports.InterFuncPag = InterFuncPag; +exports.IntersectionType = IntersectionType; +exports.JsonPrinter = JsonPrinter; +exports.KLimitedContextSensitive = KLimitedContextSensitive; +exports.LEXICAL_ENV_NAME_PREFIX = LEXICAL_ENV_NAME_PREFIX; +exports.LIFECYCLE_METHOD_NAME = LIFECYCLE_METHOD_NAME; +exports.LexicalEnvType = LexicalEnvType; +exports.LineColPosition = LineColPosition; +exports.LiteralType = LiteralType; +exports.LoadPagEdge = LoadPagEdge; +exports.Local = Local; +exports.LocalSignature = LocalSignature; +exports.Logger = ConsoleLogger; +exports.MethodSignature = MethodSignature; +exports.MethodSignatureManager = MethodSignatureManager; +exports.MethodSubSignature = MethodSubSignature; +exports.ModelUtils = ModelUtils; +exports.ModulePath = ModulePath; +exports.NAME_DELIMITER = NAME_DELIMITER; +exports.NAME_PREFIX = NAME_PREFIX; +exports.NEVER_KEYWORD = NEVER_KEYWORD; +exports.NULL_KEYWORD = NULL_KEYWORD; +exports.NUMBER_KEYWORD = NUMBER_KEYWORD; +exports.NamespaceSignature = NamespaceSignature; +exports.NavigationMethodsSet = NavigationMethodsSet; +exports.NeverType = NeverType; +exports.NullType = NullType; +exports.NumberType = NumberType; +exports.OH_PACKAGE_JSON5 = OH_PACKAGE_JSON5; +exports.ON_OFF = ON_OFF; +exports.OhosRouterMethodsSet = OhosRouterMethodsSet; +exports.OhosWindowMethodsSet = OhosWindowMethodsSet; +exports.PAGStat = PAGStat; +exports.PROMISE = PROMISE; +exports.PTAStat = PTAStat; +exports.Pag = Pag; +exports.PagArrayNode = PagArrayNode; +exports.PagBuilder = PagBuilder; +exports.PagEdge = PagEdge; +exports.PagFuncNode = PagFuncNode; +exports.PagGlobalThisNode = PagGlobalThisNode; +exports.PagInstanceFieldNode = PagInstanceFieldNode; +exports.PagLocalNode = PagLocalNode; +exports.PagNewContainerExprNode = PagNewContainerExprNode; +exports.PagNewExprNode = PagNewExprNode; +exports.PagNode = PagNode; +exports.PagParamNode = PagParamNode; +exports.PagStaticFieldNode = PagStaticFieldNode; +exports.PagThisRefNode = PagThisRefNode; +exports.PathEdge = PathEdge; +exports.PathEdgePoint = PathEdgePoint; +exports.PointerAnalysis = PointerAnalysis; +exports.PointerAnalysisConfig = PointerAnalysisConfig; +exports.PrimitiveType = PrimitiveType; +exports.Printer = Printer; +exports.PrinterBuilder = PrinterBuilder; +exports.PtsSet = PtsSet; +exports.RapidTypeAnalysis = RapidTypeAnalysis; +exports.RefUseReplacer = RefUseReplacer; +exports.SCCDetection = SCCDetection; +exports.SPECIAL_CONTAINER_COMPONENT = SPECIAL_CONTAINER_COMPONENT; +exports.STATIC_BLOCK_METHOD_NAME_PREFIX = STATIC_BLOCK_METHOD_NAME_PREFIX; +exports.STATIC_INIT_METHOD_NAME = STATIC_INIT_METHOD_NAME; +exports.STRING_KEYWORD = STRING_KEYWORD; +exports.SUPER_NAME = SUPER_NAME; +exports.Scene = Scene; +exports.SceneConfig = SceneConfig; +exports.SceneManager = SceneManager; +exports.Scope = Scope; +exports.SourceClassPrinter = SourceClass; +exports.SourceFilePrinter = SourceFilePrinter; +exports.SourceMethodPrinter = SourceMethod; +exports.SourceNamespacePrinter = SourceNamespace; +exports.Stack = Stack; +exports.StaticSingleAssignmentFormer = StaticSingleAssignmentFormer; +exports.Stmt = Stmt; +exports.StmtUseReplacer = StmtUseReplacer; +exports.StringType = StringType; +exports.SystemRouterMethodsSet = SystemRouterMethodsSet; +exports.TEMP_LOCAL_PREFIX = TEMP_LOCAL_PREFIX; +exports.THIS_NAME = THIS_NAME; +exports.TSCONFIG_JSON = TSCONFIG_JSON; +exports.TextMatching = TextMatching; +exports.ThisPagEdge = ThisPagEdge; +exports.TupleType = TupleType; +exports.Type = Type; +exports.TypeInference = TypeInference; +exports.UIFuncBackEdge = UIFuncBackEdge; +exports.UIFuncEdge = UIFuncEdge; +exports.UIFuncGraph = UIFuncGraph; +exports.UIFuncGraphBuilder = UIFuncGraphBuilder; +exports.UIFuncNode = UIFuncNode; +exports.UNDEFINED_KEYWORD = UNDEFINED_KEYWORD; +exports.UNKNOWN_CLASS_NAME = UNKNOWN_CLASS_NAME; +exports.UNKNOWN_FIELD_NAME = UNKNOWN_FIELD_NAME; +exports.UNKNOWN_FILE_NAME = UNKNOWN_FILE_NAME; +exports.UNKNOWN_KEYWORD = UNKNOWN_KEYWORD; +exports.UNKNOWN_METHOD_NAME = UNKNOWN_METHOD_NAME; +exports.UNKNOWN_NAME = UNKNOWN_NAME; +exports.UNKNOWN_NAMESPACE_NAME = UNKNOWN_NAMESPACE_NAME; +exports.UNKNOWN_PROJECT_NAME = UNKNOWN_PROJECT_NAME; +exports.UnclearReferenceType = UnclearReferenceType; +exports.UndefinedType = UndefinedType; +exports.UndefinedVariableChecker = UndefinedVariableChecker; +exports.UndefinedVariableSolver = UndefinedVariableSolver; +exports.UnionType = UnionType; +exports.UnknownType = UnknownType; +exports.UpdatePossibleNodes = UpdatePossibleNodes; +exports.VOID_KEYWORD = VOID_KEYWORD; +exports.ValueUtil = ValueUtil; +exports.ViewTreePrinter = ViewTreePrinter; +exports.VisibleValue = VisibleValue; +exports.VoidType = VoidType; +exports.WritePagEdge = WritePagEdge; +exports.addCfg2Stmt = addCfg2Stmt; +exports.backtraceLocalInitValue = backtraceLocalInitValue; +exports.buildArkUIViewTree = buildArkUIViewTree; +exports.classSignatureCompare = classSignatureCompare; +exports.cloneArkUIViewTreeNode = cloneArkUIViewTreeNode; +exports.extractLastBracketContent = extractLastBracketContent; +exports.fetchDependenciesFromFile = fetchDependenciesFromFile; +exports.fieldSignatureCompare = fieldSignatureCompare; +exports.fileSignatureCompare = fileSignatureCompare; +exports.genSignature4ImportClause = genSignature4ImportClause; +exports.getAllFiles = getAllFiles; +exports.getCallbackMethodFromStmt = getCallbackMethodFromStmt; +exports.getFileRecursively = getFileRecursively; +exports.getNavigationType = getNavigationType; +exports.index_2_pageId = index_2_pageId; +exports.isEtsAtomicComponent = isEtsAtomicComponent; +exports.isEtsContainerComponent = isEtsContainerComponent; +exports.isEtsSystemComponent = isEtsSystemComponent; +exports.isItemRegistered = isItemRegistered; +exports.methodSignatureCompare = methodSignatureCompare; +exports.methodSubSignatureCompare = methodSubSignatureCompare; +exports.parseJsonText = parseJsonText; +exports.parseObjectLiteral = parseObjectLiteral; +exports.printCallGraphDetails = printCallGraphDetails; +exports.splitStringWithRegex = splitStringWithRegex; +exports.tabBar_2_TabContent = tabBar_2_TabContent; +exports.transfer2UnixPath = transfer2UnixPath; diff --git a/static/test-demo/config/Wechat_HarmonyOS.json b/static/test-demo/config/Wechat_HarmonyOS.json new file mode 100644 index 0000000..37f9a83 --- /dev/null +++ b/static/test-demo/config/Wechat_HarmonyOS.json @@ -0,0 +1,16 @@ +{ + "targetProjectName": "Wechat_HarmonyOS", + "targetProjectDirectory": "./projects/Wechat_HarmonyOS", + "sdks": [ + { + "name": "etsSdk", + "path": "ets", + "moduleName": "" + } + ], + + "options": { + "enableLeadingComments": true + } +} + diff --git a/static/test-demo/config/ets/NOTICE.txt b/static/test-demo/config/ets/NOTICE.txt new file mode 100644 index 0000000..8db5bcb --- /dev/null +++ b/static/test-demo/config/ets/NOTICE.txt @@ -0,0 +1,4860 @@ +Notices for files contained in SDK in this directory: +============================================================ +Notices for file(s): +/toolchains/lib/static_libxml2.a +------------------------------------------------------------ +Notices for software(s): +Software: openEuler:libxml2 +Version: 2.9.14-9.oe2203sp3 +Path: //third_party/libxml2 +------------------------------------------------------------ +Except where otherwise noted in the source code (e.g. the files hash.c, +list.c and the trio files, which are covered by a similar licence but +with different Copyright notices) all the files are: + + Copyright (C) 1998-2012 Daniel Veillard. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is fur- +nished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT- +NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +============================================================ +Notices for file(s): +/js/build-tools/ace-loader/bin/jerry-snapshot +/js/build-tools/ace-loader/bin/jerry +/toolchains/lib/libjerryscript.a +------------------------------------------------------------ +Notices for software(s): +Software: jerryscript +Version: v2.3.0 +Path: //third_party/jerryscript +------------------------------------------------------------ +Copyright JS Foundation and other contributors, http://js.foundation + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright JS Foundation and other contributors, http://js.foundation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +============================================================ +Notices for file(s): +/ets/build-tools/ets-loader/node_modules/typescript +------------------------------------------------------------ +Notices for software(s): +Software: typescript +Version: 4.9.5 +Path: //third_party/typescript +------------------------------------------------------------ +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and + +You must cause any modified files to carry prominent notices stating that You changed the files; and + +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + + +============================================================ +Notices for file(s): +/ets/api +/ets/api/@internal/full +/ets/api/@internal/full/canvaspattern.d.ts +/ets/api/@internal/full/featureability.d.ts +/ets/api/@internal/full/global.d.ts +/ets/api/@internal/full/index.d.ts +/ets/api/@internal/full/lifecycle.d.ts +/ets/api/@system.app.d.ts +/ets/api/@system.configuration.d.ts +/ets/api/@system.file.d.ts +/ets/api/@system.mediaquery.d.ts +/ets/api/@system.prompt.d.ts +/ets/api/@system.router.d.ts +/ets/build-tools/ets-loader/bin/ark/build-mac/aot/src +/ets/build-tools/ets-loader/bin/ark/build-mac/aot/src/lib_ark_builtins.d.ts +/ets/build-tools/ets-loader/bin/ark/build-mac/bin +/ets/build-tools/ets-loader/bin/ark/build-mac/bin/ark_aot_compiler +/ets/build-tools/ets-loader/bin/ark/build-mac/bin/libhilog.dylib +/ets/build-tools/ets-loader/bin/ark/build-mac/bin/libhmicui18n.dylib +/ets/build-tools/ets-loader/bin/ark/build-mac/bin/libhmicuuc.dylib +/ets/build-tools/ets-loader/bin/ark/build-mac/bin/libsec_shared.dylib +/ets/build-tools/ets-loader/bin/ark/build-mac/bin/libshared_libz.dylib +/ets/component/action_sheet.d.ts +/ets/component/alert_dialog.d.ts +/ets/component/alphabet_indexer.d.ts +/ets/component/badge.d.ts +/ets/component/blank.d.ts +/ets/component/button.d.ts +/ets/component/calendar_picker.d.ts +/ets/component/canvas.d.ts +/ets/component/checkbox.d.ts +/ets/component/checkboxgroup.d.ts +/ets/component/circle.d.ts +/ets/component/column.d.ts +/ets/component/column_split.d.ts +/ets/component/common.d.ts +/ets/component/common_ts_ets_api.d.ts +/ets/component/component3d.d.ts +/ets/component/container_span.d.ts +/ets/component/content_slot.d.ts +/ets/component/context_menu.d.ts +/ets/component/counter.d.ts +/ets/component/custom_dialog_controller.d.ts +/ets/component/data_panel.d.ts +/ets/component/date_picker.d.ts +/ets/component/divider.d.ts +/ets/component/ellipse.d.ts +/ets/component/embedded_component.d.ts +/ets/component/enums.d.ts +/ets/component/flex.d.ts +/ets/component/flow_item.d.ts +/ets/component/focus.d.ts +/ets/component/folder_stack.d.ts +/ets/component/for_each.d.ts +/ets/component/form_link.d.ts +/ets/component/gauge.d.ts +/ets/component/gesture.d.ts +/ets/component/grid.d.ts +/ets/component/gridItem.d.ts +/ets/component/grid_col.d.ts +/ets/component/grid_container.d.ts +/ets/component/grid_row.d.ts +/ets/component/hyperlink.d.ts +/ets/component/image.d.ts +/ets/component/image_animator.d.ts +/ets/component/image_common.d.ts +/ets/component/image_span.d.ts +/ets/component/index-full.d.ts +/ets/component/indicatorcomponent.d.ts +/ets/component/lazy_for_each.d.ts +/ets/component/line.d.ts +/ets/component/list.d.ts +/ets/component/list_item.d.ts +/ets/component/list_item_group.d.ts +/ets/component/loading_progress.d.ts +/ets/component/location_button.d.ts +/ets/component/marquee.d.ts +/ets/component/matrix2d.d.ts +/ets/component/menu.d.ts +/ets/component/menu_item.d.ts +/ets/component/menu_item_group.d.ts +/ets/component/nav_destination.d.ts +/ets/component/nav_router.d.ts +/ets/component/navigation.d.ts +/ets/component/navigator.d.ts +/ets/component/node_container.d.ts +/ets/component/page_transition.d.ts +/ets/component/panel.d.ts +/ets/component/particle.d.ts +/ets/component/paste_button.d.ts +/ets/component/path.d.ts +/ets/component/pattern_lock.d.ts +/ets/component/polygon.d.ts +/ets/component/polyline.d.ts +/ets/component/progress.d.ts +/ets/component/qrcode.d.ts +/ets/component/radio.d.ts +/ets/component/rating.d.ts +/ets/component/rect.d.ts +/ets/component/refresh.d.ts +/ets/component/relative_container.d.ts +/ets/component/repeat.d.ts +/ets/component/rich_editor.d.ts +/ets/component/rich_text.d.ts +/ets/component/row.d.ts +/ets/component/row_split.d.ts +/ets/component/save_button.d.ts +/ets/component/scroll.d.ts +/ets/component/scroll_bar.d.ts +/ets/component/search.d.ts +/ets/component/security_component.d.ts +/ets/component/select.d.ts +/ets/component/shape.d.ts +/ets/component/sidebar.d.ts +/ets/component/slider.d.ts +/ets/component/span.d.ts +/ets/component/stack.d.ts +/ets/component/state_management.d.ts +/ets/component/stepper.d.ts +/ets/component/stepper_item.d.ts +/ets/component/styled_string.d.ts +/ets/component/swiper.d.ts +/ets/component/symbol_span.d.ts +/ets/component/symbolglyph.d.ts +/ets/component/tab_content.d.ts +/ets/component/tabs.d.ts +/ets/component/text.d.ts +/ets/component/text_area.d.ts +/ets/component/text_clock.d.ts +/ets/component/text_common.d.ts +/ets/component/text_input.d.ts +/ets/component/text_picker.d.ts +/ets/component/text_timer.d.ts +/ets/component/time_picker.d.ts +/ets/component/toggle.d.ts +/ets/component/units.d.ts +/ets/component/video.d.ts +/ets/component/water_flow.d.ts +/ets/component/web.d.ts +/ets/component/with_theme.d.ts +/ets/component/xcomponent.d.ts +/ets/kits +/ets/kits/kits +/js/api/@internal/full +/js/api/@internal/full/canvaspattern.d.ts +/js/api/@internal/full/console.d.ts +/js/api/@internal/full/dom.d.ts +/js/api/@internal/full/featureability.d.ts +/js/api/@internal/full/global.d.ts +/js/api/@internal/full/index.d.ts +/js/api/@internal/full/viewmodel.d.ts +/js/api/@internal/lite +/js/api/@internal/lite/console.d.ts +/js/api/@internal/lite/global.d.ts +/js/api/@internal/lite/index.d.ts +/js/api/@internal/lite/viewmodel.d.ts +/js/api/config +/js/api/config/css +/js/api/config/hml +/js/build-tools/binary-tools/fastjson-1.2.83.jar +/js/build-tools/binary-tools/haptobin_tool.jar +/js/form +/js/form/action +/js/form/css +/js/form/hml +/previewer/common +/previewer/common/bin/Previewer +/previewer/common/bin/fontconfig.json +/previewer/common/bin/icudt72l.dat +/previewer/common/bin/lib2d_graphics.dylib +/previewer/common/bin/lib2d_graphics_new.dylib +/previewer/common/bin/libability_simulator.dylib +/previewer/common/bin/libace_compatible.dylib +/previewer/common/bin/libace_forward_compatibility.dylib +/previewer/common/bin/libace_napi.dylib +/previewer/common/bin/libace_uicontent.dylib +/previewer/common/bin/libark_inspector.dylib +/previewer/common/bin/libark_jsruntime.dylib +/previewer/common/bin/libark_tooling.dylib +/previewer/common/bin/libcjson.dylib +/previewer/common/bin/libconsole.dylib +/previewer/common/bin/libcrypto_openssl.dylib +/previewer/common/bin/libcrypto_restool.dylib +/previewer/common/bin/libcurl_shared.dylib +/previewer/common/bin/libdistributeddata_inner_mock.dylib +/previewer/common/bin/libdistributeddb_mock.dylib +/previewer/common/bin/libdrawable_descriptor.dylib +/previewer/common/bin/libdrawing_napi_impl.dylib +/previewer/common/bin/libfilemgmt_libhilog.dylib +/previewer/common/bin/libfilemgmt_libn.dylib +/previewer/common/bin/libglfw.dylib +/previewer/common/bin/libglfw_render_context.dylib +/previewer/common/bin/libglobal_resmgr_mac.dylib +/previewer/common/bin/libgraphics_effect.dylib +/previewer/common/bin/libhilog.dylib +/previewer/common/bin/libhilog_mac.dylib +/previewer/common/bin/libhmicui18n.dylib +/previewer/common/bin/libhmicuuc.dylib +/previewer/common/bin/libide_extension.dylib +/previewer/common/bin/libide_util.dylib +/previewer/common/bin/libintl_util.dylib +/previewer/common/bin/libjsoncpp.dylib +/previewer/common/bin/libnative_preferences.dylib +/previewer/common/bin/libnative_rdb.dylib +/previewer/common/bin/libnghttp2_shared.dylib +/previewer/common/bin/libpreviewer_window.dylib +/previewer/common/bin/libpreviewer_window_napi.dylib +/previewer/common/bin/librender_service_base.dylib +/previewer/common/bin/librender_service_client.dylib +/previewer/common/bin/librosen_text.dylib +/previewer/common/bin/libsec_shared.dylib +/previewer/common/bin/libshared_libz.dylib +/previewer/common/bin/libskia_canvaskit.dylib +/previewer/common/bin/libsqlite_sdk.dylib +/previewer/common/bin/libssl_openssl.dylib +/previewer/common/bin/libstring_utils.dylib +/previewer/common/bin/libtexgine.dylib +/previewer/common/bin/libtimer.dylib +/previewer/common/bin/libuv.dylib +/previewer/common/bin/module/arkui +/previewer/common/bin/module/arkui/modifier.abc +/previewer/common/bin/module/arkui/node.abc +/previewer/common/bin/module/arkui/shape.abc +/previewer/common/bin/module/arkui/theme.abc +/previewer/common/bin/module/arkui/uicontext.abc +/previewer/common/resources +/previewer/liteWearable/bin/Simulator +/previewer/liteWearable/config +/previewer/liteWearable/config/SourceHanSansSC-Regular.otf +/previewer/liteWearable/config/line_cj.brk +/toolchains/configcheck/configSchema_lite.json +/toolchains/configcheck/configSchema_rich.json +/toolchains/hdc +/toolchains/hnpcli +/toolchains/id_defined.json +/toolchains/idl +/toolchains/lib/2d_graphics_source_mac.a +/toolchains/lib/ace_base_i18n_mac.a +/toolchains/lib/ace_base_mac.a +/toolchains/lib/ace_core_components_ability_mac.a +/toolchains/lib/ace_core_components_align_mac.a +/toolchains/lib/ace_core_components_animation_ng_mac.a +/toolchains/lib/ace_core_components_arc_mac.a +/toolchains/lib/ace_core_components_badge_mac.a +/toolchains/lib/ace_core_components_base_ng_mac.a +/toolchains/lib/ace_core_components_box_mac.a +/toolchains/lib/ace_core_components_bubble_mac.a +/toolchains/lib/ace_core_components_button_mac.a +/toolchains/lib/ace_core_components_calendar_mac.a +/toolchains/lib/ace_core_components_camera_mac.a +/toolchains/lib/ace_core_components_chart_mac.a +/toolchains/lib/ace_core_components_checkable_mac.a +/toolchains/lib/ace_core_components_clip_mac.a +/toolchains/lib/ace_core_components_clock_mac.a +/toolchains/lib/ace_core_components_common_mac.a +/toolchains/lib/ace_core_components_common_v2_mac.a +/toolchains/lib/ace_core_components_container_modal_mac.a +/toolchains/lib/ace_core_components_counter_mac.a +/toolchains/lib/ace_core_components_coverage_mac.a +/toolchains/lib/ace_core_components_custom_dialog_mac.a +/toolchains/lib/ace_core_components_custom_paint_mac.a +/toolchains/lib/ace_core_components_data_panel_mac.a +/toolchains/lib/ace_core_components_dialog_mac.a +/toolchains/lib/ace_core_components_dialog_modal_mac.a +/toolchains/lib/ace_core_components_dialog_tween_mac.a +/toolchains/lib/ace_core_components_display_mac.a +/toolchains/lib/ace_core_components_divider_mac.a +/toolchains/lib/ace_core_components_drag_bar_mac.a +/toolchains/lib/ace_core_components_drop_filter_mac.a +/toolchains/lib/ace_core_components_event_ng_mac.a +/toolchains/lib/ace_core_components_flex_mac.a +/toolchains/lib/ace_core_components_focus_animation_mac.a +/toolchains/lib/ace_core_components_focus_collaboration_mac.a +/toolchains/lib/ace_core_components_focusable_mac.a +/toolchains/lib/ace_core_components_font_mac.a +/toolchains/lib/ace_core_components_foreach_mac.a +/toolchains/lib/ace_core_components_foreach_part_upd_mac.a +/toolchains/lib/ace_core_components_foreach_v2_mac.a +/toolchains/lib/ace_core_components_gesture_listener_mac.a +/toolchains/lib/ace_core_components_gestures_ng_mac.a +/toolchains/lib/ace_core_components_grid_layout_mac.a +/toolchains/lib/ace_core_components_grid_layout_v2_mac.a +/toolchains/lib/ace_core_components_grid_mac.a +/toolchains/lib/ace_core_components_grid_v2_mac.a +/toolchains/lib/ace_core_components_hyperlink_mac.a +/toolchains/lib/ace_core_components_ifelse_mac.a +/toolchains/lib/ace_core_components_image_mac.a +/toolchains/lib/ace_core_components_image_provider_ng_mac.a +/toolchains/lib/ace_core_components_indexer_mac.a +/toolchains/lib/ace_core_components_indexer_v2_mac.a +/toolchains/lib/ace_core_components_inspector_v2_mac.a +/toolchains/lib/ace_core_components_layout_ng_mac.a +/toolchains/lib/ace_core_components_list_mac.a +/toolchains/lib/ace_core_components_list_v2_mac.a +/toolchains/lib/ace_core_components_manager_ng_mac.a +/toolchains/lib/ace_core_components_marquee_mac.a +/toolchains/lib/ace_core_components_menu_mac.a +/toolchains/lib/ace_core_components_mouse_listener_mac.a +/toolchains/lib/ace_core_components_multimodal_mac.a +/toolchains/lib/ace_core_components_navigation_bar_mac.a +/toolchains/lib/ace_core_components_navigator_mac.a +/toolchains/lib/ace_core_components_option_mac.a +/toolchains/lib/ace_core_components_overlay_mac.a +/toolchains/lib/ace_core_components_padding_mac.a +/toolchains/lib/ace_core_components_page_mac.a +/toolchains/lib/ace_core_components_page_transition_mac.a +/toolchains/lib/ace_core_components_panel_mac.a +/toolchains/lib/ace_core_components_pattern_lock_mac.a +/toolchains/lib/ace_core_components_pattern_ng_mac.a +/toolchains/lib/ace_core_components_patternlock_pattern_ng_mac.a +/toolchains/lib/ace_core_components_picker_mac.a +/toolchains/lib/ace_core_components_piece_mac.a +/toolchains/lib/ace_core_components_popup_mac.a +/toolchains/lib/ace_core_components_positioned_mac.a +/toolchains/lib/ace_core_components_preview_mock_pattern_ng_mac.a +/toolchains/lib/ace_core_components_progress_mac.a +/toolchains/lib/ace_core_components_property_ng_mac.a +/toolchains/lib/ace_core_components_proxy_mac.a +/toolchains/lib/ace_core_components_qrcode_mac.a +/toolchains/lib/ace_core_components_qrcode_pattern_ng_mac.a +/toolchains/lib/ace_core_components_rating_mac.a +/toolchains/lib/ace_core_components_refresh_mac.a +/toolchains/lib/ace_core_components_relative_container_mac.a +/toolchains/lib/ace_core_components_render_ng_mac.a +/toolchains/lib/ace_core_components_rich_editor_pattern_ng_mac.a +/toolchains/lib/ace_core_components_root_mac.a +/toolchains/lib/ace_core_components_scoring_mac.a +/toolchains/lib/ace_core_components_scroll_bar_mac.a +/toolchains/lib/ace_core_components_scroll_mac.a +/toolchains/lib/ace_core_components_search_mac.a +/toolchains/lib/ace_core_components_security_component_pattern_ng_mac.a +/toolchains/lib/ace_core_components_select_mac.a +/toolchains/lib/ace_core_components_select_popup_mac.a +/toolchains/lib/ace_core_components_semi_modal_mac.a +/toolchains/lib/ace_core_components_shadow_mac.a +/toolchains/lib/ace_core_components_shape_mac.a +/toolchains/lib/ace_core_components_shared_transition_mac.a +/toolchains/lib/ace_core_components_sheet_mac.a +/toolchains/lib/ace_core_components_side_bar_mac.a +/toolchains/lib/ace_core_components_slider_mac.a +/toolchains/lib/ace_core_components_split_container_mac.a +/toolchains/lib/ace_core_components_stack_mac.a +/toolchains/lib/ace_core_components_stage_mac.a +/toolchains/lib/ace_core_components_stepper_mac.a +/toolchains/lib/ace_core_components_svg_mac.a +/toolchains/lib/ace_core_components_svg_ng_mac.a +/toolchains/lib/ace_core_components_swiper_mac.a +/toolchains/lib/ace_core_components_swiper_v2_mac.a +/toolchains/lib/ace_core_components_syntax_ng_mac.a +/toolchains/lib/ace_core_components_tab_bar_mac.a +/toolchains/lib/ace_core_components_tabs_v2_mac.a +/toolchains/lib/ace_core_components_text_clock_mac.a +/toolchains/lib/ace_core_components_text_field_mac.a +/toolchains/lib/ace_core_components_text_field_pattern_ng_mac.a +/toolchains/lib/ace_core_components_text_mac.a +/toolchains/lib/ace_core_components_text_overlay_mac.a +/toolchains/lib/ace_core_components_text_span_mac.a +/toolchains/lib/ace_core_components_texttimer_mac.a +/toolchains/lib/ace_core_components_theme_mac.a +/toolchains/lib/ace_core_components_tip_mac.a +/toolchains/lib/ace_core_components_toast_mac.a +/toolchains/lib/ace_core_components_toggle_mac.a +/toolchains/lib/ace_core_components_tool_bar_mac.a +/toolchains/lib/ace_core_components_touch_listener_mac.a +/toolchains/lib/ace_core_components_track_mac.a +/toolchains/lib/ace_core_components_transform_mac.a +/toolchains/lib/ace_core_components_transition_mac.a +/toolchains/lib/ace_core_components_triangle_mac.a +/toolchains/lib/ace_core_components_tween_mac.a +/toolchains/lib/ace_core_components_video_mac.a +/toolchains/lib/ace_core_components_watch_slider_mac.a +/toolchains/lib/ace_core_components_water_flow_v2_mac.a +/toolchains/lib/ace_core_components_wrap_mac.a +/toolchains/lib/ace_core_interfaces_native_node_mac.a +/toolchains/lib/ace_core_mac.a +/toolchains/lib/ace_core_pipeline_ng_mac.a +/toolchains/lib/ace_kit_common_simulator.a +/toolchains/lib/ace_kit_deviceinfo_simulator.a +/toolchains/lib/ace_kit_file_simulator.a +/toolchains/lib/ace_kit_kvstore_simulator.a +/toolchains/lib/ace_lite.a +/toolchains/lib/ace_napi_static.a +/toolchains/lib/ace_resource.a +/toolchains/lib/animator_static_mac.a +/toolchains/lib/app_check_tool.jar +/toolchains/lib/app_packing_tool.jar +/toolchains/lib/app_unpacking_tool.jar +/toolchains/lib/ark_debugger_static.a +/toolchains/lib/atomicservicebar_static_mac.a +/toolchains/lib/binary_resource_mac_and_windows.a +/toolchains/lib/bridge_accessibility_mac.a +/toolchains/lib/bridge_common_mac.a +/toolchains/lib/card_frontend_mac.a +/toolchains/lib/cli_lite.a +/toolchains/lib/cli_rich.a +/toolchains/lib/componentsnapshot_static_mac.a +/toolchains/lib/componentutils_static_mac.a +/toolchains/lib/configuration_static_mac.a +/toolchains/lib/darwin.a +/toolchains/lib/data_codec.a +/toolchains/lib/declarative_frontend_mac.a +/toolchains/lib/declarative_js_engine_ark_mac.a +/toolchains/lib/declarative_js_engine_bridge_ark_mac.a +/toolchains/lib/device_static_mac.a +/toolchains/lib/displaysync_static_mac.a +/toolchains/lib/dragcontroller_static_mac.a +/toolchains/lib/focuscontroller_static_mac.a +/toolchains/lib/font_static_mac.a +/toolchains/lib/framework_bridge_mac.a +/toolchains/lib/global_resmgr_simulator.a +/toolchains/lib/graphic_utils_static_ide.a +/toolchains/lib/graphics_effect_src.a +/toolchains/lib/grid_static_mac.a +/toolchains/lib/hilog.a +/toolchains/lib/impl_eventhandler.a +/toolchains/lib/inspector_static_mac.a +/toolchains/lib/js_engine_ark_mac.a +/toolchains/lib/js_engine_bridge_ark_mac.a +/toolchains/lib/js_frontend_mac.a +/toolchains/lib/js_inspector_mac.a +/toolchains/lib/jsapp_lite.a +/toolchains/lib/jsapp_rich.a +/toolchains/lib/libace_static_mac.a +/toolchains/lib/libark_ecma_debugger_set.a +/toolchains/lib/libark_js_intl_arm_set.a +/toolchains/lib/libark_js_intl_set.a +/toolchains/lib/libark_jsoptimizer_set_with_maple.a +/toolchains/lib/libark_jsruntime_arm_set.a +/toolchains/lib/libark_jsruntime_set.a +/toolchains/lib/libark_jsruntime_static.a +/toolchains/lib/libark_llvmcodegen_set.a +/toolchains/lib/libark_mock_stub_set.a +/toolchains/lib/libcg.a +/toolchains/lib/libcgaarch64.a +/toolchains/lib/libcglowerer.a +/toolchains/lib/libcgphases.a +/toolchains/lib/libcgx8664.a +/toolchains/lib/libcommandline.a +/toolchains/lib/libdriver_option.a +/toolchains/lib/libhilognapi_src.a +/toolchains/lib/libmaple_driver.a +/toolchains/lib/libmempool.a +/toolchains/lib/libmpl2mpl.a +/toolchains/lib/libmplad.a +/toolchains/lib/libmplbe.a +/toolchains/lib/libmplir.a +/toolchains/lib/libmplme.a +/toolchains/lib/libmplpgo.a +/toolchains/lib/libmplphase.a +/toolchains/lib/libmplutil.a +/toolchains/lib/libnativeapi_battery_simulator.a +/toolchains/lib/libtexgine_drawing.a +/toolchains/lib/libtexgine_source.a +/toolchains/lib/measure_static_mac.a +/toolchains/lib/mediaquery_static_mac.a +/toolchains/lib/mock_image_native.a +/toolchains/lib/mock_ipc_core.a +/toolchains/lib/mock_lite.a +/toolchains/lib/mock_rich.a +/toolchains/lib/mock_utils.a +/toolchains/lib/napi_utils_static_mac.a +/toolchains/lib/nativeapi_locale_simulator.a +/toolchains/lib/observer_static_mac.a +/toolchains/lib/overlay_static_mac.a +/toolchains/lib/parameterbase.a +/toolchains/lib/performancemonitor_static_mac.a +/toolchains/lib/plugin_frontend_mac.a +/toolchains/lib/preview_entrance_source.a +/toolchains/lib/preview_external_source.a +/toolchains/lib/preview_inspector_source.a +/toolchains/lib/preview_osal_source.a +/toolchains/lib/prompt_static_mac.a +/toolchains/lib/promptaction_static_mac.a +/toolchains/lib/render_frame_trace.a +/toolchains/lib/render_service_base_src.a +/toolchains/lib/render_service_client_src.a +/toolchains/lib/rosen_libharfbuzz_mac.a +/toolchains/lib/rosen_libicu_mac.a +/toolchains/lib/rosen_text_inner.a +/toolchains/lib/rosen_text_skia.a +/toolchains/lib/router_static_mac.a +/toolchains/lib/sandbox_utils.a +/toolchains/lib/simulator_osal.a +/toolchains/lib/skia_li +/toolchains/lib/skia_paragraph.a +/toolchains/lib/skia_shaper.a +/toolchains/lib/skia_unicode.a +/toolchains/lib/surface_headers.a +/toolchains/lib/sysparam_simulator.a +/toolchains/lib/ui_ide.a +/toolchains/lib/util_lite.a +/toolchains/lib/util_rich.a +/toolchains/lib/utilsecurec_source.a +/toolchains/lib/websocket_base.a +/toolchains/lib/websocket_server.a +/toolchains/modulecheck/app.json +/toolchains/modulecheck/appStartup.json +/toolchains/modulecheck/commonEvents.json +/toolchains/modulecheck/configuration.json +/toolchains/modulecheck/customUtds.json +/toolchains/modulecheck/distroFilter.json +/toolchains/modulecheck/forms.json +/toolchains/modulecheck/insightIntent.json +/toolchains/modulecheck/menu.json +/toolchains/modulecheck/module.json +/toolchains/modulecheck/pages.json +/toolchains/modulecheck/routerMap.json +/toolchains/modulecheck/shortcuts.json +/toolchains/rawheap_translator +/toolchains/restool +/toolchains/syscap_tool +/toolchains/syscapcheck +/toolchains/syscapcheck/sysCapSchema.json +------------------------------------------------------------ +Notices for software(s): +------------------------------------------------------------ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + +============================================================ +Notices for file(s): +/previewer/common/bin/NotoSansCJK-Regular.ttc +/previewer/common/bin/NotoSerifCJK-Regular.ttc +------------------------------------------------------------ +Notices for software(s): +Software: noto-cjk +Version: Serif2.002 +Path: //third_party/noto-cjk +------------------------------------------------------------ +This Font Software is licensed under the SIL Open Font License, +Version 1.1. + +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font +creation efforts of academic and linguistic communities, and to +provide a free and open framework in which fonts may be shared and +improved in partnership with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply to +any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software +components as distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, +deleting, or substituting -- in part or in whole -- any of the +components of the Original Version, by changing formats or by porting +the Font Software to a new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, +modify, redistribute, and sell modified and unmodified copies of the +Font Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, in +Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the +corresponding Copyright Holder. This restriction only applies to the +primary font name as presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created using +the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + + +============================================================ +Notices for file(s): +/toolchains/lib/protobuf_lite_static.a +/toolchains/lib/protobuf_static.a +/toolchains/lib/protoc_static_lib.a +------------------------------------------------------------ +Notices for software(s): +Software: google/protobuf +Version: 3.13.0 +Path: //third_party/protobuf +------------------------------------------------------------ +Copyright 2008 Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Code generated by the Protocol Buffer compiler is owned by the owner +of the input file used when generating it. This code is not +standalone and requires a support library to be linked with it. This +support library is itself covered by the above license. + + +============================================================ +Notices for file(s): +/toolchains/diff +/toolchains/lib/libringbuffer.a +/toolchains/lib/libupdaterlog.a +/toolchains/lib/libupdaterpackage.a +------------------------------------------------------------ +Notices for software(s): +------------------------------------------------------------ +Copyright (C) 2021 Huawei Device Co., Ltd. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************** + +bsdiff, it is derived from the following: +https://github.com/mendsley/bsdiff +version 4.3 +*************************************************************** + Copyright 2003-2005 Colin Percival + Copyright 2012 Matthew Endsley + All rights reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted providing that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*************************************************************** + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + +============================================================ +Notices for file(s): +/toolchains/lib/libsec_static.a +------------------------------------------------------------ +Notices for software(s): +Software: openEuler:libboundscheck +Version: v1.1.16 +Path: //third_party/bounds_checking_function +------------------------------------------------------------ +木兰宽松许可证, 第2版 + +2020年1月 http://license.coscl.org.cn/MulanPSL2 + +您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束: + +0. 定义 + +“软件” 是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。 + +“贡献” 是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。 + +“贡献者” 是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。 + +“法人实体” 是指提交贡献的机构及其“关联实体”。 + +“关联实体” 是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。 + +1. 授予版权许可 + +每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。 + +2. 授予专利许可 + +每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。 + +3. 无商标许可 + +“本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。 + +4. 分发限制 + +您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。 + +5. 免责声明与责任限制 + +“软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。 + +6. 语言 + +“本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。 + +条款结束 + +如何将木兰宽松许可证,第2版,应用到您的软件 + +如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步: + +1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字; + +2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中; + +3, 请将如下声明文本放入每个源文件的头部注释中。 + +Copyright (c) [Year] [name of copyright holder] +[Software Name] is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. +Mulan Permissive Software License,Version 2 +Mulan Permissive Software License,Version 2 (Mulan PSL v2) + +January 2020 http://license.coscl.org.cn/MulanPSL2 + +Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions: + +0. Definition + +Software means the program and related documents which are licensed under this License and comprise all Contribution(s). + +Contribution means the copyrightable work licensed by a particular Contributor under this License. + +Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License. + +Legal Entity means the entity making a Contribution and all its Affiliates. + +Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, 'control' means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity. + +1. Grant of Copyright License + +Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not. + +2. Grant of Patent License + +Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken. + +3. No Trademark License + +No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in section 4. + +4. Distribution Restriction + +You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software. + +5. Disclaimer of Warranty and Limitation of Liability + +THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT'S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +6. Language + +THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL. + +END OF THE TERMS AND CONDITIONS + +How to Apply the Mulan Permissive Software License,Version 2 (Mulan PSL v2) to Your Software + +To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps: + +Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner; +Create a file named "LICENSE" which contains the whole context of this License in the first directory of your software package; +Attach the statement to the appropriate annotated syntax at the beginning of each source file. +Copyright (c) [Year] [name of copyright holder] +[Software Name] is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. + +============================================================ +Notices for file(s): +/toolchains/lib/curl.a +------------------------------------------------------------ +Notices for software(s): +Software: curl +Version: 8.6.0 +Path: //third_party/curl +------------------------------------------------------------ +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 1996 - 2024, Daniel Stenberg, , and many +contributors, see the THANKS file. + +All rights reserved. + +Permission to use, copy, modify, and distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright +notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall not +be used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization of the copyright holder. + + +============================================================ +Notices for file(s): +/toolchains/ark_disasm +/toolchains/lib/abc2program_frontend_static.a +/toolchains/lib/arkdisassembler_frontend_static.a +/toolchains/lib/arkfile_header_deps.a +/toolchains/lib/libarkassembler_frontend_set_static.a +/toolchains/lib/libarkassembler_frontend_static.a +/toolchains/lib/libarkbase_frontend_set_static.a +/toolchains/lib/libarkbase_frontend_static.a +/toolchains/lib/libarkbase_static.a +/toolchains/lib/libarkbytecodeopt_frontend_static.a +/toolchains/lib/libarkcompiler_frontend_static.a +/toolchains/lib/libarkfile_frontend_set_static.a +/toolchains/lib/libarkfile_frontend_static.a +/toolchains/lib/libarkfile_static.a +/toolchains/lib/libarkziparchive_frontend_set_static.a +/toolchains/lib/libarkziparchive_frontend_static.a +/toolchains/lib/libarkziparchive_static.a +------------------------------------------------------------ +Notices for software(s): +------------------------------------------------------------ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +============================================================ +Notices for file(s): +/toolchains/lib/turbojpeg_static.a +------------------------------------------------------------ +Notices for software(s): +Software: openEuler:libjpeg-turbo +Version: 2.1.1-5.oe2203sp3 +Path: //third_party/libjpeg-turbo +------------------------------------------------------------ +libjpeg-turbo Licenses +====================== + +libjpeg-turbo is covered by three compatible BSD-style open source licenses: + +- The IJG (Independent JPEG Group) License, which is listed in + [README.ijg](README.ijg) + + This license applies to the libjpeg API library and associated programs + (any code inherited from libjpeg, and any modifications to that code.) + +- The Modified (3-clause) BSD License, which is listed below + + This license covers the TurboJPEG API library and associated programs, as + well as the build system. + +- The [zlib License](https://opensource.org/licenses/Zlib) + + This license is a subset of the other two, and it covers the libjpeg-turbo + SIMD extensions. + + +Complying with the libjpeg-turbo Licenses +========================================= + +This section provides a roll-up of the libjpeg-turbo licensing terms, to the +best of our understanding. + +1. If you are distributing a modified version of the libjpeg-turbo source, + then: + + 1. You cannot alter or remove any existing copyright or license notices + from the source. + + **Origin** + - Clause 1 of the IJG License + - Clause 1 of the Modified BSD License + - Clauses 1 and 3 of the zlib License + + 2. You must add your own copyright notice to the header of each source + file you modified, so others can tell that you modified that file (if + there is not an existing copyright header in that file, then you can + simply add a notice stating that you modified the file.) + + **Origin** + - Clause 1 of the IJG License + - Clause 2 of the zlib License + + 3. You must include the IJG README file, and you must not alter any of the + copyright or license text in that file. + + **Origin** + - Clause 1 of the IJG License + +2. If you are distributing only libjpeg-turbo binaries without the source, or + if you are distributing an application that statically links with + libjpeg-turbo, then: + + 1. Your product documentation must include a message stating: + + This software is based in part on the work of the Independent JPEG + Group. + + **Origin** + - Clause 2 of the IJG license + + 2. If your binary distribution includes or uses the TurboJPEG API, then + your product documentation must include the text of the Modified BSD + License (see below.) + + **Origin** + - Clause 2 of the Modified BSD License + +3. You cannot use the name of the IJG or The libjpeg-turbo Project or the + contributors thereof in advertising, publicity, etc. + + **Origin** + - IJG License + - Clause 3 of the Modified BSD License + +4. The IJG and The libjpeg-turbo Project do not warrant libjpeg-turbo to be + free of defects, nor do we accept any liability for undesirable + consequences resulting from your use of the software. + + **Origin** + - IJG License + - Modified BSD License + - zlib License + + +The Modified (3-clause) BSD License +=================================== + +Copyright (C)2009-2021 D. R. Commander. All Rights Reserved.
+Copyright (C)2015 Viktor Szathmáry. All Rights Reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +- Neither the name of the libjpeg-turbo Project nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + +Why Three Licenses? +=================== + +The zlib License could have been used instead of the Modified (3-clause) BSD +License, and since the IJG License effectively subsumes the distribution +conditions of the zlib License, this would have effectively placed +libjpeg-turbo binary distributions under the IJG License. However, the IJG +License specifically refers to the Independent JPEG Group and does not extend +attribution and endorsement protections to other entities. Thus, it was +desirable to choose a license that granted us the same protections for new code +that were granted to the IJG for code derived from their software. + + +============================================================ +Notices for file(s): +/js/build-tools/ace-loader/lib/parse +------------------------------------------------------------ +Notices for software(s): +Software: parse5 +Version: v7.1.2 +Path: //third_party/parse5 +------------------------------------------------------------ +Copyright (c) 2013-2019 Ivan Nikulin (ifaaan@gmail.com, https://github.com/inikulin) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +============================================================ +Notices for file(s): +/toolchains/lib/liblz4_static.a +------------------------------------------------------------ +Notices for software(s): +Software: lz4 +Version: 1.9.4 +Path: //third_party/lz4 +------------------------------------------------------------ +This repository uses 2 different licenses : +- all files in the `lib` directory use a BSD 2-Clause license +- all other files use a GPLv2 license, unless explicitly stated otherwise + +Relevant license is reminded at the top of each source file, +and with presence of COPYING or LICENSE file in associated directories. + +This model is selected to emphasize that +files in the `lib` directory are designed to be included into 3rd party applications, +while all other files, in `programs`, `tests` or `examples`, +are intended to be used "as is", as part of their intended scenarios, +with no intention to support 3rd party integration use cases. + + +============================================================ +Notices for file(s): +/toolchains/lib/websockets_static.a +------------------------------------------------------------ +Notices for software(s): +Software: libwebsockets +Version: 4.3.3 +Path: //third_party/libwebsockets +------------------------------------------------------------ +Libwebsockets and included programs are provided under the terms of the +MIT license shown below, with the exception that some sources are under +a similar permissive license like BSD, or are explicitly CC0 / public +domain to remove any obstacles from basing differently-licensed code on +them. + +Original liberal license retained: + + - lib/misc/sha-1.c - 3-clause BSD license retained, link to original [BSD3] + - win32port/zlib - ZLIB license (see zlib.h) [ZLIB] + - lib/tls/mbedtls/wrapper - Apache 2.0 (only built if linked against mbedtls) [APACHE2] + lib/tls/mbedtls/mbedtls-extensions.c + - lib/misc/base64-decode.c - already MIT + - lib/misc/ieeehalfprecision.c - 2-clause BSD license retained [BSD2] + +Relicensed to MIT: + + - lib/misc/daemonize.c - relicensed from Public Domain to MIT, + link to original Public Domain version + - lib/plat/windows/windows-resolv.c - relicensed from "Beerware v42" to MIT + +Public Domain (CC-zero) to simplify reuse: + + - test-apps/*.c + - test-apps/*.h + - minimal-examples/* + - lwsws/* + +Although libwebsockets is available under a permissive license, it does not +change the reality of dealing with large lumps of external code... if your +copy diverges it is guaranteed to contain security problems after a while +and can be very painful to pick backports (especially since historically, +we are very hot on cleaning and refactoring the codebase). The least +painful and lowest risk way remains sending your changes and fixes upstream +to us so you can easily use later releases and fixes. + +## MIT License applied to libwebsockets + +https://opensource.org/licenses/MIT + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. + +## BSD2 + +``` + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. +``` + +## BSD3 + +For convenience, a copy of the license on `./lib/misc/sha-1.c`. In binary +distribution, this applies to builds with ws support enabled, and without +`LWS_WITHOUT_BUILTIN_SHA1` at cmake. + +``` +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH +``` + +## ZLIB + +For convenience, a copy of the license on zlib. In binary distribution, +this applies for win32 builds with internal zlib only. You can avoid +building any zlib usage or copy at all with `-DLWS_WITH_ZLIB=0` (the +default), and so avoid needing to observe the license for binary +distribution that doesn't include the related code. + +``` + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu +``` + + +## APACHE2 + +For convenience, a copy of the license on the mbedtls wrapper part. In binary +distribution, this applies only when building lws against mbedtls. + +The canonical license application to source files uses the URL reference, so the +whole is not reproduced here. + +``` +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +``` + +## CC0 + +For convenience,the full text of CC0 dedication found on the lws examples. +The intention of this is to dedicate the examples to the public domain, so +users can build off and modify them without any constraint. + +``` +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: + + the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; + moral rights retained by the original author(s) and/or performer(s); + publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; + rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; + rights protecting the extraction, dissemination, use and reuse of data in a Work; + database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and + other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. + Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. + Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. + Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. +``` + + + +============================================================ +Notices for file(s): +/js/build-tools/ace-loader/lib/element.js +/js/build-tools/ace-loader/lib/json.js +/js/build-tools/ace-loader/lib/legacy.js +/js/build-tools/ace-loader/lib/loader.js +/js/build-tools/ace-loader/lib/parser.js +/js/build-tools/ace-loader/lib/script.js +/js/build-tools/ace-loader/lib/scripter +/js/build-tools/ace-loader/lib/style.js +/js/build-tools/ace-loader/lib/styler +/js/build-tools/ace-loader/lib/template.js +/js/build-tools/ace-loader/lib/util.js +------------------------------------------------------------ +Notices for software(s): +Software: weex-loader +Version: v0.7.12 +Path: //third_party/weex-loader +------------------------------------------------------------ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2019 Apache Incubator-Weex + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +============================================================ +Notices for file(s): +/ets/build-tools/ets-loader +/ets/build-tools/ets-loader/codegen +/ets/build-tools/ets-loader/codegen/codegen +/ets/build-tools/ets-loader/compile_plugin.js +/ets/build-tools/ets-loader/components +/ets/build-tools/ets-loader/components/components +/ets/build-tools/ets-loader/declarations +/ets/build-tools/ets-loader/declarations/declarations +/ets/build-tools/ets-loader/form_components +/ets/build-tools/ets-loader/form_components/form_components +/ets/build-tools/ets-loader/kit_configs +/ets/build-tools/ets-loader/kit_configs/kit_configs +/ets/build-tools/ets-loader/lib +/ets/build-tools/ets-loader/lib/lib +/ets/build-tools/ets-loader/main.js +/ets/build-tools/ets-loader/node_modules +/ets/build-tools/ets-loader/npm-install.js +/ets/build-tools/ets-loader/obfuscateWhiteList.json5 +/ets/build-tools/ets-loader/package-lock.json +/ets/build-tools/ets-loader/package.json +/ets/build-tools/ets-loader/rollup.config.js +/ets/build-tools/ets-loader/server +/ets/build-tools/ets-loader/server/server +/ets/build-tools/ets-loader/sysResource.js +/ets/build-tools/ets-loader/tsconfig.esm.json +/ets/build-tools/ets-loader/tsconfig.json +/ets/build-tools/ets-loader/webpack.config.js +/ets/component +/ets/component/build_config.json +/ets/component/component_config.json +/ets/component/form_config.json +/js/build-tools/ace-loader +/js/build-tools/ace-loader/babel.config.js +/js/build-tools/ace-loader/index.js +/js/build-tools/ace-loader/lib/lib +/js/build-tools/ace-loader/main.product.js +/js/build-tools/ace-loader/node_modules +/js/build-tools/ace-loader/npm-install.js +/js/build-tools/ace-loader/package-lock.json +/js/build-tools/ace-loader/package.json +/js/build-tools/ace-loader/sample +/js/build-tools/ace-loader/webpack.lite.config.js +/js/build-tools/ace-loader/webpack.rich.config.js +/toolchains/lib/arraylist_static.a +/toolchains/lib/buffer_static.a +/toolchains/lib/deque_static.a +/toolchains/lib/hashmap_static.a +/toolchains/lib/hashset_static.a +/toolchains/lib/lightweightmap_static.a +/toolchains/lib/lightweightset_static.a +/toolchains/lib/linkedlist_static.a +/toolchains/lib/list_static.a +/toolchains/lib/plainarray_static.a +/toolchains/lib/queue_static.a +/toolchains/lib/stack_static.a +/toolchains/lib/struct_static.a +/toolchains/lib/treemap_static.a +/toolchains/lib/treeset_static.a +/toolchains/lib/vector_static.a +------------------------------------------------------------ +Notices for software(s): +------------------------------------------------------------ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + +============================================================ +Notices for file(s): +/toolchains +/toolchains/OpenHarmony.p12 +/toolchains/OpenHarmonyProfileDebug.pem +/toolchains/OpenHarmonyProfileRelease.pem +/toolchains/UnsgnedDebugProfileTemplate.json +/toolchains/UnsgnedReleasedProfileTemplate.json +/toolchains/hap-sign-tool.jar +/toolchains/lib/libGLES.a +------------------------------------------------------------ +Notices for software(s): +Software: Khronos Group - OpenGL ES +Version: 63161d674db04a96635c6ab300db793e83f6762c +Path: //third_party/openGLES +------------------------------------------------------------ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +============================================================ +Notices for file(s): +/previewer/common/bin/HMOSColorEmojiCompat.ttf +/previewer/common/bin/HMOSColorEmojiFlags.ttf +/previewer/common/bin/HMSymbolVF.ttf +/previewer/common/bin/HarmonyOS_Sans.ttf +/previewer/common/bin/HarmonyOS_Sans_Condensed.ttf +/previewer/common/bin/HarmonyOS_Sans_Condensed_Italic.ttf +/previewer/common/bin/HarmonyOS_Sans_Digit.ttf +/previewer/common/bin/HarmonyOS_Sans_Digit_Medium.ttf +/previewer/common/bin/HarmonyOS_Sans_Italic.ttf +/previewer/common/bin/HarmonyOS_Sans_Naskh_Arabic.ttf +/previewer/common/bin/HarmonyOS_Sans_Naskh_Arabic_UI.ttf +/previewer/common/bin/HarmonyOS_Sans_SC.ttf +/previewer/common/bin/HarmonyOS_Sans_TC.ttf +/previewer/common/bin/hm_symbol_config.json +/previewer/common/bin/hm_symbol_config_next.json +------------------------------------------------------------ +Notices for software(s): +------------------------------------------------------------ +System resource package is licensed under Apache License Version 2.0. +-------------------------------------------------------------------------------- + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + +============================================================ +Notices for file(s): +/ets/build-tools/ets-loader/bin/ark/build-mac/bin/es2abc +/ets/build-tools/ets-loader/bin/ark/build-mac/bin/merge_abc +/ets/build-tools/ets-loader/bin/ark/build-mac/legacy_api8 +/ets/build-tools/ets-loader/bin/ark/build-mac/legacy_api8/bin/js2abc +/ets/build-tools/ets-loader/bin/ark/build-mac/legacy_api8/node_modules +/ets/build-tools/ets-loader/bin/ark/build-mac/legacy_api8/package-lock.json +/ets/build-tools/ets-loader/bin/ark/build-mac/legacy_api8/package.json +/ets/build-tools/ets-loader/bin/ark/build-mac/legacy_api8/src/index.js +/ets/build-tools/ets-loader/bin/ark/ts2abc.js +/js/build-tools/ace-loader/bin/ark/build-mac/bin/es2abc +/js/build-tools/ace-loader/bin/ark/build-mac/bin/merge_abc +/js/build-tools/ace-loader/bin/ark/build-mac/legacy_api8 +/js/build-tools/ace-loader/bin/ark/build-mac/legacy_api8/bin/js2abc +/js/build-tools/ace-loader/bin/ark/build-mac/legacy_api8/node_modules +/js/build-tools/ace-loader/bin/ark/build-mac/legacy_api8/package-lock.json +/js/build-tools/ace-loader/bin/ark/build-mac/legacy_api8/package.json +/js/build-tools/ace-loader/bin/ark/build-mac/legacy_api8/src/index.js +/js/build-tools/ace-loader/bin/ark/ts2abc.js +/toolchains/lib/assembly_proto_static.a +/toolchains/lib/es2panda_lib.a +/toolchains/lib/panda_assembly_proto_static.a +------------------------------------------------------------ +Notices for software(s): +------------------------------------------------------------ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + The "ts2panda\scripts\diagnosticMessages.json" file may contain + some information or content from the following software: + TypeScript + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. All rights reserved. + Licensed under the Apache License, Version 2.0 (the "License"); you may not use + this file except in compliance with the License. You may obtain a copy of the + License at http://www.apache.org/licenses/LICENSE-2.0 + + THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED + WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, + MERCHANTABLITY OR NON-INFRINGEMENT. + + See the Apache Version 2.0 License for specific language governing permissions + and limitations under the License. + ***************************************************************************** */ + Apache License 2.0 + +============================================================ +Notices for file(s): +/toolchains/lib/libuv_source.a +/toolchains/lib/uv_static.a +------------------------------------------------------------ +Notices for software(s): +Software: libuv +Version: v1.48.0 +Path: //third_party/libuv +------------------------------------------------------------ +Copyright (c) 2015-present libuv project contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. + + +============================================================ +Notices for file(s): +/toolchains/lib/harfbuzz_static.a +------------------------------------------------------------ +Notices for software(s): +Software: openEuler:HarfBuzz +Version: 2.8.2-4.oe2203sp3 +Path: //third_party/harfbuzz +------------------------------------------------------------ +HarfBuzz is licensed under the so-called "Old MIT" license. Details follow. +For parts of HarfBuzz that are licensed under different licenses see individual +files names COPYING in subdirectories where applicable. + +Copyright © 2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020 Google, Inc. +Copyright © 2018,2019,2020 Ebrahim Byagowi +Copyright © 2019,2020 Facebook, Inc. +Copyright © 2012 Mozilla Foundation +Copyright © 2011 Codethink Limited +Copyright © 2008,2010 Nokia Corporation and/or its subsidiary(-ies) +Copyright © 2009 Keith Stribley +Copyright © 2009 Martin Hosken and SIL International +Copyright © 2007 Chris Wilson +Copyright © 2005,2006,2020,2021 Behdad Esfahbod +Copyright © 2005 David Turner +Copyright © 2004,2007,2008,2009,2010 Red Hat, Inc. +Copyright © 1998-2004 David Turner and Werner Lemberg + +For full copyright notices consult the individual files in the package. + + +Permission is hereby granted, without written agreement and without +license or royalty fees, to use, copy, modify, and distribute this +software and its documentation for any purpose, provided that the +above copyright notice and the following two paragraphs appear in +all copies of this software. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + + +============================================================ +Notices for file(s): +/toolchains/lib/libusb_source.a +/toolchains/libusb_shared.dylib +------------------------------------------------------------ +Notices for software(s): +Software: openEuler:libusbx +Version: 1.0.26-1.oe2203sp3 +Path: //third_party/libusb +------------------------------------------------------------ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + + + +============================================================ +Notices for file(s): +/toolchains/lib/libz.a +------------------------------------------------------------ +Notices for software(s): +Software: zlib +Version: 1.3.1 +Path: //third_party/zlib +------------------------------------------------------------ +Copyright notice: + + (C) 1995-2022 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + +============================================================ +Notices for file(s): +/toolchains/lib/node_header_notice.a +------------------------------------------------------------ +Notices for software(s): +Software: Node.js +Version: v18.20.1 +Path: //third_party/node +------------------------------------------------------------ +Node.js is licensed for use as follows: + +""" +Copyright Node.js contributors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +""" + +This license applies to parts of Node.js originating from the +https://github.com/joyent/node repository: + +""" +Copyright Joyent, Inc. and other Node contributors. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +""" + +The Node.js license applies to all parts of Node.js that are not externally +maintained libraries. + +The externally maintained libraries used by Node.js are: + +- Acorn, located at deps/acorn, is licensed as follows: + """ + MIT License + + Copyright (C) 2012-2022 by various contributors (see AUTHORS) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + """ + +- c-ares, located at deps/cares, is licensed as follows: + """ + MIT License + + Copyright (c) 1998 Massachusetts Institute of Technology + Copyright (c) 2007 - 2023 Daniel Stenberg with many contributors, see AUTHORS + file. + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + """ + +- cjs-module-lexer, located at deps/cjs-module-lexer, is licensed as follows: + """ + MIT License + ----------- + + Copyright (C) 2018-2020 Guy Bedford + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + """ + +- ittapi, located at deps/v8/third_party/ittapi, is licensed as follows: + """ + Copyright (c) 2019 Intel Corporation. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + """ + +- ICU, located at deps/icu-small, is licensed as follows: + """ + UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE + + See Terms of Use + for definitions of Unicode Inc.’s Data Files and Software. + + NOTICE TO USER: Carefully read the following legal agreement. + BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S + DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), + YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE + TERMS AND CONDITIONS OF THIS AGREEMENT. + IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE + THE DATA FILES OR SOFTWARE. + + COPYRIGHT AND PERMISSION NOTICE + + Copyright © 1991-2023 Unicode, Inc. All rights reserved. + Distributed under the Terms of Use in https://www.unicode.org/copyright.html. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of the Unicode data files and any associated documentation + (the "Data Files") or Unicode software and any associated documentation + (the "Software") to deal in the Data Files or Software + without restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, and/or sell copies of + the Data Files or Software, and to permit persons to whom the Data Files + or Software are furnished to do so, provided that either + (a) this copyright and permission notice appear with all copies + of the Data Files or Software, or + (b) this copyright and permission notice appear in associated + Documentation. + + THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF + ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT OF THIRD PARTY RIGHTS. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS + NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL + DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THE DATA FILES OR SOFTWARE. + + Except as contained in this notice, the name of a copyright holder + shall not be used in advertising or otherwise to promote the sale, + use or other dealings in these Data Files or Software without prior + written authorization of the copyright holder. + + ---------------------------------------------------------------------- + + Third-Party Software Licenses + + This section contains third-party software notices and/or additional + terms for licensed third-party software components included within ICU + libraries. + + ---------------------------------------------------------------------- + + ICU License - ICU 1.8.1 to ICU 57.1 + + COPYRIGHT AND PERMISSION NOTICE + + Copyright (c) 1995-2016 International Business Machines Corporation and others + All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, and/or sell copies of the Software, and to permit persons + to whom the Software is furnished to do so, provided that the above + copyright notice(s) and this permission notice appear in all copies of + the Software and that both the above copyright notice(s) and this + permission notice appear in supporting documentation. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY + SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER + RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + Except as contained in this notice, the name of a copyright holder + shall not be used in advertising or otherwise to promote the sale, use + or other dealings in this Software without prior written authorization + of the copyright holder. + + All trademarks and registered trademarks mentioned herein are the + property of their respective owners. + + ---------------------------------------------------------------------- + + Chinese/Japanese Word Break Dictionary Data (cjdict.txt) + + # The Google Chrome software developed by Google is licensed under + # the BSD license. Other software included in this distribution is + # provided under other licenses, as set forth below. + # + # The BSD License + # http://opensource.org/licenses/bsd-license.php + # Copyright (C) 2006-2008, Google Inc. + # + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions are met: + # + # Redistributions of source code must retain the above copyright notice, + # this list of conditions and the following disclaimer. + # Redistributions in binary form must reproduce the above + # copyright notice, this list of conditions and the following + # disclaimer in the documentation and/or other materials provided with + # the distribution. + # Neither the name of Google Inc. nor the names of its + # contributors may be used to endorse or promote products derived from + # this software without specific prior written permission. + # + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + # + # + # The word list in cjdict.txt are generated by combining three word lists + # listed below with further processing for compound word breaking. The + # frequency is generated with an iterative training against Google web + # corpora. + # + # * Libtabe (Chinese) + # - https://sourceforge.net/project/?group_id=1519 + # - Its license terms and conditions are shown below. + # + # * IPADIC (Japanese) + # - http://chasen.aist-nara.ac.jp/chasen/distribution.html + # - Its license terms and conditions are shown below. + # + # ---------COPYING.libtabe ---- BEGIN-------------------- + # + # /* + # * Copyright (c) 1999 TaBE Project. + # * Copyright (c) 1999 Pai-Hsiang Hsiao. + # * All rights reserved. + # * + # * Redistribution and use in source and binary forms, with or without + # * modification, are permitted provided that the following conditions + # * are met: + # * + # * . Redistributions of source code must retain the above copyright + # * notice, this list of conditions and the following disclaimer. + # * . Redistributions in binary form must reproduce the above copyright + # * notice, this list of conditions and the following disclaimer in + # * the documentation and/or other materials provided with the + # * distribution. + # * . Neither the name of the TaBE Project nor the names of its + # * contributors may be used to endorse or promote products derived + # * from this software without specific prior written permission. + # * + # * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + # * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + # * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + # * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + # * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + # * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + # * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + # * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + # * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + # * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + # * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + # * OF THE POSSIBILITY OF SUCH DAMAGE. + # */ + # + # /* + # * Copyright (c) 1999 Computer Systems and Communication Lab, + # * Institute of Information Science, Academia + # * Sinica. All rights reserved. + # * + # * Redistribution and use in source and binary forms, with or without + # * modification, are permitted provided that the following conditions + # * are met: + # * + # * . Redistributions of source code must retain the above copyright + # * notice, this list of conditions and the following disclaimer. + # * . Redistributions in binary form must reproduce the above copyright + # * notice, this list of conditions and the following disclaimer in + # * the documentation and/or other materials provided with the + # * distribution. + # * . Neither the name of the Computer Systems and Communication Lab + # * nor the names of its contributors may be used to endorse or + # * promote products derived from this software without specific + # * prior written permission. + # * + # * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + # * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + # * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + # * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + # * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + # * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + # * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + # * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + # * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + # * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + # * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + # * OF THE POSSIBILITY OF SUCH DAMAGE. + # */ + # + # Copyright 1996 Chih-Hao Tsai @ Beckman Institute, + # University of Illinois + # c-tsai4@uiuc.edu http://casper.beckman.uiuc.edu/~c-tsai4 + # + # ---------------COPYING.libtabe-----END-------------------------------- + # + # + # ---------------COPYING.ipadic-----BEGIN------------------------------- + # + # Copyright 2000, 2001, 2002, 2003 Nara Institute of Science + # and Technology. All Rights Reserved. + # + # Use, reproduction, and distribution of this software is permitted. + # Any copy of this software, whether in its original form or modified, + # must include both the above copyright notice and the following + # paragraphs. + # + # Nara Institute of Science and Technology (NAIST), + # the copyright holders, disclaims all warranties with regard to this + # software, including all implied warranties of merchantability and + # fitness, in no event shall NAIST be liable for + # any special, indirect or consequential damages or any damages + # whatsoever resulting from loss of use, data or profits, whether in an + # action of contract, negligence or other tortuous action, arising out + # of or in connection with the use or performance of this software. + # + # A large portion of the dictionary entries + # originate from ICOT Free Software. The following conditions for ICOT + # Free Software applies to the current dictionary as well. + # + # Each User may also freely distribute the Program, whether in its + # original form or modified, to any third party or parties, PROVIDED + # that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear + # on, or be attached to, the Program, which is distributed substantially + # in the same form as set out herein and that such intended + # distribution, if actually made, will neither violate or otherwise + # contravene any of the laws and regulations of the countries having + # jurisdiction over the User or the intended distribution itself. + # + # NO WARRANTY + # + # The program was produced on an experimental basis in the course of the + # research and development conducted during the project and is provided + # to users as so produced on an experimental basis. Accordingly, the + # program is provided without any warranty whatsoever, whether express, + # implied, statutory or otherwise. The term "warranty" used herein + # includes, but is not limited to, any warranty of the quality, + # performance, merchantability and fitness for a particular purpose of + # the program and the nonexistence of any infringement or violation of + # any right of any third party. + # + # Each user of the program will agree and understand, and be deemed to + # have agreed and understood, that there is no warranty whatsoever for + # the program and, accordingly, the entire risk arising from or + # otherwise connected with the program is assumed by the user. + # + # Therefore, neither ICOT, the copyright holder, or any other + # organization that participated in or was otherwise related to the + # development of the program and their respective officials, directors, + # officers and other employees shall be held liable for any and all + # damages, including, without limitation, general, special, incidental + # and consequential damages, arising out of or otherwise in connection + # with the use or inability to use the program or any product, material + # or result produced or otherwise obtained by using the program, + # regardless of whether they have been advised of, or otherwise had + # knowledge of, the possibility of such damages at any time during the + # project or thereafter. Each user will be deemed to have agreed to the + # foregoing by his or her commencement of use of the program. The term + # "use" as used herein includes, but is not limited to, the use, + # modification, copying and distribution of the program and the + # production of secondary products from the program. + # + # In the case where the program, whether in its original form or + # modified, was distributed or delivered to or received by a user from + # any person, organization or entity other than ICOT, unless it makes or + # grants independently of ICOT any specific warranty to the user in + # writing, such person, organization or entity, will also be exempted + # from and not be held liable to the user for any such damages as noted + # above as far as the program is concerned. + # + # ---------------COPYING.ipadic-----END---------------------------------- + + ---------------------------------------------------------------------- + + Lao Word Break Dictionary Data (laodict.txt) + + # Copyright (C) 2016 and later: Unicode, Inc. and others. + # License & terms of use: http://www.unicode.org/copyright.html + # Copyright (c) 2015 International Business Machines Corporation + # and others. All Rights Reserved. + # + # Project: https://github.com/rober42539/lao-dictionary + # Dictionary: https://github.com/rober42539/lao-dictionary/laodict.txt + # License: https://github.com/rober42539/lao-dictionary/LICENSE.txt + # (copied below) + # + # This file is derived from the above dictionary version of Nov 22, 2020 + # ---------------------------------------------------------------------- + # Copyright (C) 2013 Brian Eugene Wilson, Robert Martin Campbell. + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions are met: + # + # Redistributions of source code must retain the above copyright notice, this + # list of conditions and the following disclaimer. Redistributions in binary + # form must reproduce the above copyright notice, this list of conditions and + # the following disclaimer in the documentation and/or other materials + # provided with the distribution. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + # OF THE POSSIBILITY OF SUCH DAMAGE. + # -------------------------------------------------------------------------- + + ---------------------------------------------------------------------- + + Burmese Word Break Dictionary Data (burmesedict.txt) + + # Copyright (c) 2014 International Business Machines Corporation + # and others. All Rights Reserved. + # + # This list is part of a project hosted at: + # github.com/kanyawtech/myanmar-karen-word-lists + # + # -------------------------------------------------------------------------- + # Copyright (c) 2013, LeRoy Benjamin Sharon + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions + # are met: Redistributions of source code must retain the above + # copyright notice, this list of conditions and the following + # disclaimer. Redistributions in binary form must reproduce the + # above copyright notice, this list of conditions and the following + # disclaimer in the documentation and/or other materials provided + # with the distribution. + # + # Neither the name Myanmar Karen Word Lists, nor the names of its + # contributors may be used to endorse or promote products derived + # from this software without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + # SUCH DAMAGE. + # -------------------------------------------------------------------------- + + ---------------------------------------------------------------------- + + Time Zone Database + + ICU uses the public domain data and code derived from Time Zone + Database for its time zone support. The ownership of the TZ database + is explained in BCP 175: Procedure for Maintaining the Time Zone + Database section 7. + + # 7. Database Ownership + # + # The TZ database itself is not an IETF Contribution or an IETF + # document. Rather it is a pre-existing and regularly updated work + # that is in the public domain, and is intended to remain in the + # public domain. Therefore, BCPs 78 [RFC5378] and 79 [RFC3979] do + # not apply to the TZ Database or contributions that individuals make + # to it. Should any claims be made and substantiated against the TZ + # Database, the organization that is providing the IANA + # Considerations defined in this RFC, under the memorandum of + # understanding with the IETF, currently ICANN, may act in accordance + # with all competent court orders. No ownership claims will be made + # by ICANN or the IETF Trust on the database or the code. Any person + # making a contribution to the database or code waives all rights to + # future claims in that contribution or in the TZ Database. + + ---------------------------------------------------------------------- + + Google double-conversion + + Copyright 2006-2011, the V8 project authors. All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ---------------------------------------------------------------------- + + File: aclocal.m4 (only for ICU4C) + Section: pkg.m4 - Macros to locate and utilise pkg-config. + + Copyright © 2004 Scott James Remnant . + Copyright © 2012-2015 Dan Nicholson + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. + + As a special exception to the GNU General Public License, if you + distribute this file as part of a program that contains a + configuration script generated by Autoconf, you may include it under + the same distribution terms that you use for the rest of that + program. + + (The condition for the exception is fulfilled because + ICU4C includes a configuration script generated by Autoconf, + namely the `configure` script.) + + ---------------------------------------------------------------------- + + File: config.guess (only for ICU4C) + + This file is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + + As a special exception to the GNU General Public License, if you + distribute this file as part of a program that contains a + configuration script generated by Autoconf, you may include it under + the same distribution terms that you use for the rest of that + program. This Exception is an additional permission under section 7 + of the GNU General Public License, version 3 ("GPLv3"). + + (The condition for the exception is fulfilled because + ICU4C includes a configuration script generated by Autoconf, + namely the `configure` script.) + + ---------------------------------------------------------------------- + + File: install-sh (only for ICU4C) + + Copyright 1991 by the Massachusetts Institute of Technology + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation, and that the name of M.I.T. not be used in advertising or + publicity pertaining to distribution of the software without specific, + written prior permission. M.I.T. makes no representations about the + suitability of this software for any purpose. It is provided "as is" + without express or implied warranty. + """ + +- libuv, located at deps/uv, is licensed as follows: + """ + Copyright (c) 2015-present libuv project contributors. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. + This license applies to parts of libuv originating from the + https://github.com/joyent/libuv repository: + + ==== + + Copyright Joyent, Inc. and other Node contributors. All rights reserved. + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. + + ==== + + This license applies to all parts of libuv that are not externally + maintained libraries. + + The externally maintained libraries used by libuv are: + + - tree.h (from FreeBSD), copyright Niels Provos. Two clause BSD license. + + - inet_pton and inet_ntop implementations, contained in src/inet.c, are + copyright the Internet Systems Consortium, Inc., and licensed under the ISC + license. + """ + +- llhttp, located at deps/llhttp, is licensed as follows: + """ + This software is licensed under the MIT License. + + Copyright Fedor Indutny, 2018. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. + """ + +- corepack, located at deps/corepack, is licensed as follows: + """ + **Copyright © Corepack contributors** + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + """ + +- undici, located at deps/undici, is licensed as follows: + """ + MIT License + + Copyright (c) Matteo Collina and Undici contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + """ + +- postject, located at test/fixtures/postject-copy, is licensed as follows: + """ + Postject is licensed for use as follows: + + """ + MIT License + + Copyright (c) 2022 Postman, Inc + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + """ + + The Postject license applies to all parts of Postject that are not externally + maintained libraries. + + The externally maintained libraries used by Postject are: + + - LIEF, located at vendor/LIEF, is licensed as follows: + """ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + \ No newline at end of file diff --git a/static/test-demo/config/ets/oh-uni-package.json b/static/test-demo/config/ets/oh-uni-package.json new file mode 100644 index 0000000..0ac60e5 --- /dev/null +++ b/static/test-demo/config/ets/oh-uni-package.json @@ -0,0 +1,10 @@ +{ + "apiVersion": "15", + "displayName": "Ets", + "meta": { + "metaVersion": "3.0.0" + }, + "path": "ets", + "releaseType": "Release", + "version": "5.0.3.135" +} \ No newline at end of file diff --git a/static/test-demo/package.json b/static/test-demo/package.json new file mode 100644 index 0000000..74a5ee4 --- /dev/null +++ b/static/test-demo/package.json @@ -0,0 +1,147 @@ +{ + "name": "arkanalyzer", + "version": "1.0.0", + "description": "简体中文 | [English](./README.en.md)", + "main": "rollup.config.js", + "directories": { + "doc": "docs", + "test": "tests" + }, + "dependencies": { + "acorn": "^8.15.0", + "acorn-walk": "^8.3.4", + "ansi-styles": "^5.2.0", + "arg": "^4.1.3", + "argparse": "^2.0.1", + "assertion-error": "^1.1.0", + "balanced-match": "^1.0.2", + "brace-expansion": "^1.1.11", + "cac": "^6.7.14", + "chai": "^4.5.0", + "check-error": "^1.0.3", + "commander": "^11.0.0", + "concat-map": "^0.0.1", + "confbox": "^0.1.8", + "create-require": "^1.1.1", + "cross-spawn": "^7.0.6", + "date-format": "^4.0.14", + "debug": "^4.4.1", + "deep-eql": "^4.1.4", + "diff": "^4.0.2", + "diff-sequences": "^29.6.3", + "entities": "^4.5.0", + "esbuild": "^0.21.5", + "estree-walker": "^3.0.3", + "execa": "^8.0.1", + "flatted": "^3.3.3", + "fs-extra": "^8.1.0", + "fs.realpath": "^1.0.0", + "function-bind": "^1.1.2", + "get-func-name": "^2.0.2", + "get-stream": "^8.0.1", + "glob": "^7.2.3", + "graceful-fs": "^4.2.11", + "has-flag": "^4.0.0", + "hasown": "^2.0.2", + "html-escaper": "^2.0.2", + "human-signals": "^5.0.0", + "inflight": "^1.0.6", + "inherits": "^2.0.4", + "is-core-module": "^2.16.1", + "is-stream": "^3.0.0", + "isexe": "^2.0.0", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.1.7", + "js-tokens": "^9.0.1", + "json5": "^2.2.3", + "jsonfile": "^4.0.0", + "linkify-it": "^5.0.0", + "local-pkg": "^0.5.1", + "log4js": "^6.7.1", + "loupe": "^2.3.7", + "lunr": "^2.3.9", + "magic-string": "^0.30.17", + "magicast": "^0.3.5", + "make-dir": "^4.0.0", + "make-error": "^1.3.6", + "markdown-it": "^14.1.0", + "mdurl": "^2.0.0", + "merge-stream": "^2.0.0", + "mimic-fn": "^4.0.0", + "minimatch": "^3.1.2", + "mlly": "^1.7.4", + "ms": "^2.1.3", + "nanoid": "^3.3.11", + "npm-run-path": "^5.3.0", + "ohos-typescript": "^4.9.5-r4", + "once": "^1.4.0", + "onetime": "^6.0.0", + "p-limit": "^5.0.0", + "path-is-absolute": "^1.0.1", + "path-key": "^3.1.1", + "path-parse": "^1.0.7", + "pathe": "^1.1.2", + "pathval": "^1.1.1", + "picocolors": "^1.1.1", + "picomatch": "^4.0.3", + "pkg-types": "^1.3.1", + "postcss": "^8.5.4", + "pretty-format": "^29.7.0", + "punycode.js": "^2.3.1", + "react-is": "^18.3.1", + "resolve": "^1.22.10", + "rfdc": "^1.4.1", + "rollup": "^4.49.0", + "semver": "^7.7.2", + "shebang-command": "^2.0.0", + "shebang-regex": "^3.0.0", + "siginfo": "^2.0.0", + "signal-exit": "^4.1.0", + "source-map-js": "^1.2.1", + "stackback": "^0.0.2", + "std-env": "^3.9.0", + "streamroller": "^3.1.5", + "strip-final-newline": "^3.0.0", + "strip-literal": "^2.1.1", + "supports-color": "^7.2.0", + "supports-preserve-symlinks-flag": "^1.0.0", + "test-exclude": "^6.0.0", + "tinybench": "^2.9.0", + "tinypool": "^0.8.4", + "tinyspy": "^2.2.1", + "ts-node": "^10.9.2", + "tslib": "^2.8.1", + "type-detect": "^4.1.0", + "typedoc": "^0.28.5", + "typedoc-plugin-markdown": "^4.6.4", + "uc.micro": "^2.1.0", + "ufo": "^1.6.1", + "undici-types": "^6.21.0", + "universalify": "^0.1.2", + "v8-compile-cache-lib": "^3.0.1", + "vite": "^5.4.19", + "vite-node": "^1.6.1", + "vitest": "^1.6.1", + "which": "^2.0.2", + "why-is-node-running": "^2.3.0", + "wrappy": "^1.0.2", + "yaml": "^2.8.0", + "yn": "^3.1.1", + "yocto-queue": "^1.2.1" + }, + "devDependencies": { + "@rollup/plugin-commonjs": "^28.0.6", + "@rollup/plugin-node-resolve": "^16.0.1", + "@rollup/plugin-typescript": "^11.1.6", + "typescript": "^5.9.2" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build": "rollup -c" + }, + "keywords": [], + "author": "", + "license": "ISC" +}