1/* 2 * Copyright (c) 2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import { KNativePointer } from './InteropTypes'; 17import { BuildConfig } from './types'; 18 19export enum PluginHook { 20 NEW = 'afterNew', 21 PARSED = 'parsed', 22 SCOPE_INITED = 'scopeInited', 23 CHECKED = 'checked', 24 LOWERED = 'lowered', 25 ASM_GENERATED = 'asmGenerated', 26 BIN_GENERATED = 'binGenerated', 27 CLEAN = 'clean' 28} 29 30type PluginHandlerFunction = () => void; 31 32type PluginHandlerObject = { 33 order: 'pre' | 'post' | undefined; 34 handler: PluginHandlerFunction; 35}; 36 37type PluginHandler = PluginHandlerFunction | PluginHandlerObject; 38 39interface Plugins { 40 name: string; 41 afterNew?: PluginHandler; 42 parsed?: PluginHandler; 43 scopeInited?: PluginHandler; 44 checked?: PluginHandler; 45 lowered?: PluginHandler; 46 asmGenerated?: PluginHandler; 47 binGenerated?: PluginHandler; 48 clean?: PluginHandler; 49} 50 51type PluginExecutor = { 52 name: string; 53 handler: PluginHandler; 54}; 55 56type PluginInitFunction = () => Plugins; 57 58type RawPlugins = { 59 name: string; 60 init: PluginInitFunction | undefined; 61}; 62 63class PluginContext { 64 private ast: object | undefined; 65 private program: object | undefined; 66 private projectConfig: object | undefined; 67 private contextPtr: KNativePointer | undefined; 68 69 constructor() { 70 this.ast = undefined; 71 this.program = undefined; 72 this.projectConfig = undefined; 73 this.contextPtr = undefined; 74 } 75 76 public setArkTSAst(ast: object): void { 77 this.ast = ast; 78 } 79 80 public getArkTSAst(): object | undefined { 81 return this.ast; 82 } 83 84 public setArkTSProgram(program: object): void { 85 this.program = program; 86 } 87 88 public getArkTSProgram(): object | undefined { 89 return this.program; 90 } 91 92 public setProjectConfig(projectConfig: object): void { 93 this.projectConfig = projectConfig; 94 } 95 96 public getProjectConfig(): object | undefined { 97 return this.projectConfig; 98 } 99 100 public setContextPtr(ptr: KNativePointer): void { 101 this.contextPtr = ptr; 102 } 103 104 public getContextPtr(): KNativePointer | undefined { 105 return this.contextPtr; 106 } 107} 108 109export class PluginDriver { 110 private static instance: PluginDriver | undefined; 111 private sortedPlugins: Map<PluginHook, PluginExecutor[] | undefined>; 112 private allPlugins: Map<string, Plugins>; 113 private context: PluginContext; 114 115 constructor() { 116 this.sortedPlugins = new Map<PluginHook, PluginExecutor[] | undefined>(); 117 this.allPlugins = new Map<string, Plugins>(); 118 this.context = new PluginContext(); 119 } 120 121 public static getInstance(): PluginDriver { 122 if (!this.instance) { 123 this.instance = new PluginDriver(); 124 } 125 return this.instance; 126 } 127 128 public static destroyInstance(): void { 129 PluginDriver.instance = undefined; 130 } 131 132 public initPlugins(projectConfig: BuildConfig): void { 133 const pluginResults: RawPlugins[] = Object.entries(projectConfig.plugins).map(([key, value]) => { 134 let pluginObject = require(value as string); 135 let initFunction = Object.values(pluginObject)[0] as PluginInitFunction; 136 return { 137 name: key, 138 init: initFunction 139 }; 140 }); 141 142 pluginResults.forEach((plugin: RawPlugins) => { 143 if (plugin.init !== undefined) { 144 this.allPlugins.set(plugin.name, plugin.init()); 145 } 146 }); 147 148 this.context.setProjectConfig(projectConfig); 149 } 150 151 private getPlugins(hook: PluginHook): PluginExecutor[] | undefined { 152 if (!this.sortedPlugins.has(hook)) { 153 const sortedPlugins: PluginExecutor[] = this.getSortedPlugins(hook); 154 if (sortedPlugins.length === 0) { 155 this.sortedPlugins.set(hook, undefined); 156 } else { 157 this.sortedPlugins.set(hook, sortedPlugins); 158 } 159 } 160 161 return this.sortedPlugins.get(hook); 162 } 163 164 private getSortedPlugins(hook: PluginHook): PluginExecutor[] { 165 let pre: PluginExecutor[] = []; 166 let normal: PluginExecutor[] = []; 167 let post: PluginExecutor[] = []; 168 169 this.allPlugins.forEach((pluginObject: Plugins, name: string) => { 170 if (!pluginObject[hook]) { 171 return; 172 } 173 174 let pluginName: string = pluginObject.name; 175 let handler: PluginHandler = pluginObject[hook]!; 176 let order: string | undefined = typeof handler === 'object' ? handler.order : undefined; 177 178 let rawPluginHook: PluginExecutor = { 179 name: pluginName, 180 handler: typeof handler === 'object' ? handler.handler : handler 181 }; 182 183 if (order === 'pre') { 184 pre.push(rawPluginHook); 185 } else if (order === 'post') { 186 post.push(rawPluginHook); 187 } else { 188 normal.push(rawPluginHook); 189 } 190 }); 191 192 return [...pre, ...normal, ...post]; 193 } 194 195 public runPluginHook(hook: PluginHook): void { 196 let plugins: PluginExecutor[] | undefined = this.getPlugins(hook); 197 if (!plugins) { 198 return; 199 } 200 plugins.forEach((executor: PluginExecutor) => { 201 return (executor.handler as Function).apply(this.context); 202 }); 203 } 204 205 public getPluginContext(): PluginContext { 206 return this.context; 207 } 208} 209