diff --git a/ui2abc/libarkts/native/src/common.cc b/ui2abc/libarkts/native/src/common.cc index 78e565aec2523da5c3ca84d8369bb48a7562cb2f..4267c23cfc74c46fdbdba1aab23455230bdc4a11 100644 --- a/ui2abc/libarkts/native/src/common.cc +++ b/ui2abc/libarkts/native/src/common.cc @@ -122,6 +122,7 @@ void* FindLibrary() std::string prefix = envValue ? std::string(envValue) : DEFAULT_SDK_PATH; libraryName = prefix + ("/" PLUGIN_DIR "/lib/") + NAME; } + std::cout << "LOADING " << libraryName << std::endl; return loadLibrary(libraryName); } @@ -132,12 +133,12 @@ es2panda_Impl *GetImplSlow() } auto library = FindLibrary(); if (!library) { - printf("No library (es2panda_lib.cc)"); + printf("No library (es2panda_lib.cc)\n"); abort(); } auto symbol = findSymbol(library, "es2panda_GetImpl"); if (!symbol) { - printf("no entry point"); + printf("no entry point\n"); abort(); } es2pandaImplementation = reinterpret_cast(symbol)(ES2PANDA_LIB_VERSION); diff --git a/ui2abc/libarkts/src/Es2pandaNativeModule.ts b/ui2abc/libarkts/src/Es2pandaNativeModule.ts index db7cffb801c0618aac4b453f5d4f0cfd8ebd9290..b7299b9fd2d8757b3387484bfc510903c9f7cd27 100644 --- a/ui2abc/libarkts/src/Es2pandaNativeModule.ts +++ b/ui2abc/libarkts/src/Es2pandaNativeModule.ts @@ -178,6 +178,12 @@ export class Es2pandaNativeModule { } _IsArrayExpression(node: KPtr): KBoolean { throw new Error("Not implemented") + } + _MemInitialize(): void { + throw new Error('MemInitialize was not overloaded by native module initialization'); + } + _MemFinalize(): void { + throw new Error('MemFinalize was not overloaded by native module initialization'); } _ETSParserGetGlobalProgramAbsName(context: KNativePointer): KNativePointer { throw new Error("Not implemented") diff --git a/ui2abc/libarkts/src/arkts-api/index.ts b/ui2abc/libarkts/src/arkts-api/index.ts index e5497804db96f2ed2ee3c9534aaf47df53715a21..08daa75e354a785cad9f889368363583478cfe4f 100644 --- a/ui2abc/libarkts/src/arkts-api/index.ts +++ b/ui2abc/libarkts/src/arkts-api/index.ts @@ -17,6 +17,7 @@ export * from "../generated/Es2pandaEnums" export * from "../generated" export * from "./utilities/private" +export * from "./utilities/performance" export * from "./utilities/public" export * from "./factory/nodeFactory" export * from "./visitor" @@ -38,4 +39,5 @@ export * from "./node-utilities/ArkTsConfig" export * from "./node-utilities/Program" export * from "./peers/ImportPathManager" export { global as arktsGlobal } from "./static/global" +export * from "./static/globalUtils" export * from "./wrapper-compat" \ No newline at end of file diff --git a/ui2abc/libarkts/src/arkts-api/peers/Context.ts b/ui2abc/libarkts/src/arkts-api/peers/Context.ts index 349884d6e2050f5671db201cc4cc2ec8d8dffec3..d33ae46f0c1afd7b837c13d98bf60ff81e72cf3e 100644 --- a/ui2abc/libarkts/src/arkts-api/peers/Context.ts +++ b/ui2abc/libarkts/src/arkts-api/peers/Context.ts @@ -71,7 +71,17 @@ export class Context extends ArktsObject { ) ) } - + + static createCacheContextFromFile( + configPtr: KNativePointer, + fileName: string, + globalContextPtr: KNativePointer, + isExternal: boolean + ): Context { + return new Context( + global.es2panda._CreateCacheContextFromFile(configPtr, passString(fileName), globalContextPtr, isExternal) + ); + } destroy() { if (this.peer != nullptr) { diff --git a/ui2abc/libarkts/src/arkts-api/static/globalUtils.ts b/ui2abc/libarkts/src/arkts-api/static/globalUtils.ts new file mode 100644 index 0000000000000000000000000000000000000000..0f947b0c1356cad3cb6fff483f088fc8279e0f15 --- /dev/null +++ b/ui2abc/libarkts/src/arkts-api/static/globalUtils.ts @@ -0,0 +1,27 @@ +/* + * 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. + */ + +import { KNativePointer } from "@koalaui/interop"; +import { Context } from "../peers/Context"; +import { global } from "./global"; +import { NodeCache } from "../node-cache"; + +export function getOrUpdateGlobalContext(peer: KNativePointer): Context { + if (!global.compilerContext || global.context !== peer) { + NodeCache.clear(); + global.compilerContext = new Context(peer); + } + return global.compilerContext; +} \ No newline at end of file diff --git a/ui2abc/libarkts/src/arkts-api/utilities/performance.ts b/ui2abc/libarkts/src/arkts-api/utilities/performance.ts new file mode 100644 index 0000000000000000000000000000000000000000..c938de34c133bf65a15d51689e47623741fe87ec --- /dev/null +++ b/ui2abc/libarkts/src/arkts-api/utilities/performance.ts @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2022-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. + */ + +interface Event { + name: string, + startTime: number, + endTime?: number, + parentEvent?: string, + duration?: number +} + +function formatTime(ms: number): string { + const milliseconds = Math.floor(ms % 1000); + const seconds = Math.floor((ms / 1000) % 60); + const minutes = Math.floor((ms / (1000 * 60)) % 60); + const hours = Math.floor(ms / (1000 * 60 * 60)); + + return `${pad(hours, 2)}:${pad(minutes, 2)}:${pad(seconds, 2)}:${pad(milliseconds, 3)}`; +} + +function pad(value: number, length: number): string { + return value.toString().padStart(length, '0'); +} + +function round(value: number, index: number = 2): number { + const factor = Math.pow(10, index); + return Math.round(value * factor) / factor; +} + +export class Performance { + private static instance: Performance; + private events: Map; + private historyEvents = new Map(); + private scopes: string[]; + private shouldSkip: boolean; + private totalDuration: number; + + private constructor() { + this.events = new Map(); + this.historyEvents = new Map(); + this.scopes = []; + this.shouldSkip = true; + this.totalDuration = 0; + } + + public static getInstance(): Performance { + if (!this.instance) { + this.instance = new Performance(); + } + return this.instance; + } + + skip(shouldSkip: boolean = true): void { + this.shouldSkip = shouldSkip; + } + + createEvent(name: string): Event { + if (this.shouldSkip) { + return { name: '', startTime: 0 }; + } + const startTime: number = performance.now(); + const newEvent: Event = { name, startTime }; + this.events.set(name, newEvent); + this.scopes.push(name); + return newEvent; + } + + stopEvent(name: string, shouldLog: boolean = false): Event { + if (this.shouldSkip) { + return { name: '', startTime: 0 }; + } + if (!this.events.has(name) || this.scopes.length === 0) { + throw new Error(`Event ${name} is not created.`); + } + if (this.scopes[this.scopes.length - 1] !== name) { + console.warn(`[PERFORMANCE WARNING] Event ${name} early exit.`); + } + this.scopes.pop(); + + const event: Event = this.events.get(name)!; + const endTime: number = performance.now(); + const parentEvent: string = this.scopes[this.scopes.length - 1]; + const duration: number = endTime - event.startTime; + if (!parentEvent) { + this.totalDuration += duration; + } + + if (shouldLog) { + console.log( + `[PERFORMANCE] name: ${event.name}, parent: ${parentEvent}, duration: ${formatTime(duration)}(${round(duration)}), total: ${formatTime(this.totalDuration)}(${round(this.totalDuration)})` + ); + } + + const newEvent = { ...event, endTime, parentEvent, duration }; + const history = this.historyEvents.get(parentEvent ?? null) || []; + this.historyEvents.set(parentEvent ?? null, [...history, newEvent]); + return newEvent; + } + + stopLastEvent(shouldLog: boolean = false): Event { + if (this.shouldSkip) { + return { name: '', startTime: 0 }; + } + if (this.scopes.length === 0) { + throw new Error("No last event"); + } + const name: string = this.scopes.pop()!; + if (!this.events.has(name)) { + throw new Error(`Event ${name} is not created.`); + } + + const event: Event = this.events.get(name)!; + const endTime: number = performance.now(); + const parentEvent: string = this.scopes[this.scopes.length - 1]; + const duration: number = endTime - event.startTime; + if (!parentEvent) { + this.totalDuration += duration; + } + + if (shouldLog) { + console.log( + `[PERFORMANCE] name: ${event.name}, parent: ${parentEvent}, duration: ${formatTime(duration)}(${round(duration)}), total: ${formatTime(this.totalDuration)}(${round(this.totalDuration)})` + ); + } + + const newEvent = { ...event, endTime, parentEvent, duration }; + const history = this.historyEvents.get(parentEvent ?? null) || []; + this.historyEvents.set(parentEvent ?? null, [...history, newEvent]); + return newEvent; + } + + clearAllEvents(shouldLog: boolean = false): void { + if (this.shouldSkip) { + return; + } + for (let i = 0; i < this.scopes.length; i ++) { + this.stopLastEvent(shouldLog); + } + this.events = new Map(); + } + + clearTotalDuration(): void { + this.totalDuration = 0; + } + + clearHistory(): void { + this.historyEvents = new Map(); + } + + visualizeEvents(shouldLog: boolean = false): void { + if (this.shouldSkip) { + return; + } + const that = this; + function buildVisualization(parentKey: string | null, indentLevel: number): [string, number] { + const children = that.historyEvents.get(parentKey) || []; + let result = ''; + + children.forEach(child => { + const indent = ' '.repeat(indentLevel); + const duration = child.duration ?? 0; + const [_result, count] = buildVisualization(child.name, indentLevel + 1); + result += `${indent}- ${child.name}: ${formatTime(duration)}(${round(duration)}), ${count}\n`; + result += _result; + }); + + return [result, children.length]; + } + + const [finalResult, _] = buildVisualization(null, 0); + if (shouldLog) { + console.log(`[PERFORMANCE] ===== FINAL RESULT ====`); + console.log(`TOTAL: ${formatTime(this.totalDuration)}(${round(this.totalDuration)})`); + console.log(finalResult.trimEnd()); + console.log(`[PERFORMANCE] ===== FINAL RESULT ====`); + } + } +} \ No newline at end of file diff --git a/ui2abc/libarkts/src/arkts-api/utilities/public.ts b/ui2abc/libarkts/src/arkts-api/utilities/public.ts index dfca572305bde0f02e0fc2a0ef19c904f16d57eb..9ca2138e4993981184d8b9c58ab2bcf4a45b9175 100644 --- a/ui2abc/libarkts/src/arkts-api/utilities/public.ts +++ b/ui2abc/libarkts/src/arkts-api/utilities/public.ts @@ -15,9 +15,9 @@ import { global } from "../static/global" import { isNumber, throwError, withWarning } from "../../utils" -import { KNativePointer, nullptr } from "@koalaui/interop" -import { passNode, passNodeArray, unpackNodeArray, unpackNonNullableNode } from "./private" -import { Es2pandaContextState, Es2pandaModifierFlags } from "../../generated/Es2pandaEnums" +import { KInt, KNativePointer, nullptr } from "@koalaui/interop" +import { passNode, passNodeArray, passString, passStringArray, unpackNodeArray, unpackNonNullableNode } from "./private" +import { Es2pandaContextState, Es2pandaMethodDefinitionKind, Es2pandaModifierFlags } from "../../generated/Es2pandaEnums" import type { AstNode } from "../peers/AstNode" import { type AnnotationUsage, @@ -29,7 +29,18 @@ import { isMemberExpression, isScriptFunction, isIdentifier, - isETSModule + isETSModule, + ETSImportDeclaration, + ImportSpecifier, + isMethodDefinition, + isNumberLiteral, + isObjectExpression, + isTSInterfaceDeclaration, + MemberExpression, + Program, + Property, + SourcePosition, + isProperty } from "../../generated" import { Config } from "../peers/Config" import { Context } from "../peers/Context" @@ -86,7 +97,8 @@ export function checkErrors() { } } -export function proceedToState(state: Es2pandaContextState): void { +export function proceedToState(state: Es2pandaContextState, context?: KNativePointer /* from wrapper */): void { + if (context != undefined) global.compilerContext = new Context(context) // wrapper compatibility if (state <= global.es2panda._ContextState(global.context)) { return } @@ -112,19 +124,77 @@ export function rebindSubtree(node: AstNode): void { checkErrors() } + export function getDecl(node: AstNode): AstNode | undefined { if (isMemberExpression(node)) { - return getDecl(node.property!) + return getDeclFromArrayOrObjectMember(node); + } + if (isObjectExpression(node)) { + return getPeerObjectDecl(passNode(node)); + } + const decl = getPeerDecl(passNode(node)); + if (!!decl) { + return decl; + } + if (!!node.parent && isProperty(node.parent)) { + return getDeclFromProperty(node.parent); + } + return undefined; +} + +function getDeclFromProperty(node: Property): AstNode | undefined { + if (!node.key) { + return undefined; + } + if (!!node.parent && !isObjectExpression(node.parent)) { + return getPeerDecl(passNode(node.key)); + } + return getDeclFromObjectExpressionProperty(node); +} + +function getDeclFromObjectExpressionProperty(node: Property): AstNode | undefined { + const declNode = getPeerObjectDecl(passNode(node.parent)); + if (!declNode || !node.key || !isIdentifier(node.key)) { + return undefined; + } + let body: readonly AstNode[] = []; + if (isClassDefinition(declNode)) { + body = declNode.body; + } else if (isTSInterfaceDeclaration(declNode)) { + body = declNode.body?.body ?? []; + } + return body.find( + (statement) => + isMethodDefinition(statement) && + statement.kind === Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET && + !!statement.key && + !!node.key && + isIdentifier(node.key) && + (statement.key as any).name === node.key.name + ); +} + +function getDeclFromArrayOrObjectMember(node: MemberExpression): AstNode | undefined { + if (isNumberLiteral(node.property)) { + return getDecl(node.object!); } - return getPeerDecl(passNode(node)) + return getDecl(node.property!); } export function getPeerDecl(peer: KNativePointer): AstNode | undefined { - const decl = global.es2panda._DeclarationFromIdentifier(global.context, peer) + const decl = global.es2panda._DeclarationFromIdentifier(global.context, peer); if (decl === nullptr) { - return undefined + return undefined; } - return unpackNonNullableNode(decl) + return unpackNonNullableNode(decl); +} + +export function getPeerObjectDecl(peer: KNativePointer): AstNode | undefined { + const decl = global.es2panda._ClassVariableDeclaration(global.context, peer); + if (decl === nullptr) { + return undefined; + } + return unpackNonNullableNode(decl); } export function getAnnotations(node: AstNode): readonly AnnotationUsage[] { @@ -149,6 +219,22 @@ export function getFileName(): string { return global.filePath } +// export function classDefinitionSetFromStructModifier(node: ClassDefinition): void { +// global.es2panda._ClassDefinitionSetFromStructModifier(global.context, node.peer); +// } + +// export function classDefinitionIsFromStructConst(node: ClassDefinition): boolean { +// return global.es2panda._ClassDefinitionIsFromStructConst(global.context, node.peer); +// } + +// export function ImportSpecifierSetRemovable(node: ImportSpecifier): void { +// global.es2panda._ImportSpecifierSetRemovable(global.context, node.peer); +// } + +// export function ImportSpecifierIsRemovableConst(node: ImportSpecifier): boolean { +// return global.es2panda._ImportSpecifierIsRemovableConst(global.context, node.peer); +// } + // TODO: It seems like Definition overrides AstNode modifiers // with it's own modifiers which is completely unrelated set of flags. // Use this function if you need @@ -157,6 +243,15 @@ export function classDefinitionFlags(node: ClassDefinition): Es2pandaModifierFla return global.generatedEs2panda._AstNodeModifiers(global.context, node.peer) } +// TODO: Import statements should be inserted to the statements +export function importDeclarationInsert(node: ETSImportDeclaration, program: Program): void { + global.es2panda._InsertETSImportDeclarationAndParse(global.context, program.peer, node.peer); +} + +// export function getProgramFromAstNode(node: AstNode): Program { +// return new Program(global.es2panda._AstNodeProgram(global.context, node.peer)); +// } + // TODO: ClassProperty's optional flag is set by AstNode's modifiers flags. export function classPropertySetOptional(node: ClassProperty, value: boolean): ClassProperty { if (value) { @@ -188,6 +283,11 @@ export function modifiersToString(modifiers: Es2pandaModifierFlags): string { }).join(" ") } +// export function destroyConfig(config: KNativePointer): void { +// global.es2panda._DestroyConfig(config); +// global.resetConfig(); +// } + export function nameIfIdentifier(node: AstNode): string { return isIdentifier(node) ? `'${node.name}'` : "" } @@ -222,3 +322,74 @@ export function collectDependencies(files: string[], configPath: string): string } return Array.from(result) } + + +export function setAllParents(ast: AstNode): void { + global.es2panda._AstNodeUpdateAll(global.context, ast.peer); +} + +// export function generateTsDeclarationsFromContext(outputDeclEts: string, outputEts: string, exportAll: boolean): KInt { +// return global.es2panda._GenerateTsDeclarationsFromContext( +// global.context, +// passString(outputDeclEts), +// passString(outputEts), +// exportAll +// ); +// } + +// export function generateStaticDeclarationsFromContext(outputPath: string): KInt { +// return global.generatedEs2panda._GenerateStaticDeclarationsFromContext( +// global.context, +// passString(outputPath) +// ); +// } + +export function isDefaultAccessModifierClassProperty(property: ClassProperty): boolean { + return global.generatedEs2panda._ClassPropertyIsDefaultAccessModifierConst(global.context, property.peer); +} + +export function getStartPosition(node: AstNode): SourcePosition { + return new SourcePosition(global.generatedEs2panda._AstNodeStartConst(global.context, node.peer)); +} + +export function getEndPosition(node: AstNode): SourcePosition { + return new SourcePosition(global.generatedEs2panda._AstNodeEndConst(global.context, node.peer)); +} + +export function MemInitialize(): void { + global.es2panda._MemInitialize(); +} + +export function MemFinalize(): void { + global.es2panda._MemFinalize(); +} + +export function CreateGlobalContext( + config: KNativePointer, + externalFileList: string[], + fileNum: KInt, + lspUsage: boolean +): KNativePointer { + return global.es2panda._CreateGlobalContext(config, passStringArray(externalFileList), fileNum, lspUsage); +} + +export function DestroyGlobalContext(context: KNativePointer): void { + global.es2panda._DestroyGlobalContext(context); +} + +export function CreateCacheContextFromFile( + configPtr: KNativePointer, + filename: string, + globalContext: KNativePointer, + isExternal: Boolean +): KNativePointer { + return global.es2panda._CreateCacheContextFromFile(configPtr, passString(filename), globalContext, isExternal); +} + +export function insertGlobalStructInfo(structName: string): void { + global.es2panda._InsertGlobalStructInfo(global.context, passString(structName)); +} + +export function hasGlobalStructInfo(structName: string): boolean { + return global.es2panda._HasGlobalStructInfo(global.context, passString(structName)); +} diff --git a/ui2abc/libarkts/src/index.ts b/ui2abc/libarkts/src/index.ts index c1738ed5eae2c458c7e2a22d6c51b123ed110f40..b2a5e363428f60520b60db88f039661b9dc3dd13 100644 --- a/ui2abc/libarkts/src/index.ts +++ b/ui2abc/libarkts/src/index.ts @@ -21,6 +21,7 @@ export * from "./generated" export * from "./arkts-api/utilities/private" export * from "./arkts-api/utilities/public" +export * from "./arkts-api/utilities/performance" export * from "./arkts-api/factory/nodeFactory" export * from "./arkts-api/visitor" export * from "./arkts-api/AbstractVisitor" @@ -41,6 +42,7 @@ export * from "./arkts-api/peers/ExternalSource" export * from "./arkts-api/peers/ImportPathManager" export * from "./arkts-api/peers/Options" export { global as arktsGlobal } from "./arkts-api/static/global" +export * from "./arkts-api/static/globalUtils" export * as arkts from "./arkts-api" export * from "./plugin-utils" diff --git a/ui2abc/memo-plugin/package.json b/ui2abc/memo-plugin/package.json index c2fbdece3423731d22523f9a3d84edfc32477a03..a51ae085c06a3d13a6996a3f2e7ed876bbcd3b9f 100644 --- a/ui2abc/memo-plugin/package.json +++ b/ui2abc/memo-plugin/package.json @@ -33,6 +33,6 @@ "test:all": "npm run test && npm run diagnostics && npm run executable" }, "devDependencies": { - "@koalaui/harness": "1.7.3+devel" + "@koalaui/harness": "1.7.4+devel" } }