From 884849b6c17ec532c317302c20e392a30ecf15d4 Mon Sep 17 00:00:00 2001 From: l00913061 Date: Wed, 10 Sep 2025 16:46:51 +0800 Subject: [PATCH] fileInterop0910 Signed-off-by: l00913061 --- arkui-plugins/common/file-manager.ts | 40 +---- arkui-plugins/common/program-visitor.ts | 73 ++------ .../ui-plugins/checked-transformer.ts | 54 ++---- .../ui-plugins/component-transformer.ts | 70 -------- arkui-plugins/ui-plugins/interop/interop.ts | 151 ++++++---------- .../ui-plugins/interop/legacy-transformer.ts | 167 +++++++++++------- .../ui-plugins/interop/predefines.ts | 5 + arkui-plugins/ui-plugins/interop/utils.ts | 43 ++++- .../ui-plugins/struct-translators/factory.ts | 10 +- 9 files changed, 251 insertions(+), 362 deletions(-) diff --git a/arkui-plugins/common/file-manager.ts b/arkui-plugins/common/file-manager.ts index 11158be41..e0e8d85f4 100644 --- a/arkui-plugins/common/file-manager.ts +++ b/arkui-plugins/common/file-manager.ts @@ -29,53 +29,19 @@ export class FileManager { static setInstance(instance: FileManager | undefined): void { if (instance === undefined) { - throw Error('fileManager is undefined!'); + FileManager.instance = new FileManager(); } FileManager.instance = instance; } static getInstance(): FileManager { if (!FileManager.instance) { - throw Error('fileManager not exist.'); + FileManager.instance = new FileManager(); } return FileManager.instance; } - private static isFirstLineUseStatic(filePath: string): boolean { - const firstLine = readFirstLineSync(filePath); - return firstLine === "'use static'"; - } - getLanguageVersionByFilePath(filePath: string): string { - const path = toUnixPath(filePath); - for (const apiPath of FileManager.staticApiPath) { - if (path.startsWith(apiPath)) { - return LANGUAGE_VERSION.ARKTS_1_2; - } - } - for (const apiPath of FileManager.dynamicApiPath) { - if (path.startsWith(apiPath)) { - return LANGUAGE_VERSION.ARKTS_1_1; - } - } - if (FileManager.buildConfig.compileFiles?.includes(filePath)) { - return LANGUAGE_VERSION.ARKTS_1_2; - } - for (const [pkgName, moduleInfo] of FileManager.arkTSModuleMap) { - if (!path.startsWith(moduleInfo.modulePath)) { - continue; - } - if (moduleInfo.language !== LANGUAGE_VERSION.ARKTS_HYBRID) { - return moduleInfo.language; - } - /** - * when process hybrid hsp or har we can't get info of 1.1, - * only by module decl-fileinfo.json or `'use static'` - */ - if (FileManager.isFirstLineUseStatic(filePath)) { - return LANGUAGE_VERSION.ARKTS_1_2; - } - } - return LANGUAGE_VERSION.ARKTS_1_1; + return LANGUAGE_VERSION.ARKTS_1_2; } } \ No newline at end of file diff --git a/arkui-plugins/common/program-visitor.ts b/arkui-plugins/common/program-visitor.ts index 9327b9075..d5fb01f0f 100644 --- a/arkui-plugins/common/program-visitor.ts +++ b/arkui-plugins/common/program-visitor.ts @@ -17,11 +17,11 @@ import * as arkts from '@koalaui/libarkts'; import { AbstractVisitor, VisitorOptions } from './abstract-visitor'; import { matchPrefix } from './arkts-utils'; import { getDumpFileName, debugDumpAstNode, debugLog } from './debug'; -import { InteroperAbilityNames } from '../ui-plugins/interop/predefines'; import { PluginContext } from './plugin-context'; import { LegacyTransformer } from '../ui-plugins/interop/legacy-transformer'; -import { ComponentTransformer } from '../ui-plugins/component-transformer'; import { ProgramSkipper } from './program-skipper'; +import { FileManager } from './file-manager'; +import { LANGUAGE_VERSION } from './predefines'; export interface ProgramVisitorOptions extends VisitorOptions { pluginName: string; @@ -61,10 +61,6 @@ function flattenVisitorsInHooks( ]; } -export interface StructMap { - [key: string]: string; -} - export class ProgramVisitor extends AbstractVisitor { private readonly pluginName: string; private readonly state: arkts.Es2pandaContextState; @@ -73,8 +69,6 @@ export class ProgramVisitor extends AbstractVisitor { private readonly hooks?: ProgramHooks; private filenames: Map; private pluginContext?: PluginContext; - private legacyModuleList: string[] = []; - private legacyStructMap: Map; private isFrameworkMode: boolean = false; constructor(options: ProgramVisitorOptions) { @@ -86,8 +80,7 @@ export class ProgramVisitor extends AbstractVisitor { this.hooks = options.hooks; this.filenames = new Map(); this.pluginContext = options.pluginContext; - this.legacyModuleList = []; - this.legacyStructMap = new Map(); + FileManager.setInstance((this.pluginContext as PluginContext)?.getFileManager()); if (options.isFrameworkMode !== undefined) { this.isFrameworkMode = options.isFrameworkMode; @@ -97,26 +90,6 @@ export class ProgramVisitor extends AbstractVisitor { reset(): void { super.reset(); this.filenames = new Map(); - this.legacyStructMap = new Map(); - this.legacyModuleList = []; - } - - private getLegacyModule(): void { - const moduleList = this.pluginContext?.getProjectConfig()?.dependentModuleList; - if (moduleList === undefined) { - return; - } - for (const module of moduleList) { - const language = module.language; - const moduleName = module.moduleName; - if (language !== InteroperAbilityNames.ARKTS_1_1) { - continue; - } - if (!this.legacyStructMap.has(moduleName)) { - this.legacyStructMap.set(moduleName, {}); - this.legacyModuleList.push(moduleName); - } - } } private dumpExternalSource( @@ -134,15 +107,12 @@ export class ProgramVisitor extends AbstractVisitor { ); } - private visitLegacyInExternalSource(currProgram: arkts.Program, name: string): void { - if (this.state === arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED) { - const structList = this.visitorLegacy(currProgram.astNode, currProgram, name); - const moduleName = name.split('/')[0]; - const structMap = this.legacyStructMap.get(moduleName)!; - for (const struct of structList) { - structMap[struct] = name; - } - } + private visitLegacyInExternalSource(program: arkts.Program, externalSourceName: string): void { + const transformer = new LegacyTransformer(); + transformer.isExternal = !!externalSourceName; + transformer.externalSourceName = externalSourceName; + transformer.program = program; + transformer.visitor(program.astNode); } private visitNonLegacyInExternalSource( @@ -173,13 +143,20 @@ export class ProgramVisitor extends AbstractVisitor { } } + private isLegacyFile(path: string): boolean { + const fileManager = FileManager.getInstance(); + if (fileManager.getLanguageVersionByFilePath(path) === LANGUAGE_VERSION.ARKTS_1_1) { + return true; + } + return false; + } + private visitExternalSources( program: arkts.Program, programQueue: arkts.Program[] ): void { const visited = new Set(); const queue: arkts.Program[] = programQueue; - this.getLegacyModule(); while (queue.length > 0) { const currProgram = queue.shift()!; if (visited.has(currProgram.peer) || currProgram.isASTLowered()) { @@ -188,7 +165,8 @@ export class ProgramVisitor extends AbstractVisitor { if (currProgram.peer !== program.peer) { const name: string = this.filenames.get(currProgram.peer)!; const cachePath: string | undefined = this.pluginContext?.getProjectConfig()?.cachePath; - if (this.legacyModuleList && matchPrefix(this.legacyModuleList, name)) { + if (this.state === arkts.Es2pandaContextState.ES2PANDA_STATE_PARSED && this.pluginName === 'uiTransform' && + this.isLegacyFile(currProgram.absName)) { this.visitLegacyInExternalSource(currProgram, name); } else { this.visitNonLegacyInExternalSource(program, currProgram, name, cachePath); @@ -266,9 +244,6 @@ export class ProgramVisitor extends AbstractVisitor { this.preVisitor(hook, node, program, externalSourceName); for (const transformer of this.visitors) { - if (this.legacyStructMap.size > 0 && transformer instanceof ComponentTransformer) { - transformer.registerMap(this.legacyStructMap); - } this.visitTransformer(transformer, script, externalSourceName, program); arkts.setAllParents(script); if (!transformer.isExternal) { @@ -288,16 +263,6 @@ export class ProgramVisitor extends AbstractVisitor { return script; } - private visitorLegacy(node: arkts.AstNode, program?: arkts.Program, externalSourceName?: string): string[] { - const transformer = new LegacyTransformer(); - transformer.isExternal = !!externalSourceName; - transformer.externalSourceName = externalSourceName; - transformer.program = program; - transformer.visitor(node); - const structList = transformer.getList(); - return structList; - } - private visitTransformer( transformer: AbstractVisitor, script: arkts.EtsScript, diff --git a/arkui-plugins/ui-plugins/checked-transformer.ts b/arkui-plugins/ui-plugins/checked-transformer.ts index 6ad631797..c511b411d 100644 --- a/arkui-plugins/ui-plugins/checked-transformer.ts +++ b/arkui-plugins/ui-plugins/checked-transformer.ts @@ -47,10 +47,11 @@ import { builderRewriteByType } from './builder-lambda-translators/builder-facto import { MonitorCache } from './property-translators/cache/monitorCache'; import { ComponentAttributeCache } from './builder-lambda-translators/cache/componentAttributeCache'; import { MetaDataCollector } from '../common/metadata-collector'; +import { FileManager } from '../common/file-manager'; +import { LANGUAGE_VERSION } from '../common/predefines'; export class CheckedTransformer extends AbstractVisitor { private scope: ScopeInfoCollection; - private legacyBuilderSet: Set = new Set(); projectConfig: ProjectConfig | undefined; aceBuildJson: LoaderJson; resourceInfo: ResourceInfo; @@ -61,25 +62,6 @@ export class CheckedTransformer extends AbstractVisitor { this.scope = { customComponents: [] }; this.aceBuildJson = loadBuildJson(this.projectConfig); this.resourceInfo = initResourceInfo(this.projectConfig, this.aceBuildJson); - this.legacyBuilderSet = new Set(); - this.initBuilderMap(); - } - - initBuilderMap(): void { - const moduleList = this.projectConfig?.dependentModuleList; - if (moduleList === undefined) { - return; - } - for (const module of moduleList) { - const language = module.language; - const moduleName = module.moduleName; - if (language !== InteroperAbilityNames.ARKTS_1_1) { - continue; - } - if (!this.legacyBuilderSet.has(moduleName)) { - this.legacyBuilderSet.add(moduleName); - } - } } init(): void { @@ -127,29 +109,25 @@ export class CheckedTransformer extends AbstractVisitor { } isFromBuilder1_1(decl: arkts.AstNode | undefined): boolean { - if (!decl || this.legacyBuilderSet.size === 0) { + if (!decl || !arkts.isMethodDefinition(decl)) { return false; } - const moduleName = arkts.getProgramFromAstNode(decl).moduleName?.split('/')[0]; - - if (!this.legacyBuilderSet.has(moduleName)) { + const path = arkts.getProgramFromAstNode(decl).absName; + const fileManager = FileManager.getInstance(); + if (fileManager.getLanguageVersionByFilePath(path) !== LANGUAGE_VERSION.ARKTS_1_1) { return false; } - let isFrom1_1 = false; - if (arkts.isMethodDefinition(decl)) { - const annotations = decl.scriptFunction.annotations; - const decorators: string[] = annotations.map((annotation) => { - return (annotation.expr as arkts.Identifier).name; - }); - decorators.forEach((element) => { - if (element === 'Builder') { - isFrom1_1 = true; - return; - } - }); + const annotations = decl.scriptFunction.annotations; + const decorators: string[] = annotations.map((annotation) => { + return (annotation.expr as arkts.Identifier).name; + }); + for (const decorator of decorators) { + if (decorator === 'memo') { + return true; + } } - return isFrom1_1; + return false; } addcompatibleComponentImport(): void { @@ -201,7 +179,7 @@ export class CheckedTransformer extends AbstractVisitor { } else if (arkts.isClassDeclaration(node)) { return structFactory.transformNormalClass(node, this.externalSourceName); } else if (arkts.isCallExpression(node)) { - return structFactory.transformCallExpression(node, this.projectConfig, this.resourceInfo); + return structFactory.transformCallExpression(node, this.projectConfig, this.resourceInfo, this.scope.customComponents.length === 0); } else if (arkts.isTSInterfaceDeclaration(node)) { return structFactory.tranformInterfaceMembers(node, this.externalSourceName); } else if ( diff --git a/arkui-plugins/ui-plugins/component-transformer.ts b/arkui-plugins/ui-plugins/component-transformer.ts index ae383cc16..3fc5fce9d 100644 --- a/arkui-plugins/ui-plugins/component-transformer.ts +++ b/arkui-plugins/ui-plugins/component-transformer.ts @@ -44,7 +44,6 @@ import { factory as entryFactory } from './entry-translators/factory'; import { hasDecoratorName, findDecoratorInfos, DecoratorInfo } from './property-translators/utils'; import { factory } from './ui-factory'; import { factory as propertyFactory } from './property-translators/factory'; -import { StructMap } from '../common/program-visitor'; import { CUSTOM_COMPONENT_IMPORT_SOURCE_NAME, DecoratorIntrinsicNames, @@ -54,7 +53,6 @@ import { NavigationNames, EntryWrapperNames, } from '../common/predefines'; -import { generateInstantiateInterop } from './interop/interop'; export interface ComponentTransformerOptions extends VisitorOptions { arkui?: string; @@ -84,9 +82,6 @@ export class ComponentTransformer extends AbstractVisitor { private isPageLifeCycleImported: boolean = false; private isLayoutCallbackImported: boolean = false; private shouldAddLinkIntrinsic: boolean = false; - private hasLegacy: boolean = false; - private legacyStructMap: Map = new Map(); - private legacyCallMap: Map = new Map(); private projectConfig: ProjectConfig | undefined; private entryRouteName: string | undefined; private componentType: ComponentType = { @@ -115,9 +110,6 @@ export class ComponentTransformer extends AbstractVisitor { this.isPageLifeCycleImported = false; this.isLayoutCallbackImported = false; this.shouldAddLinkIntrinsic = false; - this.hasLegacy = false; - this.legacyStructMap = new Map(); - this.legacyCallMap = new Map(); this.componentType = { hasComponent: false, hasComponentV2: false, @@ -482,58 +474,6 @@ export class ComponentTransformer extends AbstractVisitor { return [originMember, optionsHasMember]; } - registerMap(map: Map): void { - this.legacyStructMap = map; - this.hasLegacy = true; - } - - processInteropImport(node: arkts.ETSImportDeclaration): void { - const source = node.source?.str!; - const specifiers = node.specifiers as arkts.ImportSpecifier[]; - if (this.legacyStructMap.has(source)) { - const structMap = this.legacyStructMap.get(source); - if (!structMap) { - return; - } - for (const specifier of specifiers) { - const name = (specifier as arkts.ImportSpecifier)!.local!.name; - if (structMap[name]) { - this.legacyCallMap.set(name, structMap[name]); - } - } - } - } - - processInteropCall(node: arkts.CallExpression): arkts.CallExpression { - const ident = node.expression; - if (!(ident instanceof arkts.Identifier)) { - return node; - } - const className = ident.name; - const trailingBlock = node.trailingBlock; - const content = trailingBlock - ? arkts.factory.createArrowFunction( - factory.createScriptFunction({ - body: trailingBlock, - flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, - modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - }) - ) - : undefined; - if (this.legacyCallMap.has(className)) { - const path = this.legacyCallMap.get(className)!; - const args = node.arguments; - const context: InteropContext = { - className: className, - path: path, - arguments: args && args.length === 1 && args[0] instanceof arkts.ObjectExpression ? args[0] : undefined, - content: content, - }; - return generateInstantiateInterop(context); - } - return node; - } - visitor(node: arkts.AstNode): arkts.AstNode { this.enter(node); const newNode = this.visitEachChild(node); @@ -562,16 +502,6 @@ export class ComponentTransformer extends AbstractVisitor { ) { return factory.updateCustomDialogOptionsInterface(newNode); } - // process interop code - if (!this.hasLegacy) { - return newNode; - } - if (arkts.isETSImportDeclaration(newNode)) { - this.processInteropImport(newNode); - } - if (arkts.isCallExpression(newNode)) { - return this.processInteropCall(newNode); - } return newNode; } } diff --git a/arkui-plugins/ui-plugins/interop/interop.ts b/arkui-plugins/ui-plugins/interop/interop.ts index 554444410..7e5ac4757 100644 --- a/arkui-plugins/ui-plugins/interop/interop.ts +++ b/arkui-plugins/ui-plugins/interop/interop.ts @@ -29,8 +29,6 @@ import { createELMTID, createInitReturn } from './utils'; -import { ImportCollector } from '../../common/import-collector'; -import { factory as uiFactory } from '../ui-factory'; import { DecoratorNames } from '../../common/predefines'; import { hasDecorator } from '../property-translators/utils'; @@ -290,51 +288,35 @@ function createUpdater(updateProp: arkts.Property[]): arkts.ArrowFunctionExpress } function updateArguments(context: InteropContext, name: string): arkts.ObjectExpression { + const property = arkts.factory.createProperty( + arkts.factory.createIdentifier(name), + arkts.factory.createTSAsExpression( + context.content, + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier('Function') + ) + ), + false + ) + ); return context.arguments ? arkts.factory.updateObjectExpression( context.arguments, arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, [ ...(context.arguments?.properties as arkts.Property[]), - arkts.factory.createProperty( - arkts.factory.createIdentifier(name), - arkts.factory.createTSAsExpression( - context.content, - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier('Function') - ) - ), - false - ) - ) + property ], false ) : arkts.factory.createObjectExpression( arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, - [ - arkts.factory.createProperty( - arkts.factory.createIdentifier(name), - arkts.factory.createTSAsExpression( - context.content, - arkts.factory.createTypeReference( - arkts.factory.createTypeReferencePart( - arkts.factory.createIdentifier('Function') - ) - ), - false - ) - ) - ], + [property], false ); } -function generateVarMap(context: InteropContext, node: arkts.Identifier): Map { +function generateVarMap(context: InteropContext, decl: arkts.ClassDefinition): Map { let needBuilderParam = !!context.content; - const decl = arkts.getDecl(node); - if (!(decl instanceof arkts.ClassDefinition)) { - throw Error("can't find legacy class declaration"); - } const result = new Map(); const definition = decl; const body = definition.body; @@ -351,62 +333,30 @@ function generateVarMap(context: InteropContext, node: arkts.Identifier): Map.instantiate_Interop. - */ -export function generateInstantiateInterop(context: InteropContext): arkts.CallExpression { - return arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(context.className), - arkts.factory.createIdentifier('instantiate_Interop'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - generateStructInfo(context) - ); -} - -/** - * - * @param node - * @returns {boolean} Checks if a given CallExpression represents a call to .instantiate_Interop. - */ -export function isArkUICompatible(node: arkts.AstNode): boolean { - if (node instanceof arkts.CallExpression && node.expression instanceof arkts.MemberExpression && - node.expression.property instanceof arkts.Identifier && - node.expression.property.name === 'instantiate_Interop') { - ImportCollector.getInstance().collectSource(InteroperAbilityNames.ARKUICOMPATIBLE, InteroperAbilityNames.INTEROP); - ImportCollector.getInstance().collectImport(InteroperAbilityNames.ARKUICOMPATIBLE); - ImportCollector.getInstance().collectSource(InteroperAbilityNames.GETCOMPATIBLESTATE, InteroperAbilityNames.INTEROP); - ImportCollector.getInstance().collectImport(InteroperAbilityNames.GETCOMPATIBLESTATE); - ImportCollector.getInstance().collectSource(BuilderMethodNames.TRANSFERCOMPATIBLEBUILDER, InteroperAbilityNames.INTEROP); - ImportCollector.getInstance().collectImport(BuilderMethodNames.TRANSFERCOMPATIBLEBUILDER); - ImportCollector.getInstance().collectSource(BuilderMethodNames.TRANSFERCOMPATIBLEUPDATABLEBUILDER, InteroperAbilityNames.INTEROP); - ImportCollector.getInstance().collectImport(BuilderMethodNames.TRANSFERCOMPATIBLEUPDATABLEBUILDER); - return true; +export function processArgumens(arg: arkts.Expression): arkts.ObjectExpression { + if (!arkts.isObjectExpression(arg)) { + throw new Error('Cannot find arguments for InteropComponent'); } - return false; + const properties = arg.properties.map((property: arkts.Property) => { + const key = property.key; + if (arkts.isIdentifier(key) && key.name.startsWith('__backing_')) { + return arkts.factory.updateProperty( + property, + arkts.factory.updateIdentifier( + key, + key.name.slice('__backing_'.length) + ), + property.value + ); + } + return property; + }); + return arkts.factory.updateObjectExpression( + arg, + arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, + properties, + false + ); } @@ -415,25 +365,28 @@ export function isArkUICompatible(node: arkts.AstNode): boolean { * @param node * @returns After Checked, transform instantiate_Interop -> ArkUICompatible */ -export function generateArkUICompatible(node: arkts.CallExpression): arkts.CallExpression { +export function generateArkUICompatible(node: arkts.CallExpression, globalBuilder: boolean): arkts.CallExpression { const classInterop = (node.expression as arkts.MemberExpression).object as arkts.Identifier; const className = classInterop.name; + const decl = arkts.getDecl(classInterop); + if (!(decl instanceof arkts.ClassDefinition)) { + throw Error("can't find legacy class declaration"); + } + const filePath = arkts.getProgramFromAstNode(decl).moduleName; const args = node.arguments; - const path = (args[0] as arkts.StringLiteral).str; - const line = args[1] instanceof arkts.UndefinedLiteral ? undefined : (args[1] as arkts.NumberLiteral).value; - const col = args[2] instanceof arkts.UndefinedLiteral ? undefined : (args[2] as arkts.NumberLiteral).value; - const options = args[3] instanceof arkts.UndefinedLiteral ? undefined : args[3] as arkts.ObjectExpression; - const content = args[4] instanceof arkts.UndefinedLiteral ? undefined : args[4] as arkts.ArrowFunctionExpression; + const options = args.length < 2 || arkts.isUndefinedLiteral(args[1]) ? undefined : processArgumens(args[1]); + const content = args.length < 3 || arkts.isUndefinedLiteral(args[2]) ? undefined : args[2]; + if (!!content) { + arkts.NodeCache.getInstance().collect(content); + } const context: InteropContext = { className: className, - path: path, - line: line, - col: col, + path: filePath, arguments: options, content: content, }; - const varMap: Map = generateVarMap(context, classInterop); + const varMap: Map = generateVarMap(context, decl); const updateProp: arkts.Property[] = []; const initializer = createInitializer(context, varMap, updateProp); const updater = createUpdater(updateProp); @@ -444,7 +397,7 @@ export function generateArkUICompatible(node: arkts.CallExpression): arkts.CallE [ initializer, updater, - arkts.factory.createThisExpression(), + globalBuilder ? arkts.factory.createUndefinedLiteral() : arkts.factory.createThisExpression(), ] ); arkts.NodeCache.getInstance().collect(result); diff --git a/arkui-plugins/ui-plugins/interop/legacy-transformer.ts b/arkui-plugins/ui-plugins/interop/legacy-transformer.ts index 1b831a9b0..a5e9c61c1 100644 --- a/arkui-plugins/ui-plugins/interop/legacy-transformer.ts +++ b/arkui-plugins/ui-plugins/interop/legacy-transformer.ts @@ -23,6 +23,7 @@ import { AbstractVisitor, VisitorOptions } from '../../common/abstract-visitor'; import { InteroperAbilityNames } from './predefines'; import { getCustomComponentOptionsName } from '../utils'; import { factory } from '../ui-factory'; +import { createAndInsertImportDeclaration } from '../../common/arkts-utils'; interface LegacyTransformerOptions extends VisitorOptions { structList?: string[] @@ -49,15 +50,10 @@ export class LegacyTransformer extends AbstractVisitor { // TODO: check reset reset(): void { super.reset(); - this.structList = []; this.componentInterfaceCollection = []; this.scopeInfos = []; } - getList(): string[] { - return this.structList; - } - createParam(name: string, type: string): arkts.ETSParameterExpression { return arkts.factory.createParameterDeclaration( arkts.factory.createIdentifier( @@ -72,38 +68,6 @@ export class LegacyTransformer extends AbstractVisitor { ); } - createInteropMethod(name: string): arkts.MethodDefinition { - const path = this.createParam('path', 'string'); - const line = this.createParam('line', 'number'); - line.setOptional(true); - const col = this.createParam('col', 'number'); - col.setOptional(true); - const options = this.createParam('options', getCustomComponentOptionsName(name)); - options.setOptional(true); - const trailingBlock = this.createParam('trailingBlock', 'Object'); - trailingBlock.setOptional(true); - - const script = arkts.factory.createScriptFunction( - arkts.factory.createBlock([]), - arkts.FunctionSignature.createFunctionSignature( - undefined, - [path, line, col, options, trailingBlock], - arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID), - false - ), - arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC - ); - - return arkts.factory.createMethodDefinition( - arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, - arkts.factory.createIdentifier('instantiate_Interop'), - script, - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC, - false - ); - } - generateMember(map: Map): arkts.ClassProperty[] { const properties: arkts.ClassProperty[] = []; @@ -136,6 +100,75 @@ export class LegacyTransformer extends AbstractVisitor { return interfaceNode; } + createParamsForInstatiate(name: string): arkts.ETSParameterExpression[] { + const paramFactory: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + InteroperAbilityNames.FACTORY, + factory.createLambdaFunctionType(undefined, factory.createTypeReferenceFromString(name!)) + ), + undefined + ); + const paramInitializers: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + InteroperAbilityNames.INITIALIZERS, + factory.createTypeReferenceFromString('__Options_' + name) + ), + undefined + ); + paramInitializers.setOptional(true); + const paramReuseId: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + InteroperAbilityNames.REUSEID, + factory.createTypeReferenceFromString('string') + ), + undefined + ); + paramReuseId.setOptional(true); + const paramContent: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + InteroperAbilityNames.CONTENT, + factory.createLambdaFunctionType() + ), + undefined + ); + paramContent.setOptional(true); + paramContent.annotations = [ + arkts.factory.createAnnotationUsage( + arkts.factory.createIdentifier('Builder') + ) + ]; + return [paramFactory, paramInitializers, paramReuseId, paramContent]; + } + + createInstantiateMethod(definition: arkts.ClassDefinition): arkts.MethodDefinition { + const name = definition.ident?.name!; + const isDecl: boolean = arkts.hasModifierFlag(definition, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE); + const modifiers = + arkts.classDefinitionFlags(definition) | + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC; + const body = isDecl ? undefined : arkts.factory.createBlock([arkts.factory.createReturnStatement()]); + const params = this.createParamsForInstatiate(name); + const returnTypeAnnotation = factory.createTypeReferenceFromString('Object'); + const flags = arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_METHOD; + const kind = arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD; + const key = arkts.factory.createIdentifier(InteroperAbilityNames.INSTANTIATE); + + return factory.createMethodDefinition({ + key, + kind, + function: { + key, + body, + params: params, + returnTypeAnnotation, + flags, + modifiers, + }, + modifiers, + }); + } + processComponent(node: arkts.StructDeclaration): arkts.StructDeclaration | arkts.ClassDeclaration { const definition: arkts.ClassDefinition = node.definition!; const ident = definition.ident!; @@ -146,7 +179,7 @@ export class LegacyTransformer extends AbstractVisitor { this.structList.push(ident.name); } - const instantiate_Interop: arkts.MethodDefinition = this.createInteropMethod(ident.name); + const instantiate = this.createInstantiateMethod(definition); const newDefinition = arkts.factory.updateClassDefinition( definition, @@ -156,7 +189,7 @@ export class LegacyTransformer extends AbstractVisitor { definition.implements, undefined, definition.super, - [...definition.body, instantiate_Interop], + [...definition.body, instantiate], definition.modifiers, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, arkts.Es2pandaLanguage.JS @@ -186,27 +219,27 @@ export class LegacyTransformer extends AbstractVisitor { arkts.factory.createParameterDeclaration( arkts.factory.createIdentifier(InteroperAbilityNames.PARENT, valueType), undefined, - ), + ).setOptional(true), arkts.factory.createParameterDeclaration( arkts.factory.createIdentifier(InteroperAbilityNames.PARAM, valueType), undefined, - ), + ).setOptional(true), arkts.factory.createParameterDeclaration( arkts.factory.createIdentifier('localStorage', valueType), undefined, - ), + ).setOptional(true), arkts.factory.createParameterDeclaration( arkts.factory.createIdentifier(InteroperAbilityNames.ELMTID, valueType), undefined, - ), + ).setOptional(true), arkts.factory.createParameterDeclaration( arkts.factory.createIdentifier(InteroperAbilityNames.PARAMSLAMBDA, valueType), undefined, - ), + ).setOptional(true), arkts.factory.createParameterDeclaration( arkts.factory.createIdentifier(InteroperAbilityNames.EXTRAINFO, valueType), undefined, - ) + ).setOptional(true), ], flags: arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_CONSTRUCTOR, modifiers: arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, @@ -233,20 +266,35 @@ export class LegacyTransformer extends AbstractVisitor { return result; } + createBuilderImport(): void { + if (!this.program) { + throw new Error('Failed to insert import: Transformer has no program'); + } + const source = arkts.factory.createStringLiteral('@ohos.arkui.component'); + const imported = arkts.factory.createIdentifier('Builder'); + createAndInsertImportDeclaration( + source, + imported, + imported, + arkts.Es2pandaImportKinds.IMPORT_KINDS_VALUE, + this.program + ); + } + processEtsScript(node: arkts.EtsScript): arkts.EtsScript { + this.createBuilderImport(); let updateStatements: arkts.AstNode[] = []; if (this.componentInterfaceCollection.length > 0) { updateStatements.push(...this.componentInterfaceCollection); - } - if (updateStatements.length > 0) { return arkts.factory.updateEtsScript(node, [...node.statements, ...updateStatements]); } return node; } enter(node: arkts.AstNode): void { - if (arkts.isStructDeclaration(node) && !!node.definition.ident) { - const scopeInfo: ScopeInfo = { name: node.definition.ident.name }; + if ((arkts.isStructDeclaration(node) || arkts.isClassDeclaration(node)) && !!node.definition.ident) { + const isComponent = node.definition.annotations.some(annotation => annotation.expr instanceof arkts.Identifier && annotation.expr.name === 'Component'); + const scopeInfo: ScopeInfo = { name: node.definition.ident.name, isComponent: isComponent}; this.scopeInfos.push(scopeInfo); } } @@ -318,20 +366,21 @@ export class LegacyTransformer extends AbstractVisitor { if (arkts.isEtsScript(newNode)) { return this.processEtsScript(newNode); } - if (arkts.isStructDeclaration(newNode)) { + if (arkts.isStructDeclaration(newNode) || arkts.isClassDeclaration(newNode)) { const definition = newNode.definition!; const annotations = definition.annotations; - if (annotations.some(annotation => annotation instanceof arkts.Identifier && annotation.name === 'Component')) { - return newNode; + if (annotations.some(annotation => annotation.expr instanceof arkts.Identifier && annotation.expr.name === 'Component')) { + const className = newNode.definition?.ident?.name!; + const memberMap = this.collectComponentMembers(newNode as arkts.StructDeclaration, className); + this.componentInterfaceCollection.push(this.generateComponentInterface(className, node.modifiers, memberMap)); + const updateNode = this.processComponent(newNode); + this.exit(newNode); + return updateNode; } - const className = newNode.definition?.ident?.name!; - const memberMap = this.collectComponentMembers(newNode as arkts.StructDeclaration, className); - this.componentInterfaceCollection.push(this.generateComponentInterface(className, node.modifiers, memberMap)); - const updateNode = this.processComponent(newNode); - this.exit(newNode); - return updateNode; + return newNode; } - if (this.scopeInfos.length > 0 && arkts.isMethodDefinition(newNode)) { + if (this.scopeInfos.length > 0 && this.scopeInfos[this.scopeInfos.length - 1].isComponent === true && + arkts.isMethodDefinition(newNode)) { const kind = newNode.kind; if (kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR) { const updateNode = this.processConstructor(newNode); diff --git a/arkui-plugins/ui-plugins/interop/predefines.ts b/arkui-plugins/ui-plugins/interop/predefines.ts index 5eec3bffe..aabf71186 100644 --- a/arkui-plugins/ui-plugins/interop/predefines.ts +++ b/arkui-plugins/ui-plugins/interop/predefines.ts @@ -18,6 +18,11 @@ export enum InteroperAbilityNames { ARKTS_1_1 = '1.1', ARKTS_1_2 = '1.2', ARKUICOMPATIBLE = 'compatibleComponent', + INSTANTIATE = '$_instantiate', + FACTORY = 'factory', + INITIALIZERS = 'initializers', + REUSEID = 'reuseId', + CONTENT = 'content', ELMTID = 'elmtId', NUMBER = 'number', PARENT = 'parent', diff --git a/arkui-plugins/ui-plugins/interop/utils.ts b/arkui-plugins/ui-plugins/interop/utils.ts index 3dff5e7af..b58a076bb 100644 --- a/arkui-plugins/ui-plugins/interop/utils.ts +++ b/arkui-plugins/ui-plugins/interop/utils.ts @@ -16,7 +16,11 @@ import * as arkts from '@koalaui/libarkts'; -import { ESValueMethodNames, InteroperAbilityNames } from './predefines'; +import { BuilderMethodNames, ESValueMethodNames, InteroperAbilityNames } from './predefines'; +import { LANGUAGE_VERSION } from '../../common/predefines'; +import { FileManager } from '../../common/file-manager'; +import { BuilderLambdaNames } from '../utils'; +import { ImportCollector } from '../../common/import-collector'; /** @@ -221,3 +225,40 @@ export function createGlobal(): arkts.Statement { )] ); } + + +export function isInstantiateImpl(node: arkts.MemberExpression): boolean { + const property = node.property; + if (arkts.isIdentifier(property) && property.name === BuilderLambdaNames.ORIGIN_METHOD_NAME) { + return true; + } + return false; +} + +export function isArkTS1_1(node: arkts.MemberExpression): boolean { + const struct = node.object; + const decl = arkts.getDecl(struct); + if (!decl || !arkts.isClassDefinition(decl) || decl.lang !== arkts.Es2pandaLanguage.JS) { + return false; + } + return true; +} + +export function isInteropComponent(node: arkts.CallExpression): boolean { + if ( + arkts.isMemberExpression(node.expression) && + isInstantiateImpl(node.expression) && + isArkTS1_1(node.expression) + ) { + ImportCollector.getInstance().collectSource(InteroperAbilityNames.ARKUICOMPATIBLE, InteroperAbilityNames.INTEROP); + ImportCollector.getInstance().collectImport(InteroperAbilityNames.ARKUICOMPATIBLE); + ImportCollector.getInstance().collectSource(InteroperAbilityNames.GETCOMPATIBLESTATE, InteroperAbilityNames.INTEROP); + ImportCollector.getInstance().collectImport(InteroperAbilityNames.GETCOMPATIBLESTATE); + ImportCollector.getInstance().collectSource(BuilderMethodNames.TRANSFERCOMPATIBLEBUILDER, InteroperAbilityNames.INTEROP); + ImportCollector.getInstance().collectImport(BuilderMethodNames.TRANSFERCOMPATIBLEBUILDER); + ImportCollector.getInstance().collectSource(BuilderMethodNames.TRANSFERCOMPATIBLEUPDATABLEBUILDER, InteroperAbilityNames.INTEROP); + ImportCollector.getInstance().collectImport(BuilderMethodNames.TRANSFERCOMPATIBLEUPDATABLEBUILDER); + return true; + } + return false; +} diff --git a/arkui-plugins/ui-plugins/struct-translators/factory.ts b/arkui-plugins/ui-plugins/struct-translators/factory.ts index a7ebee0c7..5d8cec169 100644 --- a/arkui-plugins/ui-plugins/struct-translators/factory.ts +++ b/arkui-plugins/ui-plugins/struct-translators/factory.ts @@ -76,12 +76,13 @@ import { } from '../../common/predefines'; import { ObservedTranslator } from '../property-translators/index'; import { addMemoAnnotation } from '../../collectors/memo-collectors/utils'; -import { generateArkUICompatible, isArkUICompatible } from '../interop/interop'; +import { generateArkUICompatible } from '../interop/interop'; import { GenSymGenerator } from '../../common/gensym-generator'; import { MethodTranslator } from '../property-translators/base'; import { MonitorCache } from '../property-translators/cache/monitorCache'; import { PropertyCache } from '../property-translators/cache/propertyCache'; import { ComponentAttributeCache } from '../builder-lambda-translators/cache/componentAttributeCache'; +import { isInteropComponent } from '../interop/utils'; export class factory { /** @@ -1085,13 +1086,14 @@ export class factory { static transformCallExpression( node: arkts.CallExpression, projectConfig: ProjectConfig | undefined, - resourceInfo: ResourceInfo + resourceInfo: ResourceInfo, + globalBuilder: boolean ): arkts.CallExpression { if (arkts.isCallExpression(node) && isResourceNode(node)) { return this.transformResource(node, projectConfig, resourceInfo); } - if (isArkUICompatible(node)) { - return generateArkUICompatible(node as arkts.CallExpression); + if (isInteropComponent(node)) { + return generateArkUICompatible(node as arkts.CallExpression, globalBuilder); } return node; } -- Gitee