From 38ecad024b99de799ff694f6d997356599f2a5f2 Mon Sep 17 00:00:00 2001 From: Jiakai Shi <13494498+victors67@user.noreply.gitee.com> Date: Fri, 14 Feb 2025 14:22:13 +0800 Subject: [PATCH] shijiakai2@huawei.com builder lambda transformation + struct transformation Signed-off-by: s00912778 --- arkoala-arkts/arkui/src/sts/arkui.sts | 12 +- arkoala-arkts/libarkts/native/src/bridges.cc | 9 + .../libarkts/plugins/input/library.sts | 2 + arkoala-arkts/libarkts/plugins/input/main.sts | 2 +- .../libarkts/plugins/src/arkts-utils.ts | 28 +++ .../plugins/src/builder-lambda-transformer.ts | 185 ++++++++++++------ .../plugins/src/checked-stage-plugin.ts | 10 +- .../plugins/src/component-transformer.ts | 5 +- .../plugins/src/struct-transformer.ts | 183 +++++++++++++++++ arkoala-arkts/libarkts/plugins/tsconfig.json | 2 + .../libarkts/src/Es2pandaNativeModule.ts | 40 ++++ .../src/arkts-api/factory/nodeFactory.ts | 21 ++ .../src/arkts-api/factory/nodeTests.ts | 21 ++ arkoala-arkts/libarkts/src/arkts-api/types.ts | 148 +++++++++++++- .../src/arkts-api/utilities/public.ts | 3 +- .../libarkts/src/arkts-api/visitor.ts | 47 +++++ arkoala-arkts/trivial/user/src/sts/hello.sts | 2 +- 17 files changed, 643 insertions(+), 77 deletions(-) create mode 100644 arkoala-arkts/libarkts/plugins/src/arkts-utils.ts create mode 100644 arkoala-arkts/libarkts/plugins/src/struct-transformer.ts diff --git a/arkoala-arkts/arkui/src/sts/arkui.sts b/arkoala-arkts/arkui/src/sts/arkui.sts index 69aa97ece..58a5b1b1a 100644 --- a/arkoala-arkts/arkui/src/sts/arkui.sts +++ b/arkoala-arkts/arkui/src/sts/arkui.sts @@ -29,9 +29,15 @@ export abstract class StructBase { console.log("Struct instantiate redirected") const instance = factory() if (builder !== undefined) builder(instance) - instance.build() + instance._build(builder, content, options) + } + build() { + throw new Error("The struct build() should never be executed directly") + } + + protected _build(style: ((instance: T)=>T)|undefined, content: (() => void)|undefined, options: OptionsT|undefined) { + throw new Error("The struct _build() must have a valid override") } - abstract build() } export enum Color { @@ -141,7 +147,7 @@ export class Text extends CommonMethod { style(instance) //content() } - + fontColor(value: Color): this { console.log("\.fontColor(", Color[value], ")") return this diff --git a/arkoala-arkts/libarkts/native/src/bridges.cc b/arkoala-arkts/libarkts/native/src/bridges.cc index 4250b7908..1ee0b055d 100644 --- a/arkoala-arkts/libarkts/native/src/bridges.cc +++ b/arkoala-arkts/libarkts/native/src/bridges.cc @@ -50,6 +50,15 @@ KNativePointer impl_AnnotationAllowedAnnotations(KNativePointer contextPtr, KNat } KOALA_INTEROP_3(AnnotationAllowedAnnotations, KNativePointer, KNativePointer, KNativePointer, KNativePointer) +KNativePointer impl_AnnotationAllowedAnnotationsConst(KNativePointer contextPtr, KNativePointer nodePtr, KNativePointer returnLen) { + auto context = reinterpret_cast(contextPtr); + auto node = reinterpret_cast(nodePtr); + std::size_t params_len = 0; + auto annotations = GetImpl()->AnnotationAllowedAnnotationsConst(context, node, ¶ms_len); + return new std::vector(annotations, annotations + params_len); +} +KOALA_INTEROP_3(AnnotationAllowedAnnotationsConst, KNativePointer, KNativePointer, KNativePointer, KNativePointer) + KNativePointer impl_AstNodeVariableConst(KNativePointer contextPtr, KNativePointer nodePtr) { auto context = reinterpret_cast(contextPtr); auto node = reinterpret_cast(nodePtr); diff --git a/arkoala-arkts/libarkts/plugins/input/library.sts b/arkoala-arkts/libarkts/plugins/input/library.sts index 9d7a1f0e1..84aa6aac1 100644 --- a/arkoala-arkts/libarkts/plugins/input/library.sts +++ b/arkoala-arkts/libarkts/plugins/input/library.sts @@ -1 +1,3 @@ export @interface Component {} + +export @interface memo {} \ No newline at end of file diff --git a/arkoala-arkts/libarkts/plugins/input/main.sts b/arkoala-arkts/libarkts/plugins/input/main.sts index bff61711a..0a64cf195 100644 --- a/arkoala-arkts/libarkts/plugins/input/main.sts +++ b/arkoala-arkts/libarkts/plugins/input/main.sts @@ -1,4 +1,4 @@ -import { Component } from "./library" +import { Component, memo } from "./library" @interface BuilderLambda { value: string diff --git a/arkoala-arkts/libarkts/plugins/src/arkts-utils.ts b/arkoala-arkts/libarkts/plugins/src/arkts-utils.ts new file mode 100644 index 000000000..549790fd7 --- /dev/null +++ b/arkoala-arkts/libarkts/plugins/src/arkts-utils.ts @@ -0,0 +1,28 @@ +import * as arkts from "@koalaui/libarkts" + +export function annotation(name: string): arkts.AnnotationUsageIr { + const ident: arkts.Identifier = arkts.factory.createIdentifier(name).setAnnotationUsage(); + const annotation: arkts.AnnotationUsageIr = arkts.factory.createAnnotationUsageIr(ident); + + annotation.modifiers = arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_ANNOTATION_USAGE; + ident.parent = annotation; + + return annotation; +} + +export function mangle(value: string): string { + return `__${value}`; +} + +export function backingField(originalName: string): string { + return mangle(`backing_${originalName}`); +} + +export function filterDefined(value: (T | undefined)[]): T[] { + return value.filter((it: T | undefined): it is T => it != undefined); +} + +export function collect(...value: (ReadonlyArray | T | undefined)[]): T[] { + const empty: (T | undefined)[] = [] + return filterDefined(empty.concat(...value)) +} diff --git a/arkoala-arkts/libarkts/plugins/src/builder-lambda-transformer.ts b/arkoala-arkts/libarkts/plugins/src/builder-lambda-transformer.ts index 6be93bccd..e65728dba 100644 --- a/arkoala-arkts/libarkts/plugins/src/builder-lambda-transformer.ts +++ b/arkoala-arkts/libarkts/plugins/src/builder-lambda-transformer.ts @@ -31,7 +31,7 @@ function getLambdaArg(lambdaBody: arkts.AstNode, typeName: string|undefined): ar arkts.factory.createIdentifier( builderLambdaInstanceName, // TODO: it should be the return type of the function annotated with the @BuilderLambda - typeName ? arkts.factory.createTypeReference( + typeName ? arkts.factory.createTypeReferenceFromId( arkts.factory.createIdentifier( typeName ) @@ -45,7 +45,7 @@ function getLambdaArg(lambdaBody: arkts.AstNode, typeName: string|undefined): ar param ], // TODO: it should be the return type of the function annotated with the @BuilderLambda - typeName ? arkts.factory.createTypeReference( + typeName ? arkts.factory.createTypeReferenceFromId( arkts.factory.createIdentifier( typeName ) @@ -190,75 +190,142 @@ function builderLambdaReplace(leaf: arkts.CallExpression): arkts.Identifier|arkt return undefined } -export class BuilderLambdaTransformer extends AbstractVisitor { - visitor(beforeChildren: arkts.AstNode): arkts.AstNode { - const node = this.visitEachChild(beforeChildren) +function builderLambdaBodyRewrite(node: arkts.AstNode): arkts.AstNode { + if (!arkts.isArrowFunctionExpression(node)) return node; - if (!arkts.isCallExpression(node)) { - return node - } + const scriptFunc: arkts.ScriptFunction = node.scriptFunction; + if (!scriptFunc || !scriptFunc.body) return node; - if (true - && arkts.isMemberExpression(node.parent) - && arkts.isIdentifier(node.parent.property) - && arkts.isCallExpression(node.parent.parent) + const body: arkts.BlockStatement = scriptFunc.body; + const statements: arkts.AstNode[] = body.statements.map((statement: arkts.AstNode) => { + if ( + arkts.isExpressionStatement(statement) + && statement.expression + && arkts.isCallExpression(statement.expression) ) { - return node + return transformBuilderLambda(statement.expression); } + return statement; + }); + const updateBody = arkts.factory.updateBlock(body, statements); + + const signature: arkts.FunctionSignature = arkts.FunctionSignature.create(undefined, [], undefined); + const updateFunc: arkts.ScriptFunction = arkts.factory.updateScriptFunction( + scriptFunc, + updateBody, + signature, // TODO: Cannot get signature from original ScriptFunction node + scriptFunc.scriptFunctionFlags, + scriptFunc.modifiers, + false, + undefined + ); - let instanceCalls: arkts.CallExpression[] = [] - let leaf: arkts.CallExpression = node + return arkts.factory.updateArrowFunction(node, updateFunc); +} - while (true - && arkts.isMemberExpression(leaf.expression) - && arkts.isIdentifier(leaf.expression.property) - && arkts.isCallExpression(leaf.expression.object) - ) { - instanceCalls.push( - arkts.factory.createCallExpression( - leaf.expression.property, - undefined, - leaf.arguments - ) - ) - leaf = leaf.expression.object - } +function transformBuilderLambda(node: arkts.CallExpression): arkts.AstNode { + let instanceCalls: arkts.CallExpression[] = [] + let leaf: arkts.CallExpression = node - const replace = builderLambdaReplace(leaf) - if (replace === undefined) { - return node - } - - instanceCalls = instanceCalls.reverse() - let lambdaBody: arkts.Identifier | arkts.CallExpression = arkts.factory.createIdentifier(builderLambdaInstanceName) - instanceCalls.forEach((call)=> { - if (!arkts.isIdentifier(call.expression)) { - throw new Error('call expression should be identifier') - } - lambdaBody = arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - lambdaBody, - call.expression, - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), + while (true + && arkts.isMemberExpression(leaf.expression) + && arkts.isIdentifier(leaf.expression.property) + && arkts.isCallExpression(leaf.expression.object) + ) { + instanceCalls.push( + arkts.factory.createCallExpression( + leaf.expression.property, undefined, - call.arguments + leaf.arguments ) - }) + ) + leaf = leaf.expression.object + } - const typeName = builderLambdaTypeName(leaf) - const lambdaArg = getLambdaArg(lambdaBody, typeName) + const replace = builderLambdaReplace(leaf) + if (replace === undefined) { + return node + } - return arkts.factory.updateCallExpression( - node, - replace, + instanceCalls = instanceCalls.reverse() + let lambdaBody: arkts.Identifier | arkts.CallExpression = arkts.factory.createIdentifier(builderLambdaInstanceName) + instanceCalls.forEach((call)=> { + if (!arkts.isIdentifier(call.expression)) { + throw new Error('call expression should be identifier') + } + lambdaBody = arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + lambdaBody, + call.expression, + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), undefined, - [ - lambdaArg, - ...leaf.arguments - ] + call.arguments ) + }) + + const typeName = builderLambdaTypeName(leaf) + const lambdaArg = getLambdaArg(lambdaBody, typeName) + + let args: arkts.AstNode[] = leaf.arguments.length < 3 + ? leaf.arguments as arkts.AstNode[] + : [ + ...leaf.arguments.slice(0, 2), + builderLambdaBodyRewrite(leaf.arguments.at(2)!), + ...leaf.arguments.slice(3) // Currently, this is never reached since the maximum length of arguments is 3 + ]; + + return arkts.factory.updateCallExpression( + node, + replace, + undefined, + [ + lambdaArg, + ...args + ] + ) +} + +function isBuilderLambda(node: arkts.AstNode): boolean { + const builderLambda: arkts.AstNode | undefined = _getDeclForBuilderLambda(node); + return !!builderLambda; +} + +// TODO: temporary solution for get declaration of a builder lambda +function _getDeclForBuilderLambda(node: arkts.AstNode): arkts.AstNode | undefined { + if (!node || !arkts.isCallExpression(node)) return undefined; + + if (node.expression && arkts.isMemberExpression(node.expression)) { + const _node: arkts.MemberExpression = node.expression; + if (_node.property && arkts.isIdentifier(_node.property) && _node.property.name === "$_instantiate") { + return node; + } + if (_node.object && arkts.isCallExpression(_node.object)) { + return _getDeclForBuilderLambda(_node.object); + } + } + + return undefined; +} + +export class BuilderLambdaTransformer extends AbstractVisitor { + visitEachChild(node: arkts.AstNode): arkts.AstNode { + if (arkts.isCallExpression(node) && isBuilderLambda(node)) { + return node; + } + + return super.visitEachChild(node); + } + + visitor(beforeChildren: arkts.AstNode): arkts.AstNode { + const node = this.visitEachChild(beforeChildren) + + if (arkts.isCallExpression(node) && isBuilderLambda(node)) { + return transformBuilderLambda(node); + } + + return node; } } diff --git a/arkoala-arkts/libarkts/plugins/src/checked-stage-plugin.ts b/arkoala-arkts/libarkts/plugins/src/checked-stage-plugin.ts index ec0578ffd..c7b9f1463 100644 --- a/arkoala-arkts/libarkts/plugins/src/checked-stage-plugin.ts +++ b/arkoala-arkts/libarkts/plugins/src/checked-stage-plugin.ts @@ -2,6 +2,7 @@ import * as ts from "@koalaui/libarkts" import { PrintVisitor } from './print-visitor' import { BuilderLambdaTransformer } from './builder-lambda-transformer' import { ComponentTransformer } from './component-transformer' +import { StructTransformer } from './struct-transformer' export interface TransformerOptions { trace?: boolean, @@ -11,6 +12,13 @@ export default function exampleTransformer( userPluginOptions?: TransformerOptions ) { return (node: ts.EtsScript) => { - return new BuilderLambdaTransformer().visitor(node) + const builderLambdaTransformer = new BuilderLambdaTransformer(); + const structTransformer = new StructTransformer(); + + let script: ts.EtsScript = node; + script = builderLambdaTransformer.visitor(script) as ts.EtsScript; + script = structTransformer.visitor(script) as ts.EtsScript; + + return script; } } diff --git a/arkoala-arkts/libarkts/plugins/src/component-transformer.ts b/arkoala-arkts/libarkts/plugins/src/component-transformer.ts index eb9dea774..10b2a6d0e 100644 --- a/arkoala-arkts/libarkts/plugins/src/component-transformer.ts +++ b/arkoala-arkts/libarkts/plugins/src/component-transformer.ts @@ -71,6 +71,7 @@ export class ComponentTransformer extends AbstractVisitor { processComponent(node: arkts.ClassDeclaration | arkts.StructDeclaration): arkts.ClassDeclaration { const className = node.definition.name.name + arkts.GlobalInfo.getInfoInstance().add(className); this.context.componentNames.push(className) const newDefinition = arkts.factory.updateClassDefinition( @@ -85,10 +86,10 @@ export class ComponentTransformer extends AbstractVisitor { arkts.factory.createIdentifier('StructBase'), arkts.factory.createTSTypeParameterInstantiation( [ - arkts.factory.createTypeReference( + arkts.factory.createTypeReferenceFromId( arkts.factory.createIdentifier(className) ), - arkts.factory.createTypeReference( + arkts.factory.createTypeReferenceFromId( arkts.factory.createIdentifier(`__Options_${className}`) ), ] diff --git a/arkoala-arkts/libarkts/plugins/src/struct-transformer.ts b/arkoala-arkts/libarkts/plugins/src/struct-transformer.ts new file mode 100644 index 000000000..1243e1c91 --- /dev/null +++ b/arkoala-arkts/libarkts/plugins/src/struct-transformer.ts @@ -0,0 +1,183 @@ +import * as arkts from "@koalaui/libarkts" +import { AbstractVisitor } from "./AbstractVisitor"; +import { annotation } from "./arkts-utils"; + +function isCustomComponentClass(node: arkts.ClassDeclaration): boolean { + const structCollection: Set = arkts.GlobalInfo.getInfoInstance().getStructCollection(); + if (structCollection.has(node.definition.name.name)) { + return true; + } + return false; +} + +function isKnownMethodDefinition(method: arkts.MethodDefinition, name: string): boolean { + if (!method || !arkts.isMethodDefinition(method)) return false; + + // For now, we only considered matched method name. + const isNameMatched: boolean = method.name?.name === name; + return isNameMatched; +} + +function createStyleArgInBuildMethod(className: string): arkts.ETSParameterExpression { + const styleLambdaParams: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + 'instance', + arkts.factory.createIdentifier(className), + ), + undefined + ); + + const styleLambda: arkts.ETSFunctionType = arkts.factory.createFunctionType( + arkts.FunctionSignature.create( + undefined, + [ + styleLambdaParams + ], + arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID) + ), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW + ); + + const optionalStyleLambda: arkts.ETSUnionType = arkts.factory.createUnionType([ + styleLambda, + arkts.factory.createUndefinedLiteral() + ]); + + const styleParam: arkts.Identifier = arkts.factory.createIdentifier( + 'style', + optionalStyleLambda + ); + + const param = arkts.factory.createParameterDeclaration(styleParam, undefined); + param.annotations = [annotation("memo")]; + + return param; +} + +function createContentArgInBuildMethod(): arkts.ETSParameterExpression { + const contentLambda: arkts.ETSFunctionType = arkts.factory.createFunctionType( + arkts.FunctionSignature.create( + undefined, + [], + arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID) + ), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW + ); + + const optionalContentLambda: arkts.ETSUnionType = arkts.factory.createUnionType([ + contentLambda, + arkts.factory.createUndefinedLiteral() + ]); + + const contentParam: arkts.Identifier = arkts.factory.createIdentifier( + 'content', + optionalContentLambda + ); + + const param = arkts.factory.createParameterDeclaration(contentParam, undefined); + param.annotations = [annotation("memo")]; + + return param; +} + +function createInitializerArgInBuildMethod(className: string): arkts.ETSParameterExpression { + return arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + 'initializers', + arkts.factory.createTypeReferenceFromId( + arkts.factory.createIdentifier(`__Options_${className}`) + ) + ).setOptional(true), + undefined + ); +} + +function prepareArgsInBuildMethod(className: string): arkts.ETSParameterExpression[] { + return [ + createStyleArgInBuildMethod(className), + createContentArgInBuildMethod(), + createInitializerArgInBuildMethod(className) + ]; +} + +function transformBuildMethod( + method: arkts.MethodDefinition, + className: string +): arkts.MethodDefinition { + const updateKey: arkts.Identifier = arkts.factory.createIdentifier( + '_build' + ); + + const scriptFunction: arkts.ScriptFunction = method.scriptFunction; + + const params: arkts.ETSParameterExpression[] = prepareArgsInBuildMethod(className); + + const signature: arkts.FunctionSignature = arkts.FunctionSignature.create( + undefined, + params, + undefined + ); + const updateScriptFunction = arkts.factory.createScriptFunction( + scriptFunction.body, + signature, + scriptFunction.scriptFunctionFlags, + scriptFunction.modifiers, + false, + undefined + ); + + updateScriptFunction.annotations = [annotation("memo")]; + + // TODO: Currently, just return method itself. Remove this once createMethodDefinition is ready. + return arkts.factory.createMethodDefinition( + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, + updateKey, + arkts.factory.createFunctionExpression(updateScriptFunction), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PROTECTED, + false + ); +} + +function tranformClassMembers(node: arkts.ClassDeclaration): arkts.ClassDeclaration { + const definition: arkts.ClassDefinition = node.definition; + const className: string = node.definition.name.name; + + const updateMembers: arkts.AstNode[] = definition.members.map((member: arkts.AstNode) => { + if (arkts.isMethodDefinition(member) && isKnownMethodDefinition(member, "constructor")) { + return arkts.factory.createMethodDefinition( + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR, + member.name, + arkts.factory.createFunctionExpression(member.scriptFunction), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_CONSTRUCTOR, + false + ); + } + if (arkts.isMethodDefinition(member) && isKnownMethodDefinition(member, "build")) { + return transformBuildMethod(member, className); + } + + return member; + }); + + const updateClassDef: arkts.ClassDefinition = arkts.factory.updateClassDefinition( + definition, + definition.name, + updateMembers, + definition.modifiers, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + definition.typeParamsDecl, + definition.superClass + ); + + return arkts.factory.updateClassDeclaration(node, updateClassDef); +} + +export class StructTransformer extends AbstractVisitor { + visitor(beforeChildren: arkts.AstNode): arkts.AstNode { + const node = this.visitEachChild(beforeChildren); + if (arkts.isClassDeclaration(node) && isCustomComponentClass(node)) { + return tranformClassMembers(node); + } + return node; + } +} diff --git a/arkoala-arkts/libarkts/plugins/tsconfig.json b/arkoala-arkts/libarkts/plugins/tsconfig.json index d0ce6f222..a460c9048 100644 --- a/arkoala-arkts/libarkts/plugins/tsconfig.json +++ b/arkoala-arkts/libarkts/plugins/tsconfig.json @@ -14,5 +14,7 @@ "./src/print-visitor.ts", "./src/builder-lambda-transformer.ts", "./src/component-transformer.ts", + "./src/struct-transformer.ts", + "./src/arkts-utils.ts", ] } diff --git a/arkoala-arkts/libarkts/src/Es2pandaNativeModule.ts b/arkoala-arkts/libarkts/src/Es2pandaNativeModule.ts index e62809f69..f3fe48606 100644 --- a/arkoala-arkts/libarkts/src/Es2pandaNativeModule.ts +++ b/arkoala-arkts/libarkts/src/Es2pandaNativeModule.ts @@ -53,6 +53,9 @@ export class Es2pandaNativeModule { _AnnotationAllowedAnnotations(context: KPtr, node: KPtr, returnLen: KPtr): KPtr { throw new Error("Not implemented") } + _AnnotationAllowedAnnotationsConst(context: KPtr, node: KPtr, returnLen: KPtr): KPtr { + throw new Error("Not implemented") + } _AstNodeRebind(context: KPtr, node: KPtr): void { throw new Error("Not implemented") } @@ -164,6 +167,9 @@ export class Es2pandaNativeModule { _ScriptFunctionBody(context: KPtr, node: KPtr): KPtr { throw new Error("Not implemented") } + _ScriptFunctionAnnotations(context: KPtr, node: KPtr, returnLen: KPtr): KPtr { + throw new Error("Not implemented") + } _ScriptFunctionSetIdent(context: KPtr, ast: KPtr, id: KPtr): KPtr { throw new Error("Not implemented") } @@ -176,6 +182,9 @@ export class Es2pandaNativeModule { _ScriptFunctionSetScope(context: KPtr, ast: KPtr, scope: KPtr): KPtr { throw new Error("Not implemented") } + _ScriptFunctionSetAnnotations(context: KPtr, ast: KPtr, annotations: KPtrArray, annotationsLen: KInt): KPtr { + throw new Error("Not implemented") + } _ScriptFunctionDeclareConst(context: KPtr, node: KPtr): KBoolean { throw new Error("Not implemented") } @@ -191,6 +200,12 @@ export class Es2pandaNativeModule { _ScriptFunctionAddFlag(context: KPtr, node: KPtr, flags: KInt): void { throw new Error("Not implemented") } + _ClassPropertyAnnotations(context: KPtr, node: KPtr, returnLen: KPtr): KPtr { + throw new Error("Not implemented") + } + _ClassPropertySetAnnotations(context: KPtr, ast: KPtr, annotations: KPtrArray, annotationsLen: KInt): KPtr { + throw new Error("Not implemented") + } _UpdateBlockStatement(context: KPtr, original: KPtr, statementList: KPtrArray, statementListLen: KInt): KPtr { throw new Error("Not implemented") } @@ -392,6 +407,12 @@ export class Es2pandaNativeModule { _ETSParameterExpressionIdent(context: KPtr, node: KPtr): KPtr { throw new Error("Not implemented") } + _ETSParameterExpressionAnnotations(context: KPtr, node: KPtr, returnLen: KPtr): KPtr { + throw new Error("Not implemented") + } + _ETSParameterExpressionSetAnnotations(context: KPtr, ast: KPtr, annotations: KPtrArray, annotationsLen: KInt): KPtr { + throw new Error("Not implemented") + } _CreateTSTypeParameterDeclaration(context: KPtr, params: KPtrArray, paramsLen: KInt, requiredParams: KInt): KPtr { throw new Error("Not implemented") } @@ -551,6 +572,25 @@ export class Es2pandaNativeModule { _DeclarationFromIdentifier(context: KPtr, identifier: KPtr): KPtr { throw new Error("Not implemented") } + _IsTSInterfaceDeclaration(ast: KNativePointer): KBoolean { + throw new Error("Not implemented") + } + + _IsAnnotationDeclaration(ast: KNativePointer): KBoolean { + throw new Error("Not implemented") + } + + _IsAnnotationUsage(ast: KNativePointer): KBoolean { + throw new Error("Not implemented") + } + + _IsClassProperty(ast: KNativePointer): KBoolean { + throw new Error("Not implemented") + } + + _CreateAnnotationUsageIr(context: KPtr, ast: KPtr): KPtr { + throw new Error("Not implemented") + } } export function initEs2panda(): Es2pandaNativeModule { diff --git a/arkoala-arkts/libarkts/src/arkts-api/factory/nodeFactory.ts b/arkoala-arkts/libarkts/src/arkts-api/factory/nodeFactory.ts index ff36ada6f..778cb68cd 100644 --- a/arkoala-arkts/libarkts/src/arkts-api/factory/nodeFactory.ts +++ b/arkoala-arkts/libarkts/src/arkts-api/factory/nodeFactory.ts @@ -15,12 +15,14 @@ import { updateNodeByNode } from "../utilities/private" import { + AnnotationUsageIr, ArrowFunctionExpression, BinaryExpression, BlockStatement, CallExpression, ClassDeclaration, ClassDefinition, + ClassProperty, ETSFunctionType, EtsImportDeclaration, ETSParameterExpression, @@ -46,6 +48,7 @@ import { TSTypeParameter, TSTypeParameterDeclaration, TSTypeParameterInstantiation, + UndefinedLiteral, VariableDeclaration, VariableDeclarator } from "../types" @@ -238,6 +241,12 @@ export const factory = { get updateClassDefinition() { return compose(ClassDefinition.create) }, + get createClassProperty() { + return ClassProperty.create + }, + get updateClassProperty() { + return compose(ClassProperty.create) + }, get createFunctionType() { return ETSFunctionType.create }, @@ -274,4 +283,16 @@ export const factory = { get updateInterfaceDeclaration() { return compose(TSInterfaceDeclaration.create) }, + get createUndefinedLiteral() { + return UndefinedLiteral.create + }, + get updateUndefinedLiteral() { + return compose(UndefinedLiteral.create) + }, + get createAnnotationUsageIr() { + return AnnotationUsageIr.create + }, + get updateAnnotationUsageIr() { + return compose(UndefinedLiteral.create) + }, } diff --git a/arkoala-arkts/libarkts/src/arkts-api/factory/nodeTests.ts b/arkoala-arkts/libarkts/src/arkts-api/factory/nodeTests.ts index 07750fe2f..70089c669 100644 --- a/arkoala-arkts/libarkts/src/arkts-api/factory/nodeTests.ts +++ b/arkoala-arkts/libarkts/src/arkts-api/factory/nodeTests.ts @@ -1,4 +1,7 @@ +import { global } from "../static/global" import { + AnnotationDeclaration, + AnnotationUsageIr, ArrowFunctionExpression, BlockStatement, CallExpression, @@ -14,11 +17,29 @@ import { ScriptFunction, StringLiteral, StructDeclaration, + TSInterfaceDeclaration, VariableDeclaration, + ClassProperty } from "../types" import { MemberExpression } from "../to-be-generated/MemberExpression" import { AstNode } from "../peers/AstNode" +export function isClassProperty(node: AstNode): node is ClassProperty { + return global.es2panda._IsClassProperty(node.peer); +} + +export function isAnnotationUsage(node: AstNode): node is AnnotationUsageIr { + return global.es2panda._IsAnnotationUsage(node.peer); +} + +export function isAnnotationDeclaration(node: AstNode): node is AnnotationDeclaration { + return global.es2panda._IsAnnotationDeclaration(node.peer); +} + +export function isTSInterfaceDeclaration(node: AstNode): node is TSInterfaceDeclaration { + return global.es2panda._IsTSInterfaceDeclaration(node.peer); +} + export function isIdentifier(node: AstNode): node is Identifier { return node instanceof Identifier } diff --git a/arkoala-arkts/libarkts/src/arkts-api/types.ts b/arkoala-arkts/libarkts/src/arkts-api/types.ts index 10b6b5197..a3168d27b 100644 --- a/arkoala-arkts/libarkts/src/arkts-api/types.ts +++ b/arkoala-arkts/libarkts/src/arkts-api/types.ts @@ -19,6 +19,7 @@ import { KBoolean, KInt, KNativePointer as KPtr, nullptr } from "@koalaui/intero import { Es2pandaClassDefinitionModifiers, Es2pandaContextState, + Es2pandaExpressionParseFlags, Es2pandaIdentifierFlags, Es2pandaImportKinds, Es2pandaMethodDefinitionKind, @@ -298,6 +299,7 @@ export class ETSTypeReferencePart extends AstNode { constructor(peer: KPtr) { assertValidPeer(peer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_TYPE_REFERENCE_PART) super(peer) + this.typeName = unpackNonNullableNode(global.generatedEs2panda._ETSTypeReferencePartName(global.context, this.peer)); } // TODO: support type params and prev @@ -317,7 +319,7 @@ export class ETSTypeReferencePart extends AstNode { ) } - // readonly typeName: Identifier + readonly typeName: Identifier } export class TSUnionType extends AstNode { @@ -396,9 +398,18 @@ export class Identifier extends AstNode { static create( name: string, - typeAnnotation?: AstNode + typeAnnotation?: AstNode, + isAnnotation?: boolean ): Identifier { - if (typeAnnotation === undefined) { + if (isAnnotation) { + return new Identifier( + global.es2panda._ETSParserCreateExpression( + global.context, + passString(name), + Es2pandaExpressionParseFlags.EXPRESSION_PARSE_FLAGS_ACCEPT_COMMA + ) + ) + } else if (typeAnnotation === undefined) { return new Identifier( global.es2panda._CreateIdentifier1(global.context, passString(name)) ) @@ -409,6 +420,16 @@ export class Identifier extends AstNode { } } + setOptional(optional: boolean): Identifier { + global.generatedEs2panda._IdentifierSetOptional(global.context, this.peer, optional) + return this + } + + setAnnotationUsage(): Identifier { + global.generatedEs2panda._IdentifierSetAnnotationUsage(global.context, this.peer); + return this; + } + protected override dumpMessage(): string { return ` ` } @@ -554,6 +575,12 @@ export class ScriptFunction extends AstNode { return new ScriptFunction(peer) } + setIdent(id: Identifier): ScriptFunction { + assertValidPeer(id.peer, Es2pandaAstNodeType.AST_NODE_TYPE_IDENTIFIER); + global.generatedEs2panda._ScriptFunctionSetIdent(global.context, this.peer, id.peer); + return this; + } + protected override dumpMessage(): string { const scriptFunctionFlags = global.generatedEs2panda._ScriptFunctionFlagsConst(global.context, this.peer) return ` ` @@ -596,6 +623,23 @@ export class ScriptFunction extends AstNode { // readonly signature: FunctionSignature readonly scriptFunctionFlags: KInt readonly ident?: Identifier + + get annotations(): AnnotationUsageIr[] { + return unpackNodeArray(global.es2panda._ScriptFunctionAnnotations( + global.context, + this.peer, + nullptr + )) as AnnotationUsageIr[]; + } + + set annotations(newAnnotations: AnnotationUsageIr[]) { + global.es2panda._ScriptFunctionSetAnnotations( + global.context, + this.peer, + passNodeArray(newAnnotations), + newAnnotations.length + ); + } } export class ArrowFunctionExpression extends AstNode { @@ -721,6 +765,23 @@ export class ETSParameterExpression extends AstNode { ) ) } + + get annotations(): AnnotationUsageIr[] { + return unpackNodeArray(global.es2panda._ETSParameterExpressionAnnotations( + global.context, + this.peer, + nullptr + )) as AnnotationUsageIr[]; + } + + set annotations(newAnnotations: AnnotationUsageIr[]) { + global.es2panda._ETSParameterExpressionSetAnnotations( + global.context, + this.peer, + passNodeArray(newAnnotations), + newAnnotations.length + ); + } } export class TSTypeParameterDeclaration extends AstNode { @@ -933,10 +994,20 @@ export class ClassStaticBlock extends AstNode { } export class MethodDefinition extends AstNode { - constructor(peer: KPtr) { + constructor(peer: KPtr, key?: KPtr) { assertValidPeer(peer, Es2pandaAstNodeType.AST_NODE_TYPE_METHOD_DEFINITION) super(peer) + this.kind = global.generatedEs2panda._MethodDefinitionKindConst(global.context, this.peer); this.scriptFunction = unpackNonNullableNode(global.generatedEs2panda._MethodDefinitionFunction(global.context, this.peer)) + assertValidPeer(this.scriptFunction.peer, Es2pandaAstNodeType.AST_NODE_TYPE_SCRIPT_FUNCTION); + + // Somehow the scriptFunction cannot attach method's key to its ident after checker + if (key) { + assertValidPeer(key, Es2pandaAstNodeType.AST_NODE_TYPE_IDENTIFIER); + const _name = unpackNonNullableNode(key); + this.scriptFunction = this.scriptFunction.setIdent(_name as Identifier); + } + this.name = unpackNonNullableNode(global.generatedEs2panda._ScriptFunctionId(global.context, this.scriptFunction.peer)) this.kind = global.generatedEs2panda._MethodDefinitionKindConst(global.context, this.peer) } @@ -956,13 +1027,19 @@ export class MethodDefinition extends AstNode { passNode(value), modifiers, isComputed - ) + ), + key.peer ) } + // TODO: does not work + isConstructor(): boolean { + return global.generatedEs2panda._MethodDefinitionIsConstructorConst(global.context, this.peer); + } + + readonly kind: Es2pandaMethodDefinitionKind; readonly scriptFunction: ScriptFunction readonly name: Identifier - readonly kind: Es2pandaMethodDefinitionKind } export class ClassElement extends AstNode { @@ -980,6 +1057,7 @@ export class ClassProperty extends ClassElement { constructor(peer: KPtr) { assertValidPeer(peer, Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_PROPERTY) super(peer) + this.typeAnnotation = unpackNonNullableNode(global.generatedEs2panda._ClassPropertyTypeAnnotationConst(global.context, this.peer)); } static create( @@ -1000,6 +1078,25 @@ export class ClassProperty extends ClassElement { ) ) } + + get annotations(): AnnotationUsageIr[] { + return unpackNodeArray(global.es2panda._ScriptFunctionAnnotations( + global.context, + this.peer, + nullptr + )) as AnnotationUsageIr[]; + } + + set annotations(newAnnotations: AnnotationUsageIr[]) { + global.es2panda._ScriptFunctionSetAnnotations( + global.context, + this.peer, + passNodeArray(newAnnotations), + newAnnotations.length + ); + } + + readonly typeAnnotation: ETSTypeReference; } export class VariableDeclaration extends AstNode { @@ -1067,6 +1164,7 @@ export class SuperExpression extends AstNode { constructor(peer: KPtr) { assertValidPeer(peer, Es2pandaAstNodeType.AST_NODE_TYPE_SUPER_EXPRESSION) super(peer) + this.id = unpackNonNullableNode(global.generatedEs2panda._TSInterfaceDeclarationId(global.context, this.peer)); } static create( @@ -1077,6 +1175,8 @@ export class SuperExpression extends AstNode { ) ) } + + readonly id?: Identifier; } export class ImportSource extends ArktsObject { @@ -1165,8 +1265,16 @@ export class AnnotationUsageIr extends AstNode { this.properties = unpackNodeArray(global.generatedEs2panda._AnnotationUsageIrPropertiesConst(global.context, this.peer)) } - // TODO: - // static create + static create( + annoIdent: AstNode + ): AnnotationUsageIr { + return new AnnotationUsageIr( + global.es2panda._CreateAnnotationUsageIr( + global.context, + passNode(annoIdent) + ) + ); + } readonly expr: AstNode readonly properties: readonly ClassProperty[] @@ -1225,4 +1333,28 @@ export class UndefinedLiteral extends AstNode { assertValidPeer(peer, Es2pandaAstNodeType.AST_NODE_TYPE_UNDEFINED_LITERAL) super(peer) } + + static create(): UndefinedLiteral { + return new UndefinedLiteral( + global.generatedEs2panda._CreateUndefinedLiteral(global.context) + ) + } +} + +export class AnnotationDeclaration extends AstNode { + constructor(peer: KPtr) { + assertValidPeer(peer, Es2pandaAstNodeType.AST_NODE_TYPE_ANNOTATION_DECLARATION); + super(peer) + } + + static create( + expr: AstNode + ): AnnotationDeclaration { + return new AnnotationDeclaration ( + global.generatedEs2panda._CreateAnnotationDeclaration( + global.context, + passNode(expr) + ) + ) + } } diff --git a/arkoala-arkts/libarkts/src/arkts-api/utilities/public.ts b/arkoala-arkts/libarkts/src/arkts-api/utilities/public.ts index dbea933f3..7231cc0f5 100644 --- a/arkoala-arkts/libarkts/src/arkts-api/utilities/public.ts +++ b/arkoala-arkts/libarkts/src/arkts-api/utilities/public.ts @@ -16,12 +16,11 @@ import { global } from "../static/global" import { throwError } from "../../utils" import { KNativePointer, nullptr, withStringResult } from "@koalaui/interop" -import { AnnotationUsageIr, MethodDefinition } from "../types" import { passNode, unpackNodeArray, unpackNonNullableNode } from "./private" import { isClassDefinition, isFunctionDeclaration, isScriptFunction } from "../factory/nodeTests" import { Es2pandaContextState } from "../../generated/Es2pandaEnums" import { AstNode } from "../peers/AstNode" -import { Identifier } from "../types" +import { AnnotationUsageIr } from "../types" export function proceedToState(state: Es2pandaContextState): void { if (state <= global.es2panda._ContextState(global.context)) { diff --git a/arkoala-arkts/libarkts/src/arkts-api/visitor.ts b/arkoala-arkts/libarkts/src/arkts-api/visitor.ts index 0e0121e9e..7642ef988 100644 --- a/arkoala-arkts/libarkts/src/arkts-api/visitor.ts +++ b/arkoala-arkts/libarkts/src/arkts-api/visitor.ts @@ -38,6 +38,53 @@ import { MemberExpression } from "../reexport-for-generated" type Visitor = (node: AstNode) => AstNode +export interface DoubleNode { + originNode: AstNode; + translatedNode: AstNode; +} + +export class StructInfo { + stateVariables: Set = new Set(); +} + +export class GlobalInfo { + private _structCollection: Set; + private static instance: GlobalInfo; + private _structMap: Map; + + private constructor() { + this._structCollection = new Set(); + this._structMap = new Map(); + } + + public static getInfoInstance(): GlobalInfo { + if (!this.instance) { + this.instance = new GlobalInfo(); + } + return this.instance; + } + + public add(str: string): void { + this._structCollection.add(str); + } + + public getStructCollection(): Set { + return this._structCollection; + } + + public getStructInfo(structName: string): StructInfo { + const structInfo = this._structMap.get(structName); + if (!structInfo) { + return new StructInfo(); + } + return structInfo; + } + + public setStructInfo(structName: string, info: StructInfo): void { + this._structMap.set(structName, info); + } +} + // TODO: rethink (remove as) function nodeVisitor(node: T, visitor: Visitor): T { if (node === undefined) { diff --git a/arkoala-arkts/trivial/user/src/sts/hello.sts b/arkoala-arkts/trivial/user/src/sts/hello.sts index 41c42c50e..1923eb273 100644 --- a/arkoala-arkts/trivial/user/src/sts/hello.sts +++ b/arkoala-arkts/trivial/user/src/sts/hello.sts @@ -1,7 +1,7 @@ import { Text } from "@ohos.arkui" import { Column, ColumnOptions } from "@ohos.arkui" import { Button } from "@ohos.arkui" -import { Component, State, Entry } from "@ohos.arkui" +import { Component, State, Entry, memo } from "@ohos.arkui" import { Color } from "@ohos.arkui" @Entry -- Gitee