diff --git a/arkoala-arkts/libarkts/native/src/generated/bridges.cc b/arkoala-arkts/libarkts/native/src/generated/bridges.cc index 2a17ab86d56777e1f0b5b001f8cbd77f24debaca..6ad6ecdf68be36b4d402945785462a328c959a8b 100644 --- a/arkoala-arkts/libarkts/native/src/generated/bridges.cc +++ b/arkoala-arkts/libarkts/native/src/generated/bridges.cc @@ -10458,3 +10458,9 @@ KNativePointer impl_CreateFunctionDecl(KNativePointer context, KStringPtr& name, } KOALA_INTEROP_3(CreateFunctionDecl, KNativePointer, KNativePointer, KStringPtr, KNativePointer); +KBoolean impl_IsTSInterfaceDeclaration(KNativePointer ast) { + auto&& _ast_ = reinterpret_cast(ast); + auto&& _result_ = GetImpl()->IsTSInterfaceDeclaration(_ast_); + return _result_; +} +KOALA_INTEROP_1(IsTSInterfaceDeclaration, KBoolean, KNativePointer); diff --git a/arkoala-arkts/libarkts/plugins/src/builder-lambda-transformer.ts b/arkoala-arkts/libarkts/plugins/src/builder-lambda-transformer.ts index 6be93bccdffd798db15b5b2012d3d0f0fb0c7cc7..e8700f781a9dbfd1923ab308b550b72938a801bf 100644 --- a/arkoala-arkts/libarkts/plugins/src/builder-lambda-transformer.ts +++ b/arkoala-arkts/libarkts/plugins/src/builder-lambda-transformer.ts @@ -31,10 +31,10 @@ 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( - arkts.factory.createIdentifier( - typeName - ) + typeName ? arkts.factory.createTypeReferenceFromId( + arkts.factory.createIdentifier( + typeName + ) ) : undefined, ), undefined @@ -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 ) @@ -96,7 +96,6 @@ function builderLambdaTypeName(annotation: arkts.AnnotationUsageIr): string | un } */ function findBuilderLambdaAnnotation(node: arkts.CallExpression): arkts.AnnotationUsageIr|undefined { - let decl: arkts.AstNode|undefined = undefined if (arkts.isIdentifier(node.expression)) { decl = arkts.getDecl(node.expression) @@ -116,6 +115,7 @@ function findBuilderLambdaAnnotation(node: arkts.CallExpression): arkts.Annotati } const func = decl.scriptFunction const declAnnotations = arkts.getAnnotations(func) + console.log('declAnnotations: ', declAnnotations.length) if (declAnnotations.length === 0) { return undefined } @@ -194,6 +194,8 @@ export class BuilderLambdaTransformer extends AbstractVisitor { visitor(beforeChildren: arkts.AstNode): arkts.AstNode { const node = this.visitEachChild(beforeChildren) + + if (!arkts.isCallExpression(node)) { return node } @@ -248,6 +250,7 @@ export class BuilderLambdaTransformer extends AbstractVisitor { ) }) + const typeName = builderLambdaTypeName(leaf) const lambdaArg = getLambdaArg(lambdaBody, typeName) diff --git a/arkoala-arkts/libarkts/plugins/src/checked-stage-plugin.ts b/arkoala-arkts/libarkts/plugins/src/checked-stage-plugin.ts index ec0578ffde3afe67fb6a161e67b9d9e6a0123bfb..17998499afa3f7e42a34c7a78c58faf4505defbe 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,12 @@ 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 28a48ec8e36c4af891be69761433ca0a5caf68cd..29828505934384455f68126185f34be2430ac002 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( @@ -87,10 +88,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/property-translators.ts b/arkoala-arkts/libarkts/plugins/src/property-translators.ts new file mode 100644 index 0000000000000000000000000000000000000000..7ff871d62fef0196446b05208b8da73500f00a0e --- /dev/null +++ b/arkoala-arkts/libarkts/plugins/src/property-translators.ts @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2022-2024 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 * as arkts from "@koalaui/libarkts"; +import { + STATE_DECORATOR, + isState, + getNameOrError, + backingField, +} from "./transform-utils"; + +export abstract class PropertyTranslator { + constructor( + protected property: arkts.ClassProperty, + protected structName: string + ) { } + + abstract translateMember(): arkts.AstNode[] + + translateStateWithoutInitializer( + property: arkts.ClassProperty, + decoratorName: string, + structName: string + ): arkts.AstNode[] { + const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(structName); + const originalName = getNameOrError(property.key); + const newName = backingField(originalName) + const originField = arkts.factory.createClassProperty( + arkts.factory.createIdentifier(originalName).setOptional(true), + undefined, + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier('MutableState'), + arkts.factory.createTSTypeParameterInstantiation( + [ + arkts.factory.createTypeReferenceFromId( + arkts.factory.createIdentifier(property.typeAnnotation.dumpSrc()) + ), + ] + ) + ) + ), + property.modifiers, + false + ); + const field = arkts.factory.createClassProperty( + arkts.factory.createIdentifier(newName).setOptional(true), + undefined, + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier('MutableState'), + arkts.factory.createTSTypeParameterInstantiation( + [ + arkts.factory.createTypeReferenceFromId( + arkts.factory.createIdentifier(property.typeAnnotation.dumpSrc()) + ), + ] + ) + ) + ), + property.modifiers, + false + ) + currentStructInfo.stateVariables.add({ + originNode: originField, + translatedNode: field + }); + arkts.GlobalInfo.getInfoInstance().setStructInfo(structName, currentStructInfo); + + + const initializeStruct = this.generateInitializeStruct(newName, originalName, structName, property.typeAnnotation.dumpSrc(), property); + const updateItem = this.generateUpdateStruct(newName, originalName, structName); + + const member = arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(newName).setOptional(true), + arkts.factory.createIdentifier('value'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ) + const thisValue = arkts.factory.createMemberExpression( + arkts.factory.createIdentifier('this'), + member, + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ) + + const getter = this.createStateGetter(originalName, property.typeAnnotation, thisValue); + const setter = this.createStateSetter(originalName, property.typeAnnotation, thisValue); + + return [initializeStruct, updateItem, field, getter, setter] + } + + generateUpdateStruct(newName: string, originalName: string, structName: string): arkts.MethodDefinition { + const call = arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(newName).setOptional(true), + arkts.factory.createIdentifier('update'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [arkts.factory.createMemberExpression( + arkts.factory.createIdentifier('initializer').setOptional(true), + arkts.factory.createIdentifier(originalName), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + )] + ) + const body = arkts.factory.createBlock( + [ + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier('this'), + call, + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + ] + ) + + const param = arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + 'initializers', + arkts.factory.createUnionType([ + arkts.factory.createIdentifier(structName + "Options"), + arkts.factory.createIdentifier('undefined') + ]) + ), + undefined + ) + + const signature = arkts.factory.createFunctionSignature( + undefined, + [ + param + ], + arkts.factory.createIdentifier("void") + ) + + const scriptFunction = arkts.factory.createScriptFunction( + body, + signature, + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_GETTER, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC, + true, + undefined + ) + + return arkts.factory.createMethodDefinition( + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR, + arkts.factory.createIdentifier('__updateStruct'), + arkts.factory.createFunctionExpression(scriptFunction), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + false + ); + } + + generateInitializeStruct(newName: string, originalName: string, structName: string, typeAnnotationStr: string, property: arkts.ClassProperty): arkts.MethodDefinition { + const binaryItem = arkts.factory.createBinaryExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier('initializers').setOptional(true), + arkts.factory.createIdentifier(originalName), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NULLISH_COALESCING, + property.value ?? arkts.factory.createIdentifier('undefined') + ) + const call = arkts.factory.createCallExpression( + arkts.factory.createIdentifier('stateOf'), + arkts.factory.createTypeParameterDeclaration([ + arkts.factory.createTSTypeParameterInstantiation( + [ + arkts.factory.createTypeReferenceFromId( + arkts.factory.createIdentifier(typeAnnotationStr) + ) + ] + ) + ]), + [ + binaryItem, + arkts.factory.createIdentifier('this') + ] + ) + const body = arkts.factory.createBlock( + [ + arkts.factory.createAssignmentExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier('this'), + arkts.factory.createIdentifier(newName), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + call + ) + ] + ) + + const paramInitializers = arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + 'initializers', + arkts.factory.createIdentifier(structName + "Options") + ), + undefined + ) + + const paramContent = arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + 'content', + arkts.factory.createFunctionType( + arkts.FunctionSignature.create( + undefined, + [], + arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID) + ), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW + ) + ), + undefined + ) + + const signature = arkts.factory.createFunctionSignature( + undefined, + [ + paramInitializers, + paramContent + ], + arkts.factory.createIdentifier("void") + ) + + const scriptFunction = arkts.factory.createScriptFunction( + body, + signature, + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_GETTER, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC, + true, + undefined + ) + + return arkts.factory.createMethodDefinition( + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_CONSTRUCTOR, + arkts.factory.createIdentifier('__initializeStruct'), + arkts.factory.createFunctionExpression(scriptFunction), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC, + false + ); + } + + createStateGetter(originalName: string, typeAnnotation: arkts.AstNode, returnValue: arkts.MemberExpression) { + const body = arkts.factory.createBlock( + [ + arkts.factory.createReturnStatement( + returnValue + ), + ] + ) + + const signature = arkts.factory.createFunctionSignature( + undefined, + [], + typeAnnotation + ) + + const scriptFunction = arkts.factory.createScriptFunction( + body, + signature, + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_GETTER, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC, + true, + undefined + ) + + return arkts.factory.createMethodDefinition( + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET, + arkts.factory.createIdentifier(originalName), + arkts.factory.createFunctionExpression(scriptFunction), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_GETTER, + false + ); + } + + createStateSetter(originalName: string, typeAnnotation: arkts.AstNode, left: arkts.MemberExpression) { + + const body = arkts.factory.createBlock( + [ + arkts.factory.createAssignmentExpression( + left, + arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, + arkts.factory.createCallExpression( + arkts.factory.createIdentifier('observedProxy'), + undefined, + [arkts.factory.createIdentifier('value')] + ) + ) + ] + ) + + const signature = arkts.factory.createFunctionSignature( + undefined, + [ + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier( + 'value', + typeAnnotation + ), + undefined + ) + ], + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier('void') + ) + ) + ) + + const scriptFunction = arkts.factory.createScriptFunction( + body, + signature, + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_GETTER, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC, + true, + undefined + ) + + return arkts.factory.createMethodDefinition( + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET, + arkts.factory.createIdentifier(originalName), + arkts.factory.createFunctionExpression(scriptFunction), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_SETTER, + false + ); + } +} + +class State extends PropertyTranslator { + translateMember(): arkts.AstNode[] { + return this.translateStateWithoutInitializer(this.property, STATE_DECORATOR, this.structName) + } +} + +class StorageProp extends PropertyTranslator { + translateMember(): arkts.AstNode[] { + return this.translateStateWithoutInitializer(this.property, STATE_DECORATOR, this.structName) + } +} + +export function classifyProperty(member: arkts.AstNode, structName: string): PropertyTranslator | undefined { + + if (!arkts.isClassProperty(member)) return undefined + // if (isStatic(member)) return undefined + if (isState(member)) return new State(member, structName); + // if (isStorageProp(member)) return new StorageProp(member, context) + // if (isStorageLink(member)) return new StorageLink(member, context) + // if (isLocalStorageLink(member)) return new LocalStorageLink(member, context) + // if (isLocalStorageProp(member)) return new LocalStorageProp(member, context) + // if (isLink(member)) return new Link(member, context) + // if (isProp(member)) return new Prop(member, context) + // if (isObjectLink(member)) return new ObjectLink(member, context) + // if (isProvide(member)) return new Provide(member, context) + // if (isConsume(member)) return new Consume(member, context) + // if (isBuilderParam(member)) return new BuilderParam(member, context) + + // return new PlainProperty(member, context) +} 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 0000000000000000000000000000000000000000..0a0f143227911c5f3ea282b25d21cae748671a22 --- /dev/null +++ b/arkoala-arkts/libarkts/plugins/src/struct-transformer.ts @@ -0,0 +1,91 @@ +import * as arkts from "@koalaui/libarkts" +import { AbstractVisitor } from "./AbstractVisitor"; +import { filterDefined, collect, prependMemoComment } from "./transform-utils"; +import { classifyProperty, PropertyTranslator } from "./property-translators"; +import { nullptr } from "@koalaui/interop"; + +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 isCustomComponentInterface(node: arkts.TSInterfaceDeclaration): boolean { + const structCollection: Set = arkts.GlobalInfo.getInfoInstance().getStructCollection(); + if (structCollection.has(node.id.name.substr(10))) { + return true; + } + return false; +} + +function tranformPropertyMembers(classNode: arkts.ClassDeclaration, propertyTranslators: PropertyTranslator[]): arkts.AstNode[] { + const propertyMembers = propertyTranslators.map(translator => + translator.translateMember() + ) + + // The rest of the struct members are translated here directly. + // const restMembers = [] + return collect( + ...propertyMembers, + // updateStruct, + // ...restMembers + ) +} + +function addVariableInInterface(interfaceNode: arkts.TSInterfaceDeclaration): arkts.TSInterfaceDeclaration { + const interfaceName = interfaceNode.id.name.substr(10); + const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(interfaceName); + const paramters: arkts.AstNode[] = []; + currentStructInfo.stateVariables.forEach((propertyItem) => { + paramters.push(propertyItem.originNode) + paramters.push(propertyItem.translatedNode) + }) + const body = arkts.factory.createBlock(paramters) + const newInterface = arkts.factory.updateInterfaceDeclaration( + interfaceNode, + [], + interfaceNode.id, + nullptr, + body, + false, + false + ); + + return newInterface +} + +function tranformClassMembers(node: arkts.ClassDeclaration): arkts.ClassDeclaration { + const definition: arkts.ClassDefinition = node.definition; + const propertyTranslators = filterDefined( + definition.members.map(it => classifyProperty(it, definition.name.name)) + ); + const translatedMembers = tranformPropertyMembers(node, propertyTranslators); + const restMembers = definition.members.filter((member: arkts.AstNode) => arkts.isMethodDefinition(member)) + + const updateClassDef: arkts.ClassDefinition = arkts.factory.updateClassDefinition( + definition, + definition.name, + [...translatedMembers, ...restMembers], + 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); + } else if (arkts.isTSInterfaceDeclaration(node) && isCustomComponentInterface(node)) { + return addVariableInInterface(node); + } + return node; + } +} diff --git a/arkoala-arkts/libarkts/plugins/src/transform-utils.ts b/arkoala-arkts/libarkts/plugins/src/transform-utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..6d4a7f8992c4d87db2611b86110d1711803b91c9 --- /dev/null +++ b/arkoala-arkts/libarkts/plugins/src/transform-utils.ts @@ -0,0 +1,56 @@ +import * as arkts from "@koalaui/libarkts"; +import { functionOverValue } from "../../../../incremental/compat/build/src"; + +export const STATE_DECORATOR = "State"; + +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)) +} + +export function prependMemoComment(node: arkts.MethodDefinition): arkts.MethodDefinition { + return node; +} + +export function getNameOrError(node: arkts.AstNode): string { + if (arkts.isIdentifier(node)) return node.name; + throw new Error("Expected an identifier, got: " + node.type.toString()) +} + +export function mangle(value: string): string { + return `__${value}` +} + +export function backingField(originalName: string): string { + return mangle(`backing_${originalName}`) +} + +// export function hasDecorator(node: arkts.AstNode, name: string): boolean { +// return getDecorator(node, name) != undefined; +// } + +// export function getDecorator(node: arkts.AstNode, name: string): arkts.Decorator | undefined { +// return filterDecorators(node)?.find(it => isDecorator(it, name)); +// } + +// export function filterDecorators(node: arkts.AstNode): readonly arkts.Decorator[] | undefined { +// return arkts.getAllDecorators(node); +// } + +// export function isStatic(node: arkts.AstNode): boolean { +// if (!arkts.canHaveModifiers(node)) return false +// const modifierLikes = arkts.Es2pandaModifierFlags +// return !!(modifierLikes?.find(it => +// it.kind == arkts.SyntaxKind.StaticKeyword) +// ) +// } + +export function isState(property: arkts.ClassProperty): boolean { + // console.log('aaaaaaaaaaaa: ', arkts.getAnnotations(property)) + // return hasDecorator(property, StateDecorator) + return true +} \ No newline at end of file diff --git a/arkoala-arkts/libarkts/plugins/src/util.ts b/arkoala-arkts/libarkts/plugins/src/util.ts index 3bbd8f63962a6728f6738e686a97037364034ba1..3770d64d270769c81fabf674cf494c7221fb37a1 100644 --- a/arkoala-arkts/libarkts/plugins/src/util.ts +++ b/arkoala-arkts/libarkts/plugins/src/util.ts @@ -160,4 +160,4 @@ export function findFunctionDeclaration(node: ts.Node): ts.FunctionDeclaration | node = node.parent } return undefined -} +} \ No newline at end of file diff --git a/arkoala-arkts/libarkts/plugins/tsconfig.json b/arkoala-arkts/libarkts/plugins/tsconfig.json index d0ce6f2228dd65d7edd13be46f938c0f80e98558..8a406836313d3ddfc0f23e608b866a3a7f06e7ad 100644 --- a/arkoala-arkts/libarkts/plugins/tsconfig.json +++ b/arkoala-arkts/libarkts/plugins/tsconfig.json @@ -14,5 +14,8 @@ "./src/print-visitor.ts", "./src/builder-lambda-transformer.ts", "./src/component-transformer.ts", + "./src/struct-transformer.ts", + "./src/transform-utils.ts", + "./src/property-translators.ts" ] } diff --git a/arkoala-arkts/libarkts/src/arkts-api/factory/nodeFactory.ts b/arkoala-arkts/libarkts/src/arkts-api/factory/nodeFactory.ts index ff36ada6fe71586ed48aadfffa6919ab11c5a85b..fb86296f593dac46014b40dc51e0cc30a57a094f 100644 --- a/arkoala-arkts/libarkts/src/arkts-api/factory/nodeFactory.ts +++ b/arkoala-arkts/libarkts/src/arkts-api/factory/nodeFactory.ts @@ -21,6 +21,7 @@ import { CallExpression, ClassDeclaration, ClassDefinition, + ClassProperty, ETSFunctionType, EtsImportDeclaration, ETSParameterExpression, @@ -47,7 +48,10 @@ import { TSTypeParameterDeclaration, TSTypeParameterInstantiation, VariableDeclaration, - VariableDeclarator + VariableDeclarator, + FunctionSignature, + ETSUndefinedType, + AssignmentExpression } from "../types" import { MemberExpression } from "../to-be-generated/MemberExpression" import { AstNode } from "../peers/AstNode" @@ -238,6 +242,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 +284,16 @@ export const factory = { get updateInterfaceDeclaration() { return compose(TSInterfaceDeclaration.create) }, + get createFunctionSignature() { + return FunctionSignature.create + }, + get createUndefinedType() { + return ETSUndefinedType.create + }, + get createAssignmentExpression() { + return AssignmentExpression.create + }, + get updateAssignmentExpression() { + return compose(AssignmentExpression.create) + }, } diff --git a/arkoala-arkts/libarkts/src/arkts-api/factory/nodeTests.ts b/arkoala-arkts/libarkts/src/arkts-api/factory/nodeTests.ts index 07750fe2ffb1e9e3c7d35614b9f4485866415eaf..71422a1d2022f8e70170522018ca0ac544a18d5c 100644 --- a/arkoala-arkts/libarkts/src/arkts-api/factory/nodeTests.ts +++ b/arkoala-arkts/libarkts/src/arkts-api/factory/nodeTests.ts @@ -1,3 +1,4 @@ +import { global } from "../static/global" import { ArrowFunctionExpression, BlockStatement, @@ -15,6 +16,8 @@ import { StringLiteral, StructDeclaration, VariableDeclaration, + ClassProperty, + TSInterfaceDeclaration, } from "../types" import { MemberExpression } from "../to-be-generated/MemberExpression" import { AstNode } from "../peers/AstNode" @@ -23,6 +26,10 @@ export function isIdentifier(node: AstNode): node is Identifier { return node instanceof Identifier } +export function isTSInterfaceDeclaration(node: AstNode): node is TSInterfaceDeclaration { + return global.generatedEs2panda._IsTSInterfaceDeclaration(node.peer); +} + export function isCallExpression(node: AstNode): node is CallExpression { return node instanceof CallExpression } @@ -86,3 +93,7 @@ export function isStringLiteral(node: AstNode): node is StringLiteral { export function isClassDefinition(node: AstNode): node is ClassDefinition { return node instanceof ClassDefinition } + +export function isClassProperty(node: AstNode): node is ClassProperty { + return node instanceof ClassProperty +} diff --git a/arkoala-arkts/libarkts/src/arkts-api/types.ts b/arkoala-arkts/libarkts/src/arkts-api/types.ts index 7be8fafd1a26b51638a078da5d2ad41dad492ba1..f3978b0c9bfda3d811d4b3b8dd006d4be51f8cb8 100644 --- a/arkoala-arkts/libarkts/src/arkts-api/types.ts +++ b/arkoala-arkts/libarkts/src/arkts-api/types.ts @@ -249,6 +249,7 @@ export class ETSTypeReference extends AstNode { constructor(peer: KPtr) { assertValidPeer(peer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_TYPE_REFERENCE) super(peer) + // this.typeRefPart = unpackNonNullableNode(global.generatedEs2panda._ETSTypeReferencePart(global.context, this.peer)) } static create( @@ -274,14 +275,14 @@ export class ETSTypeReference extends AstNode { } // TODO: - // readonly typeName: Identifier - // readonly typeRefPart: TypeReferencePart + // readonly typeRefPart: ETSTypeReferencePart } export class ETSTypeReferencePart extends AstNode { constructor(peer: KPtr) { assertValidPeer(peer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_TYPE_REFERENCE_PART) super(peer) + this.name = unpackNonNullableNode(global.generatedEs2panda._ETSTypeReferencePartName(global.context, this.peer)) } // TODO: support type params and prev @@ -300,6 +301,7 @@ export class ETSTypeReferencePart extends AstNode { ) ) } + readonly name: Identifier // readonly typeName: Identifier } @@ -412,6 +414,11 @@ export class Identifier extends AstNode { get identifierFlags(): Es2pandaIdentifierFlags { return global.es2panda._IdentifierIdentifierFlags(global.context, this.peer) } + + setOptional(optional: boolean) { + global.generatedEs2panda._IdentifierSetOptional(global.context, this.peer, optional) + return this + } } export class StringLiteral extends AstNode { @@ -538,6 +545,12 @@ export class ScriptFunction extends AstNode { return new ScriptFunction(peer) } + setIdent(id: KPtr): ScriptFunction { + assertValidPeer(id, Es2pandaAstNodeType.AST_NODE_TYPE_IDENTIFIER); + global.generatedEs2panda._ScriptFunctionSetIdent(global.context, this.peer, id); + return this; + } + protected override dumpMessage(): string { const scriptFunctionFlags = global.generatedEs2panda._ScriptFunctionFlagsConst(global.context, this.peer) return ` ` @@ -916,10 +929,19 @@ 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.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.peer); + } + this.name = unpackNonNullableNode(global.generatedEs2panda._ScriptFunctionId(global.context, this.scriptFunction.peer)) } @@ -938,10 +960,20 @@ export class MethodDefinition extends AstNode { passNode(value), modifiers, isComputed - ) + ), + key.peer ) } + kind(): Es2pandaMethodDefinitionKind { + return global.generatedEs2panda._MethodDefinitionKindConst(global.context, this.peer) + } + + // TODO: does not work + isConstructor(): boolean { + return global.generatedEs2panda._MethodDefinitionIsConstructorConst(global.context, this.peer); + } + readonly scriptFunction: ScriptFunction readonly name: Identifier } @@ -961,11 +993,12 @@ 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( key: AstNode, - value: AstNode, + value: AstNode | undefined, typeAnnotation: AstNode, modifiers: KInt, isComputed: boolean @@ -981,6 +1014,7 @@ export class ClassProperty extends ClassElement { ) ) } + readonly typeAnnotation: ETSTypeReference } export class VariableDeclaration extends AstNode { @@ -1042,6 +1076,13 @@ export class ETSUndefinedType extends AstNode { assertValidPeer(peer, Es2pandaAstNodeType.AST_NODE_TYPE_ETS_UNDEFINED_TYPE) super(peer) } + + static create( + ): ETSUndefinedType { + return new ETSUndefinedType( + global.generatedEs2panda._CreateUndefinedLiteral(global.context) + ) + } } export class SuperExpression extends AstNode { @@ -1176,6 +1217,7 @@ export class TSInterfaceDeclaration extends AstNode { constructor(peer: KPtr) { assertValidPeer(peer, Es2pandaAstNodeType.AST_NODE_TYPE_TS_INTERFACE_DECLARATION) super(peer) + this.id = unpackNonNullableNode(global.generatedEs2panda._TSInterfaceDeclarationId(global.context, this.peer)) } static create( @@ -1199,6 +1241,8 @@ export class TSInterfaceDeclaration extends AstNode { ) ) } + + readonly id: Identifier; } export class UndefinedLiteral extends AstNode { diff --git a/arkoala-arkts/libarkts/src/arkts-api/utilities/public.ts b/arkoala-arkts/libarkts/src/arkts-api/utilities/public.ts index dbea933f3c14c53834281e4863a39e766a3d4279..f3e51b9f4d86dfdf40ffaa5efa66c5c3138ca13f 100644 --- a/arkoala-arkts/libarkts/src/arkts-api/utilities/public.ts +++ b/arkoala-arkts/libarkts/src/arkts-api/utilities/public.ts @@ -18,7 +18,7 @@ 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 { isClassDefinition, isFunctionDeclaration, isScriptFunction, isClassProperty } from "../factory/nodeTests" import { Es2pandaContextState } from "../../generated/Es2pandaEnums" import { AstNode } from "../peers/AstNode" import { Identifier } from "../types" @@ -63,7 +63,7 @@ export function getDecl(node: AstNode): AstNode | undefined { } export function getAnnotations(node: AstNode): readonly AnnotationUsageIr[] { - if (!isFunctionDeclaration(node) && !isScriptFunction(node) && !isClassDefinition(node)) { + if (!isFunctionDeclaration(node) && !isScriptFunction(node) && !isClassDefinition(node) && !isClassProperty(node)) { throwError('for now annotations allowed only for: functionDeclaration, scriptFunction, classDefinition') } return unpackNodeArray(global.es2panda._AnnotationAllowedAnnotations(global.context, node.peer, nullptr)) diff --git a/arkoala-arkts/libarkts/src/arkts-api/visitor.ts b/arkoala-arkts/libarkts/src/arkts-api/visitor.ts index 7c32de72275a859c85b41cc601d361d950e77302..d6d1729bea78c2cc040444eaa504b4b4cf455924 100644 --- a/arkoala-arkts/libarkts/src/arkts-api/visitor.ts +++ b/arkoala-arkts/libarkts/src/arkts-api/visitor.ts @@ -39,6 +39,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/libarkts/src/generated/Es2pandaNativeModule.ts b/arkoala-arkts/libarkts/src/generated/Es2pandaNativeModule.ts index d0bd3c8049222380555d2acaebd536f2d4d28c68..d380bee9f110dc6e976b1300348d87fc6e74d70f 100644 --- a/arkoala-arkts/libarkts/src/generated/Es2pandaNativeModule.ts +++ b/arkoala-arkts/libarkts/src/generated/Es2pandaNativeModule.ts @@ -3283,4 +3283,7 @@ export class Es2pandaNativeModule { _CreateFunctionDecl(context: KNativePointer, name: KStringPtr, node: KNativePointer): KNativePointer { throw new Error("This methods was not overloaded by native module initialization") } + _IsTSInterfaceDeclaration(ast: KNativePointer): KBoolean { + throw new Error("Not implemented") + } } \ No newline at end of file diff --git a/arkoala-arkts/libarkts/user.txt b/arkoala-arkts/libarkts/user.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/arkoala-arkts/trivial/user/src/sts/hello.sts b/arkoala-arkts/trivial/user/src/sts/hello.sts index 2c01dc061b7901b12b815ea5296dbe371b62089e..c554d6bc9e6903c9363d637456ca713cf4563876 100644 --- a/arkoala-arkts/trivial/user/src/sts/hello.sts +++ b/arkoala-arkts/trivial/user/src/sts/hello.sts @@ -8,6 +8,7 @@ import { Color } from "@ohos.arkui" @Component struct MyStateSample { @State color: Color = Color.White + @State message: string = 'Hello World' build() { Column({ space: 20 } as ColumnOptions, () => { Text("Hello World!")