From 29fec415eeb5d4461279da5afcd27e5d97c1870f Mon Sep 17 00:00:00 2001 From: vadimdolgachev Date: Fri, 11 Jul 2025 18:48:11 +0700 Subject: [PATCH 1/6] WIP --- arkoala-arkts/arkui/src/ArkUIEntry.ets | 5 + .../pages/observedv2/TypeDecorator.ets | 0 ets-tests/ets/suites/TraceDecoratorTests.ets | 0 incremental/compat/src/arkts/observable.ts | 61 +++++ incremental/compat/src/index.ts | 1 + incremental/compat/src/ohos/index.ts | 1 + .../compat/src/typescript/observable.ts | 42 ++++ incremental/runtime/src/index.ts | 2 +- ui2abc/ui-plugins/src/class-transformer.ts | 234 +++++++++++++++--- .../ui-plugins/src/property-transformers.ts | 4 +- ui2abc/ui-plugins/src/utils.ts | 19 +- 11 files changed, 329 insertions(+), 40 deletions(-) create mode 100644 ets-tests/ets/ets-tests/pages/observedv2/TypeDecorator.ets create mode 100644 ets-tests/ets/suites/TraceDecoratorTests.ets diff --git a/arkoala-arkts/arkui/src/ArkUIEntry.ets b/arkoala-arkts/arkui/src/ArkUIEntry.ets index 9015daf17..e35ef8154 100644 --- a/arkoala-arkts/arkui/src/ArkUIEntry.ets +++ b/arkoala-arkts/arkui/src/ArkUIEntry.ets @@ -392,6 +392,7 @@ export class Application implements UserApplicationControl { } } } + this.aboutToExit() return this.currentCrash ? nullptr : root!.peer.ptr } @@ -699,6 +700,10 @@ export class Application implements UserApplicationControl { registerNativeModuleLibraryName("ArkUIGeneratedNativeModule", "ArkoalaNative_ark.z") registerNativeModuleLibraryName("TestNativeModule", "ArkoalaNative_ark.z") } + + protected aboutToExit(): void { + this.manager?.reset() + } } function makeClickEvent(x: number, y: number): ClickEvent { diff --git a/ets-tests/ets/ets-tests/pages/observedv2/TypeDecorator.ets b/ets-tests/ets/ets-tests/pages/observedv2/TypeDecorator.ets new file mode 100644 index 000000000..e69de29bb diff --git a/ets-tests/ets/suites/TraceDecoratorTests.ets b/ets-tests/ets/suites/TraceDecoratorTests.ets new file mode 100644 index 000000000..e69de29bb diff --git a/incremental/compat/src/arkts/observable.ts b/incremental/compat/src/arkts/observable.ts index 71ae3340c..64c036b74 100644 --- a/incremental/compat/src/arkts/observable.ts +++ b/incremental/compat/src/arkts/observable.ts @@ -1171,4 +1171,65 @@ export interface ObservableClass { */ export interface ObservableClassV2 { tracedProperties(): ReadonlySet +} + +export class ClassMetadata { + private readonly parent: ClassMetadata | undefined + private readonly markAsObserved: boolean + private readonly markAsObservedV2: boolean + private readonly trackableProperties: ReadonlySet | undefined + private readonly typedProperties: ReadonlyMap | undefined + + constructor(parent: ClassMetadata | undefined, + markAsObserved: boolean, + markAsObservedV2: boolean, + trackable: string[] | undefined, + typed: ReadonlyMap | undefined) { + this.parent = parent + this.markAsObserved = markAsObserved + this.markAsObservedV2 = markAsObservedV2 + if (trackable) { + this.trackableProperties = new Set(trackable) + } + this.typedProperties = typed + } + + isObserved(): boolean { + return this.markAsObserved + } + + isObservedV2(): boolean { + return this.markAsObservedV2 + } + + isTrackable(propertyName: string): boolean { + return (this.trackableProperties?.has(propertyName) || this.parent?.trackableProperties?.has(propertyName)) ?? false + } + + getTypenameTypeDecorator(propertyName: string): string | undefined { + if (this.typedProperties) { + return this.typedProperties?.get(propertyName) + } + if (this.parent) { + return this.parent!.typedProperties?.get(propertyName) + } + return undefined + } + + static getParent(type: Object): ClassMetadata | undefined { + if (type instanceof ClassType) { + const fieldsNum = type.getFieldsNum() + for (let i = 0; i < fieldsNum; i++) { + const field = type.getField(i) + if (field.isStatic() && field.getName() == "__classMetadata") { + const meta = field.getStaticValue() + if (meta != undefined && meta instanceof ClassMetadata) { + return meta + } + break + } + } + } + return undefined + } } \ No newline at end of file diff --git a/incremental/compat/src/index.ts b/incremental/compat/src/index.ts index 3528b436a..68637adba 100644 --- a/incremental/compat/src/index.ts +++ b/incremental/compat/src/index.ts @@ -32,6 +32,7 @@ export { trackableProperties, ObservableClass, ObservableClassV2, + ClassMetadata, observableProxy, observableProxyArray, propDeepCopy, diff --git a/incremental/compat/src/ohos/index.ts b/incremental/compat/src/ohos/index.ts index be94a8a1a..1b594341d 100644 --- a/incremental/compat/src/ohos/index.ts +++ b/incremental/compat/src/ohos/index.ts @@ -31,6 +31,7 @@ export { trackableProperties, ObservableClass, ObservableClassV2, + ClassMetadata, observableProxyArray, propDeepCopy, lcClassName, diff --git a/incremental/compat/src/typescript/observable.ts b/incremental/compat/src/typescript/observable.ts index 4393ea189..4569d189d 100644 --- a/incremental/compat/src/typescript/observable.ts +++ b/incremental/compat/src/typescript/observable.ts @@ -558,3 +558,45 @@ export interface ObservableClass { export interface ObservableClassV2 { tracedProperties(): ReadonlySet | undefined } + +export class ClassMetadata { + private readonly parent: ClassMetadata | undefined + private readonly markAsObserved: boolean + private readonly markAsObservedV2: boolean + private readonly trackableProperties: ReadonlySet | undefined + private readonly typedProperties: ReadonlyMap | undefined + + constructor(parent: ClassMetadata | undefined, + markAsObserved: boolean, + markAsObservedV2: boolean, + trackable: ReadonlySet | undefined, + typed: ReadonlyMap | undefined) { + this.parent = parent + this.markAsObserved = markAsObserved + this.markAsObservedV2 = markAsObservedV2 + this.trackableProperties = trackable + this.typedProperties = typed + } + + isObserved(): boolean { + return this.markAsObserved + } + + isObservedV2(): boolean { + return this.markAsObservedV2 + } + + isTrackable(propertyName: string): boolean { + return (this.trackableProperties?.has(propertyName) || this.parent?.trackableProperties?.has(propertyName)) ?? false + } + + getTypenameTypeDecorator(propertyName: string): string | undefined { + if (this.typedProperties) { + return this.typedProperties?.get(propertyName) + } + if (this.parent) { + return this.parent!.typedProperties?.get(propertyName) + } + return undefined + } +} \ No newline at end of file diff --git a/incremental/runtime/src/index.ts b/incremental/runtime/src/index.ts index 336dc502c..7dc931d49 100644 --- a/incremental/runtime/src/index.ts +++ b/incremental/runtime/src/index.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -export { observableProxy, ObservableClass, ObservableClassV2, TrackableProps } from "@koalaui/compat" +export { observableProxy, ObservableClass, ObservableClassV2, ClassMetadata, TrackableProps } from "@koalaui/compat" export { AnimatedState, diff --git a/ui2abc/ui-plugins/src/class-transformer.ts b/ui2abc/ui-plugins/src/class-transformer.ts index 80795fe3a..00e3de8be 100644 --- a/ui2abc/ui-plugins/src/class-transformer.ts +++ b/ui2abc/ui-plugins/src/class-transformer.ts @@ -16,7 +16,14 @@ import * as arkts from "@koalaui/libarkts" import { classProperties } from "./common/arkts-utils" -import { createETSTypeReference, getComponentPackage, getRuntimePackage, Importer, mangle } from "./utils" +import { + createETSTypeReference, + getComponentPackage, + getDecoratorName, + getRuntimePackage, + Importer, + mangle +} from "./utils" import { DecoratorNames, hasDecorator, isDecoratorAnnotation } from "./utils"; import { backingFieldNameOf, fieldOf } from "./property-transformers"; @@ -76,7 +83,7 @@ export class ClassTransformer extends arkts.AbstractVisitor { if (classContext.decoratorType) { this.addImplObservedDecorator(classContext.decoratorType, classImplements) } - if (classContext.trackedProperties.length) { + if (classContext.trackedPropertyNames.length) { this.addImplTrackDecorator(classImplements) } classDef = arkts.factory.updateClassDefinition( @@ -106,17 +113,16 @@ export class ClassTransformer extends arkts.AbstractVisitor { if (classContext.isObserved()) { createImplObservedFlagMethod(result) } else if (classContext.isObservedV2()) { - const tracedProps = properties - .filter(tracePropVerifier) - .map(it => it.id?.name!) - createImplTrackablePropsMethod(classDef, "ObservableClassV2", "tracedProperties", tracedProps, result) + createImplTrackablePropsMethod(classDef, "ObservableClassV2", "tracedProperties", classContext.tracedPropertyNames, result) + createImplTypedPropsMethod(classDef, classContext.typedPropertyNames, result) } - const trackedProps = properties - .filter(trackPropVerifier) - .map(it => it.id?.name!) + const trackedProps = classContext.trackedPropertyNames if (trackedProps.length > 0) { createImplTrackablePropsMethod(classDef, "TrackableProps", "trackedProperties", trackedProps, result) } + + this.injectClassMetadata(classDef, classContext, result) + body.forEach(node => { if (arkts.isClassProperty(node) && classContext.needRewriteProperty(node)) { this.rewriteProperty(node, classContext, classDef.ident?.name!, result) @@ -129,6 +135,63 @@ export class ClassTransformer extends arkts.AbstractVisitor { return result } + injectClassMetadata(classDef: arkts.ClassDefinition, classContext: ClassContext, result: arkts.ClassElement[]) { + this.importer.add("ClassMetadata", getRuntimePackage()) + const ctorArgs: arkts.Expression[] = [] + if (classDef.super && arkts.isETSTypeReference(classDef.super)) { + ctorArgs.push(arkts.factory.createCallExpression( + fieldOf(arkts.factory.createIdentifier("ClassMetadata"), "getParent"), + [ + arkts.factory.createCallExpression( + fieldOf(arkts.factory.createIdentifier("Type"), "from"), + [], + arkts.factory.createTSTypeParameterInstantiation([createETSTypeReference(classDef.super.baseName?.name!)]), + false, + false, + ) + ], + undefined, + false, + false + )) + } else { + ctorArgs.push(arkts.factory.createUndefinedLiteral()) + } + + let trackableProps: string[] | undefined + if (classContext.trackedPropertyNames) { + trackableProps = classContext.trackedPropertyNames + } else if (classContext.tracedPropertyNames) { + trackableProps = classContext.tracedPropertyNames + } + ctorArgs.push( + arkts.factory.createBooleanLiteral(classContext.isObserved()), + arkts.factory.createBooleanLiteral(classContext.isObservedV2()), + trackableProps ? arkts.factory.createArrayExpression( + trackableProps.map(it => arkts.factory.createStringLiteral(it)) + ) : arkts.factory.createUndefinedLiteral(), + arkts.factory.createUndefinedLiteral() + ) + + result.push( + arkts.factory.createClassProperty( + arkts.factory.createIdentifier(mangle("classMetadata")), + arkts.factory.createETSNewClassInstanceExpression( + createETSTypeReference("ClassMetadata"), + ctorArgs + ), + arkts.factory.createETSUnionType([ + createETSTypeReference("ClassMetadata"), + arkts.factory.createETSUndefinedType() + ]), + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE + | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_READONLY + | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC, + false + ) + ) + } + rewriteProperty(property: arkts.ClassProperty, classContext: ClassContext, className: string, @@ -152,7 +215,13 @@ export class ClassTransformer extends arkts.AbstractVisitor { } rewriteAnnotations(annotations: readonly arkts.AnnotationUsage[]): arkts.AnnotationUsage[] { - const decorators = [DecoratorNames.TRACK, DecoratorNames.TRACE, DecoratorNames.OBSERVED, DecoratorNames.OBSERVED_V2] + const decorators = [ + DecoratorNames.TRACK, + DecoratorNames.TRACE, + DecoratorNames.OBSERVED, + DecoratorNames.OBSERVED_V2, + DecoratorNames.TYPE, + ] return annotations.filter(it => !decorators.some(decorator => isDecoratorAnnotation(it, decorator)) ) @@ -236,32 +305,64 @@ function propertyVerifier(property: arkts.ClassProperty, return arkts.hasModifierFlag(property, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC) } -function trackPropVerifier(property: arkts.ClassProperty): boolean { +function trackedPropVerifier(property: arkts.ClassProperty): boolean { return hasDecorator(property, DecoratorNames.TRACK) && propertyVerifier(property, [arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC]) } -function tracePropVerifier(property: arkts.ClassProperty): boolean { +function tracedPropVerifier(property: arkts.ClassProperty): boolean { return hasDecorator(property, DecoratorNames.TRACE) && propertyVerifier(property) } +function typedPropVerifier(property: arkts.ClassProperty): boolean { + return hasDecorator(property, DecoratorNames.TYPE) + && propertyVerifier(property, [arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC]) +} + +function getDecoratorArgs(decorator: arkts.AnnotationUsage): string[] { + const args: string[] = [] + for (const prop of decorator.properties) { + if (arkts.isClassProperty(prop) && arkts.isIdentifier(prop.value)) { + args.push(prop.value.name) + } + } + return args +} + +function getPropertyDecorators(properties: readonly arkts.ClassProperty[]): ReadonlyMap { + const propertyInfo = new Map() + for (const prop of properties) { + if (prop.id?.name == undefined) { + continue + } + const propName = prop.id.name + const decorators = propertyInfo.get(propName) ?? [] + + for (const anno of prop.annotations) { + let decorator = getDecoratorName(anno) + if (decorator === undefined) { + continue + } + if (decorators.length === 0) { + propertyInfo.set(propName, decorators) + } + decorators.push(new PropertyDecorator(decorator, getDecoratorArgs(anno))) + } + } + return propertyInfo +} + function extractClassContext(clazz: arkts.ClassDeclaration, properties: readonly arkts.ClassProperty[]): ClassContext | undefined { if (clazz.definition != undefined) { - return new ClassContext(getObservedDecoratorType(clazz.definition), - properties - .filter(trackPropVerifier) - .map(it => it.id?.name!), - properties - .filter(tracePropVerifier) - .map(it => it.id?.name!)) + return new ClassContext(getObservedDecoratorType(clazz.definition), getPropertyDecorators(properties)) } return undefined } function createBackingPropertyRValue(property: arkts.ClassProperty, value: arkts.Expression | undefined, classContext: ClassContext): arkts.Expression | undefined { const propValue = observePropIfNeeded(property.id?.name!, value, classContext) - if (isStaticProperty(property) && classContext.tracedProperties.includes(property.id?.name!)) { + if (isStaticProperty(property) && classContext.tracedPropertyNames.includes(property.id?.name!)) { return arkts.factory.createCallExpression( arkts.factory.createIdentifier("globalMutableState"), [propValue!], @@ -275,7 +376,7 @@ function createBackingPropertyRValue(property: arkts.ClassProperty, value: arkts } function createBackingPropertyType(property: arkts.ClassProperty, classContext: ClassContext) { - return isStaticProperty(property) && classContext.tracedProperties.includes(property.id?.name!) + return isStaticProperty(property) && classContext.tracedPropertyNames.includes(property.id?.name!) ? undefined : property.typeAnnotation } @@ -285,7 +386,7 @@ function createPropertyAccess(className: string, property: arkts.ClassProperty, ? createETSTypeReference(className) : arkts.factory.createThisExpression() const expr = fieldOf(receiver, backingFieldNameOf(property)) - if (isStaticProperty(property) && classContext.tracedProperties.includes(property.id?.name!)) { + if (isStaticProperty(property) && classContext.tracedPropertyNames.includes(property.id?.name!)) { return fieldOf(expr, "value") } return expr @@ -449,6 +550,39 @@ function createImplTrackablePropsMethod(classDef: arkts.ClassDefinition, ) } +function createImplTypedPropsMethod(classDef: arkts.ClassDefinition, + trackableProps: readonly string[], + result: arkts.ClassElement[]) { + const typedPropsIdent = mangle("typedProps") + const body: arkts.Statement[] = [] + body.push( + arkts.factory.createVariableDeclaration( + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [ + arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + arkts.factory.createIdentifier(typedPropsIdent, createETSTypeReference("Array", ["string"])), + arkts.factory.createArrayExpression( + trackableProps.map(it => arkts.factory.createStringLiteral(it)) + ) + ) + ] + ) + ) + + body.push(arkts.factory.createReturnStatement( + arkts.factory.createETSNewClassInstanceExpression( + createETSTypeReference("Set", ["string"]), + [arkts.factory.createIdentifier(typedPropsIdent)] + ) + )) + createMethodDefinition("typedProps", + arkts.factory.createBlockStatement(body), + createETSTypeReference("ReadonlySet", ["string"]), + result + ) +} + function createImplObservedFlagMethod(result: arkts.ClassElement[]) { result.push( arkts.factory.createClassProperty( @@ -497,9 +631,9 @@ function createMethodDefinition(methodName: string, function observePropIfNeeded(propertyName: string, propertyValue: arkts.Expression | undefined, classContext: ClassContext) { - const isClassFullyObserved = classContext.isObserved() && classContext.trackedProperties.length == 0 - const isTrackedProp = classContext.tracedProperties.includes(propertyName) - const isTracedProp = classContext.trackedProperties.includes(propertyName) + const isClassFullyObserved = classContext.isObserved() && classContext.trackedPropertyNames.length == 0 + const isTrackedProp = classContext.tracedPropertyNames.includes(propertyName) + const isTracedProp = classContext.trackedPropertyNames.includes(propertyName) if (propertyValue && (isClassFullyObserved || isTrackedProp || isTracedProp)) { return arkts.factory.createCallExpression( arkts.factory.createIdentifier("observableProxy"), @@ -574,26 +708,44 @@ function recreateDisposedState(property: arkts.ClassProperty, class ClassContext { readonly decoratorType: ObservedDecoratorType | undefined - readonly trackedProperties: readonly string[] = [] - readonly tracedProperties: readonly string[] = [] + readonly properties: ReadonlyMap constructor(observedDecoratorType: ObservedDecoratorType | undefined, - trackedProperties: readonly string[], - tracedProperties: readonly string[]) { + properties: ReadonlyMap) { this.decoratorType = observedDecoratorType - this.trackedProperties = trackedProperties - this.tracedProperties = tracedProperties + this.properties = properties this.verify() } + get trackedPropertyNames() { + return Array.from(this.properties.entries()) + .filter(it => it[1].some(it => it.name == DecoratorNames.TRACK)) + .map(it => it[0]) + } + + get tracedPropertyNames() { + return Array.from(this.properties.entries()) + .filter(it => it[1].some(it => it.name == DecoratorNames.TRACE)) + .map(it => it[0]) + } + + get typedPropertyNames() { + return this.typedProperties.map(it => it[0]) + } + + get typedProperties() { + return Array.from(this.properties.entries()) + .filter(it => it[1].some(it => it.name == DecoratorNames.TYPE)) + } + private verify() { if (this.decoratorType == DecoratorNames.OBSERVED_V2) { - if (this.trackedProperties.length) { + if (this.trackedPropertyNames.length) { throw new Error("@Track decorator is not applicable with @ObservedV2 decorator") } } else { - if (this.tracedProperties.length) { + if (this.tracedPropertyNames.length || this.typedPropertyNames.length) { throw new Error("@Trace decorator is only used with @ObservedV2 decorator") } } @@ -608,13 +760,23 @@ class ClassContext { } isClassObservable() { - return this.decoratorType || this.trackedProperties.length + return this.decoratorType || this.trackedPropertyNames.length } needRewriteProperty(property: arkts.ClassProperty): boolean { - if (this.isObserved() || this.trackedProperties.length) { + if (this.isObserved() || this.trackedPropertyNames.length) { return !isStaticProperty(property) } - return this.isObservedV2() && this.tracedProperties.includes(property.id?.name!) + return this.isObservedV2() && this.tracedPropertyNames.includes(property.id?.name!) + } +} + +class PropertyDecorator { + readonly name: DecoratorNames + readonly args: string[] = [] + + constructor(name: DecoratorNames, args: string[] = []) { + this.name = name + this.args = args } } \ No newline at end of file diff --git a/ui2abc/ui-plugins/src/property-transformers.ts b/ui2abc/ui-plugins/src/property-transformers.ts index 5a31c05c2..816f4e444 100644 --- a/ui2abc/ui-plugins/src/property-transformers.ts +++ b/ui2abc/ui-plugins/src/property-transformers.ts @@ -514,10 +514,10 @@ export class ObjectLinkTransformer extends PropertyTransformerBase { } } -export function fieldOf(base: arkts.Expression, name: string, optional: boolean = false): arkts.Expression { +export function fieldOf(base: arkts.Expression, name: string | arkts.Identifier, optional: boolean = false): arkts.Expression { const result = arkts.factory.createMemberExpression( base, - arkts.factory.createIdentifier(name), + typeof name == "string" ? arkts.factory.createIdentifier(name) : name, arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, false, optional diff --git a/ui2abc/ui-plugins/src/utils.ts b/ui2abc/ui-plugins/src/utils.ts index 6ac56d09a..072802416 100644 --- a/ui2abc/ui-plugins/src/utils.ts +++ b/ui2abc/ui-plugins/src/utils.ts @@ -264,8 +264,11 @@ export enum DecoratorNames { LOCAL_BUILDER = "LocalBuilder", TRACK = "Track", TRACE = "Trace", + TYPE = "Type", } +const DECORATOR_NAMES_SET = new Set(Object.values(DecoratorNames)); + export enum DecoratorParameters { USE_SHARED_STORAGE = "useSharedStorage", ALLOW_OVERRIDE = "allowOverride", @@ -277,8 +280,22 @@ export function hasEntryAnnotation(node: arkts.ClassDefinition): boolean { ) } +export function getAnnotationName(anno: arkts.AnnotationUsage): string | undefined { + if (!anno.expr) { + return undefined + } + return arkts.isIdentifier(anno.expr) ? anno.expr.name : undefined +} + +export function getDecoratorName(anno: arkts.AnnotationUsage): DecoratorNames | undefined { + const name = getAnnotationName(anno); + return name && DECORATOR_NAMES_SET.has(name as DecoratorNames) + ? name as DecoratorNames + : undefined +} + export function isDecoratorAnnotation(anno: arkts.AnnotationUsage, decoratorName: DecoratorNames): boolean { - return !!anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === decoratorName; + return getAnnotationName(anno) === decoratorName; } export function hasDecorator(property: arkts.ClassProperty | arkts.ClassDefinition | arkts.ClassDeclaration | arkts.MethodDefinition | arkts.FunctionDeclaration | arkts.ETSParameterExpression, decoratorName: DecoratorNames): boolean { -- Gitee From af72f4e34118d31f57fc480be1744fe6adaeeda4 Mon Sep 17 00:00:00 2001 From: vadimdolgachev Date: Wed, 23 Jul 2025 23:08:59 +0700 Subject: [PATCH 2/6] Added ClassMetadata populating --- incremental/compat/src/arkts/observable.ts | 19 ++++++++- ui2abc/ui-plugins/src/class-transformer.ts | 47 +++++++++++++--------- 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/incremental/compat/src/arkts/observable.ts b/incremental/compat/src/arkts/observable.ts index 64c036b74..2e20ff815 100644 --- a/incremental/compat/src/arkts/observable.ts +++ b/incremental/compat/src/arkts/observable.ts @@ -1164,6 +1164,8 @@ export interface ObservableClass { * Indicates whether the class is decorated with `@Observed`. */ isObserved(): boolean + + getClassMetadata(): ClassMetadata | undefined } /** @@ -1177,21 +1179,34 @@ export class ClassMetadata { private readonly parent: ClassMetadata | undefined private readonly markAsObserved: boolean private readonly markAsObservedV2: boolean + + /** + * Class property names marked with the @Track or @Trace decorator + * @private + */ private readonly trackableProperties: ReadonlySet | undefined + + /** + * Contains fields marked with the @Type decorator. + * The key of the map is the property name and the value is the typename of the corresponding field. + * @private + */ private readonly typedProperties: ReadonlyMap | undefined constructor(parent: ClassMetadata | undefined, markAsObserved: boolean, markAsObservedV2: boolean, trackable: string[] | undefined, - typed: ReadonlyMap | undefined) { + typed: [string, string][] | undefined) { this.parent = parent this.markAsObserved = markAsObserved this.markAsObservedV2 = markAsObservedV2 if (trackable) { this.trackableProperties = new Set(trackable) } - this.typedProperties = typed + if (typed) { + this.typedProperties = new Map(typed) + } } isObserved(): boolean { diff --git a/ui2abc/ui-plugins/src/class-transformer.ts b/ui2abc/ui-plugins/src/class-transformer.ts index 00e3de8be..028b3459e 100644 --- a/ui2abc/ui-plugins/src/class-transformer.ts +++ b/ui2abc/ui-plugins/src/class-transformer.ts @@ -137,10 +137,12 @@ export class ClassTransformer extends arkts.AbstractVisitor { injectClassMetadata(classDef: arkts.ClassDefinition, classContext: ClassContext, result: arkts.ClassElement[]) { this.importer.add("ClassMetadata", getRuntimePackage()) - const ctorArgs: arkts.Expression[] = [] + const classMetadataType = createETSTypeReference("ClassMetadata") + + const metaDataCtorArgs: arkts.Expression[] = [] if (classDef.super && arkts.isETSTypeReference(classDef.super)) { - ctorArgs.push(arkts.factory.createCallExpression( - fieldOf(arkts.factory.createIdentifier("ClassMetadata"), "getParent"), + metaDataCtorArgs.push(arkts.factory.createCallExpression( + fieldOf(classMetadataType, "getParent"), [ arkts.factory.createCallExpression( fieldOf(arkts.factory.createIdentifier("Type"), "from"), @@ -155,35 +157,42 @@ export class ClassTransformer extends arkts.AbstractVisitor { false )) } else { - ctorArgs.push(arkts.factory.createUndefinedLiteral()) + metaDataCtorArgs.push(arkts.factory.createUndefinedLiteral()) } let trackableProps: string[] | undefined - if (classContext.trackedPropertyNames) { + if (classContext.trackedPropertyNames.length > 0) { trackableProps = classContext.trackedPropertyNames - } else if (classContext.tracedPropertyNames) { + } else if (classContext.tracedPropertyNames.length > 0) { trackableProps = classContext.tracedPropertyNames } - ctorArgs.push( + + let typedProps: [string, string][] | undefined + if (classContext.typedProperties.length > 0) { + typedProps = [] + for (const prop of classContext.typedProperties.values()) { + typedProps.push([prop[0], prop[1][0].args[0]]) + } + } + + metaDataCtorArgs.push( arkts.factory.createBooleanLiteral(classContext.isObserved()), arkts.factory.createBooleanLiteral(classContext.isObservedV2()), - trackableProps ? arkts.factory.createArrayExpression( - trackableProps.map(it => arkts.factory.createStringLiteral(it)) - ) : arkts.factory.createUndefinedLiteral(), - arkts.factory.createUndefinedLiteral() + trackableProps + ? arkts.factory.createArrayExpression(trackableProps.map(it => arkts.factory.createStringLiteral(it))) + : arkts.factory.createUndefinedLiteral(), + typedProps + ? arkts.factory.createArrayExpression(typedProps.map(it => + arkts.factory.createArrayExpression([arkts.factory.createStringLiteral(it[0]), + arkts.factory.createStringLiteral(it[1])]))) + : arkts.factory.createUndefinedLiteral() ) result.push( arkts.factory.createClassProperty( arkts.factory.createIdentifier(mangle("classMetadata")), - arkts.factory.createETSNewClassInstanceExpression( - createETSTypeReference("ClassMetadata"), - ctorArgs - ), - arkts.factory.createETSUnionType([ - createETSTypeReference("ClassMetadata"), - arkts.factory.createETSUndefinedType() - ]), + arkts.factory.createETSNewClassInstanceExpression(classMetadataType, metaDataCtorArgs), + arkts.factory.createETSUnionType([classMetadataType, arkts.factory.createETSUndefinedType()]), arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_READONLY | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC, -- Gitee From c64f21c3814b984ec6194fcc748a460315cea237 Mon Sep 17 00:00:00 2001 From: vadimdolgachev Date: Thu, 24 Jul 2025 16:31:08 +0700 Subject: [PATCH 3/6] Fix compilation errors --- .../arkui/src/ArkStateProperties.ets | 7 +- incremental/common/src/index.ts | 2 +- incremental/compat/src/arkts/observable.ts | 56 +++-- incremental/compat/src/index.ts | 5 +- incremental/compat/src/ohos/index.ts | 5 +- .../compat/src/typescript/observable.ts | 82 ++++---- incremental/runtime/src/index.ts | 2 +- incremental/runtime/src/states/State.ts | 9 +- ui2abc/ui-plugins/src/class-transformer.ts | 191 +++--------------- 9 files changed, 115 insertions(+), 244 deletions(-) diff --git a/arkoala-arkts/arkui/src/ArkStateProperties.ets b/arkoala-arkts/arkui/src/ArkStateProperties.ets index 2fdb6e595..f91e2e22b 100644 --- a/arkoala-arkts/arkui/src/ArkStateProperties.ets +++ b/arkoala-arkts/arkui/src/ArkStateProperties.ets @@ -14,7 +14,7 @@ */ import { int32, observableProxy, propDeepCopy } from "@koalaui/common" -import { mutableState, scheduleCallback, MutableState, GlobalStateManager, globalMutableState, ObservableClassV2 } from "@koalaui/runtime" +import { mutableState, scheduleCallback, MutableState, GlobalStateManager, globalMutableState, ObservableClass, ClassMetadata } from "@koalaui/runtime" import { SubscribedAbstractProperty } from "./ArkState" import { AppStorage, LocalStorage } from "./Storage" @@ -59,7 +59,10 @@ class StatableHolder { } private isStatable(value: Value | undefined): boolean { - return value instanceof ObservableClassV2 + if (value instanceof ObservableClass) { + return value.getClassMetadata()?.isObservedV2() ?? false + } + return false } } diff --git a/incremental/common/src/index.ts b/incremental/common/src/index.ts index 174cd00fb..0533296ef 100644 --- a/incremental/common/src/index.ts +++ b/incremental/common/src/index.ts @@ -34,7 +34,7 @@ export { ObservableHandler, observableProxy, observableProxyArray, - TrackableProps, + TrackableProperties, trackableProperties, isFunction, propDeepCopy, diff --git a/incremental/compat/src/arkts/observable.ts b/incremental/compat/src/arkts/observable.ts index 2e20ff815..7ff0861f0 100644 --- a/incremental/compat/src/arkts/observable.ts +++ b/incremental/compat/src/arkts/observable.ts @@ -233,7 +233,7 @@ export function observableProxy(value: Value, parent?: ObservableHandler, const valueType = Type.of(value) if (valueType instanceof ClassType && !(value instanceof BaseEnum)) { const isObservable = isObservableClass(value as Object) - if (trackableProperties(value as Object) == undefined && !isObservable) { + if (!hasTrackableProperties(value as Object) && !isObservable) { return value as Value } if (valueType.hasEmptyConstructor()) { @@ -1133,49 +1133,38 @@ class ObservableDate extends Date { } function isObservableClass(value: Object): boolean { - return value instanceof ObservableClass ? value.isObserved() : false; -} - -/** - * Interface for getting the observed properties of a class - */ -export interface TrackableProps { - /** - * Retrieves the set of property names that are being tracked for changes using `@Track` decorator - */ - trackedProperties(): ReadonlySet + if (value instanceof ObservableClass) { + return value.getClassMetadata()?.isObserved() ?? false + } + return false } -export function trackableProperties(value: T): ReadonlySet | undefined { - if (value instanceof TrackableProps) { - return value.trackedProperties() - } - if (value instanceof ObservableClassV2) { - return value.tracedProperties() +function hasTrackableProperties(value: T): boolean { + if (value instanceof ObservableClass) { + return value.getClassMetadata()?.hasTrackableProperties() ?? false } - return undefined + return false } /** * Interface for getting the observability status of a class */ export interface ObservableClass { - /** - * Indicates whether the class is decorated with `@Observed`. - */ - isObserved(): boolean - getClassMetadata(): ClassMetadata | undefined } /** - * To implement the ObservedV2 decorator + * Interface for getting the observed properties of a class */ -export interface ObservableClassV2 { - tracedProperties(): ReadonlySet +export interface TrackableProperties { + isTrackable(propertyName: string): boolean } -export class ClassMetadata { +export function trackableProperties(value: T): TrackableProperties | undefined { + return value instanceof ClassMetadata ? value : undefined +} + +export class ClassMetadata implements TrackableProperties { private readonly parent: ClassMetadata | undefined private readonly markAsObserved: boolean private readonly markAsObservedV2: boolean @@ -1218,7 +1207,14 @@ export class ClassMetadata { } isTrackable(propertyName: string): boolean { - return (this.trackableProperties?.has(propertyName) || this.parent?.trackableProperties?.has(propertyName)) ?? false + return (this.trackableProperties?.has(propertyName) || this.parent?.isTrackable(propertyName)) ?? false + } + + hasTrackableProperties(): boolean { + if (this.trackableProperties) { + return this.trackableProperties!.size > 0 + } + return this.parent?.hasTrackableProperties() ?? false } getTypenameTypeDecorator(propertyName: string): string | undefined { @@ -1226,7 +1222,7 @@ export class ClassMetadata { return this.typedProperties?.get(propertyName) } if (this.parent) { - return this.parent!.typedProperties?.get(propertyName) + return this.parent!.getTypenameTypeDecorator(propertyName) } return undefined } diff --git a/incremental/compat/src/index.ts b/incremental/compat/src/index.ts index 68637adba..d068f170a 100644 --- a/incremental/compat/src/index.ts +++ b/incremental/compat/src/index.ts @@ -28,10 +28,9 @@ export { Observed, Observable, ObservableHandler, - TrackableProps, - trackableProperties, ObservableClass, - ObservableClassV2, + TrackableProperties, + trackableProperties, ClassMetadata, observableProxy, observableProxyArray, diff --git a/incremental/compat/src/ohos/index.ts b/incremental/compat/src/ohos/index.ts index 1b594341d..892265e66 100644 --- a/incremental/compat/src/ohos/index.ts +++ b/incremental/compat/src/ohos/index.ts @@ -27,10 +27,9 @@ export { Observable, ObservableHandler, observableProxy, - TrackableProps, - trackableProperties, ObservableClass, - ObservableClassV2, + TrackableProperties, + trackableProperties, ClassMetadata, observableProxyArray, propDeepCopy, diff --git a/incremental/compat/src/typescript/observable.ts b/incremental/compat/src/typescript/observable.ts index 4569d189d..40275c8a2 100644 --- a/incremental/compat/src/typescript/observable.ts +++ b/incremental/compat/src/typescript/observable.ts @@ -522,60 +522,56 @@ function clearObservable(data: any) { } } -/** - * Interface for getting the observed properties of a class - */ -export interface TrackableProps { - /** - * Retrieves the set of property names that are being tracked for changes using `@Track` decorator - */ - trackedProperties(): ReadonlySet | undefined -} - -export function trackableProperties(value: any): ReadonlySet | undefined { - if (typeof value.trackedProperties === 'function') { - return (value as TrackableProps).trackedProperties() - } - if (typeof value.tracedProperties === 'function') { - return (value as ObservableClassV2).tracedProperties() - } - return undefined -} - /** * Interface for getting the observability status of a class */ export interface ObservableClass { - /** - * Indicates whether the class is decorated with `@Observed`. - */ - isObserved(): boolean + getClassMetadata(): ClassMetadata | undefined } /** - * To implement the ObservedV2 decorator + * Interface for getting the observed properties of a class */ -export interface ObservableClassV2 { - tracedProperties(): ReadonlySet | undefined +export interface TrackableProperties { + isTrackable(propertyName: string): boolean +} + +export function trackableProperties(value: T): TrackableProperties | undefined { + return value instanceof ClassMetadata ? value : undefined } -export class ClassMetadata { +export class ClassMetadata implements TrackableProperties { private readonly parent: ClassMetadata | undefined private readonly markAsObserved: boolean private readonly markAsObservedV2: boolean + + /** + * Class property names marked with the @Track or @Trace decorator + * @private + */ private readonly trackableProperties: ReadonlySet | undefined + + /** + * Contains fields marked with the @Type decorator. + * The key of the map is the property name and the value is the typename of the corresponding field. + * @private + */ private readonly typedProperties: ReadonlyMap | undefined constructor(parent: ClassMetadata | undefined, markAsObserved: boolean, markAsObservedV2: boolean, - trackable: ReadonlySet | undefined, - typed: ReadonlyMap | undefined) { + trackable: string[] | undefined, + typed: [string, string][] | undefined) { this.parent = parent this.markAsObserved = markAsObserved this.markAsObservedV2 = markAsObservedV2 - this.trackableProperties = trackable - this.typedProperties = typed + if (trackable) { + this.trackableProperties = new Set(trackable) + } + if (typed) { + this.typedProperties = new Map(typed) + } } isObserved(): boolean { @@ -587,7 +583,14 @@ export class ClassMetadata { } isTrackable(propertyName: string): boolean { - return (this.trackableProperties?.has(propertyName) || this.parent?.trackableProperties?.has(propertyName)) ?? false + return (this.trackableProperties?.has(propertyName) || this.parent?.isTrackable(propertyName)) ?? false + } + + hasTrackableProperties(): boolean { + if (this.trackableProperties) { + return this.trackableProperties!.size > 0 + } + return this.parent?.hasTrackableProperties() ?? false } getTypenameTypeDecorator(propertyName: string): string | undefined { @@ -595,7 +598,18 @@ export class ClassMetadata { return this.typedProperties?.get(propertyName) } if (this.parent) { - return this.parent!.typedProperties?.get(propertyName) + return this.parent!.getTypenameTypeDecorator(propertyName) + } + return undefined + } + + static getParent(clazz: any): ClassMetadata | undefined { + let prototype = Object.getPrototypeOf(clazz) + while (prototype) { + if (prototype.hasOwnProperty("__classMetadata") && prototype.__classMetadata !== undefined) { + return prototype.__classMetadata + } + prototype = Object.getPrototypeOf(prototype) } return undefined } diff --git a/incremental/runtime/src/index.ts b/incremental/runtime/src/index.ts index 7dc931d49..de67a0cc4 100644 --- a/incremental/runtime/src/index.ts +++ b/incremental/runtime/src/index.ts @@ -13,7 +13,7 @@ * limitations under the License. */ -export { observableProxy, ObservableClass, ObservableClassV2, ClassMetadata, TrackableProps } from "@koalaui/compat" +export { observableProxy, ObservableClass, ClassMetadata, TrackableProperties, trackableProperties } from "@koalaui/compat" export { AnimatedState, diff --git a/incremental/runtime/src/states/State.ts b/incremental/runtime/src/states/State.ts index 70abd36c7..e24f6a6f2 100644 --- a/incremental/runtime/src/states/State.ts +++ b/incremental/runtime/src/states/State.ts @@ -21,6 +21,7 @@ import { markableQueue } from "../common/MarkableQueue" import { RuntimeProfiler } from "../common/RuntimeProfiler" import { IncrementalNode } from "../tree/IncrementalNode" import { ReadonlyTreeNode } from "../tree/ReadonlyTreeNode" +import { TrackableProperties } from "@koalaui/compat"; export const CONTEXT_ROOT_SCOPE = "ohos.koala.context.root.scope" export const CONTEXT_ROOT_NODE = "ohos.koala.context.root.node" @@ -1273,15 +1274,15 @@ class TrackedScope { class TrackedScopes { private trackedScopes = new Map() - private trackedProperties: ReadonlySet | undefined + private trackableProperties: TrackableProperties | undefined /** * Set the class tracked properties * @param trackedProperties */ - setTrackedProperties(trackedProperties: ReadonlySet | undefined): void { + setTrackedProperties(trackedProperties: TrackableProperties | undefined): void { this.trackedScopes.clear() - this.trackedProperties = trackedProperties + this.trackableProperties = trackedProperties } /** @@ -1291,7 +1292,7 @@ class TrackedScopes { * Pass undefined to remove tracking. */ register(propertyName: string, dependency: ScopeToStates | undefined): void { - if (!this.trackedProperties?.has(propertyName)) { + if (!this.trackableProperties?.isTrackable(propertyName)) { return } diff --git a/ui2abc/ui-plugins/src/class-transformer.ts b/ui2abc/ui-plugins/src/class-transformer.ts index 028b3459e..9547d7942 100644 --- a/ui2abc/ui-plugins/src/class-transformer.ts +++ b/ui2abc/ui-plugins/src/class-transformer.ts @@ -53,23 +53,10 @@ export class ClassTransformer extends arkts.AbstractVisitor { return node } - addImplObservedDecorator(decoratorType: ObservedDecoratorType, result: arkts.TSClassImplements[]) { - this.importer.add('observableProxy', getRuntimePackage()) - let className: string - if (decoratorType == DecoratorNames.OBSERVED) { - className = "ObservableClass" - } else if (decoratorType == DecoratorNames.OBSERVED_V2) { - className = "ObservableClassV2" - } - this.importer.add(className!, getRuntimePackage()) - result.push(arkts.factory.createTSClassImplements(createETSTypeReference(className!))) - } - - addImplTrackDecorator(result: arkts.TSClassImplements[]) { - this.importer.add('TrackableProps', getRuntimePackage()) - result.push(arkts.factory.createTSClassImplements( - createETSTypeReference("TrackableProps")) - ) + addObservableClassImplements(result: arkts.TSClassImplements[]) { + const className = "ObservableClass" + this.importer.add(className, getRuntimePackage()) + result.push(arkts.factory.createTSClassImplements(createETSTypeReference(className))) } updateClass(clazz: arkts.ClassDeclaration, @@ -80,11 +67,9 @@ export class ClassTransformer extends arkts.AbstractVisitor { ...clazz.definition?.implements ?? [], ] if (properties.length > 0) { - if (classContext.decoratorType) { - this.addImplObservedDecorator(classContext.decoratorType, classImplements) - } - if (classContext.trackedPropertyNames.length) { - this.addImplTrackDecorator(classImplements) + if (classContext.isClassObservable()) { + this.importer.add("observableProxy", getRuntimePackage()) + this.addObservableClassImplements(classImplements) } classDef = arkts.factory.updateClassDefinition( classDef, @@ -110,19 +95,9 @@ export class ClassTransformer extends arkts.AbstractVisitor { body: readonly arkts.ClassElement[], classContext: ClassContext): arkts.ClassElement[] { const result: arkts.ClassElement[] = [] - if (classContext.isObserved()) { - createImplObservedFlagMethod(result) - } else if (classContext.isObservedV2()) { - createImplTrackablePropsMethod(classDef, "ObservableClassV2", "tracedProperties", classContext.tracedPropertyNames, result) - createImplTypedPropsMethod(classDef, classContext.typedPropertyNames, result) - } - const trackedProps = classContext.trackedPropertyNames - if (trackedProps.length > 0) { - createImplTrackablePropsMethod(classDef, "TrackableProps", "trackedProperties", trackedProps, result) + if (classContext.isClassObservable()) { + this.injectClassMetadata(classDef, classContext, result) } - - this.injectClassMetadata(classDef, classContext, result) - body.forEach(node => { if (arkts.isClassProperty(node) && classContext.needRewriteProperty(node)) { this.rewriteProperty(node, classContext, classDef.ident?.name!, result) @@ -136,8 +111,11 @@ export class ClassTransformer extends arkts.AbstractVisitor { } injectClassMetadata(classDef: arkts.ClassDefinition, classContext: ClassContext, result: arkts.ClassElement[]) { - this.importer.add("ClassMetadata", getRuntimePackage()) - const classMetadataType = createETSTypeReference("ClassMetadata") + const classMetadataName = "ClassMetadata" + const classMetadataPropName = mangle("classMetadata") + + this.importer.add(classMetadataName, getRuntimePackage()) + const classMetadataType = createETSTypeReference(classMetadataName) const metaDataCtorArgs: arkts.Expression[] = [] if (classDef.super && arkts.isETSTypeReference(classDef.super)) { @@ -190,7 +168,7 @@ export class ClassTransformer extends arkts.AbstractVisitor { result.push( arkts.factory.createClassProperty( - arkts.factory.createIdentifier(mangle("classMetadata")), + arkts.factory.createIdentifier(classMetadataPropName), arkts.factory.createETSNewClassInstanceExpression(classMetadataType, metaDataCtorArgs), arkts.factory.createETSUnionType([classMetadataType, arkts.factory.createETSUndefinedType()]), arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE @@ -199,6 +177,7 @@ export class ClassTransformer extends arkts.AbstractVisitor { false ) ) + createClassMetadataMethod(classDef, classMetadataType, classMetadataPropName, result) } rewriteProperty(property: arkts.ClassProperty, @@ -472,144 +451,24 @@ function createGetterSetter(property: arkts.ClassProperty, return getter } -function addTrackablePropsFromParent(trackedPropsIdent: string, - interfaceName: string, - methodName: string, - result: arkts.Statement[]) { - const trackableProps = arkts.factory.createIfStatement( - arkts.factory.createBinaryExpression( - arkts.factory.createThisExpression(), - createETSTypeReference(interfaceName), - arkts.Es2pandaTokenType.TOKEN_TYPE_KEYW_INSTANCEOF - ), - arkts.factory.createBlockStatement( - [ - arkts.factory.createExpressionStatement( - arkts.factory.createAssignmentExpression( - arkts.factory.createIdentifier(trackedPropsIdent), - arkts.factory.createCallExpression( - fieldOf(arkts.factory.createIdentifier(trackedPropsIdent), "concat"), - [ - arkts.factory.createCallExpression( - fieldOf(arkts.factory.createIdentifier("Array"), "from"), - [ - arkts.factory.createCallExpression( - fieldOf(arkts.factory.createSuperExpression(), methodName), - [], - undefined, - false, - false, - undefined, - ) - ], - undefined, - false, - false, - undefined, - ) - ], - undefined, - false, - false, - undefined, - ), - arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION - ) - ) - ] - ) - ) - result.push(trackableProps) -} - -function createImplTrackablePropsMethod(classDef: arkts.ClassDefinition, - interfaceName: string, - methodName: string, - trackableProps: readonly string[], - result: arkts.ClassElement[]) { - const trackedPropsIdent = mangle("trackedProps") - const body: arkts.Statement[] = [] - body.push( - arkts.factory.createVariableDeclaration( - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [ - arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier(trackedPropsIdent, createETSTypeReference("Array", ["string"])), - arkts.factory.createArrayExpression( - trackableProps.map(it => arkts.factory.createStringLiteral(it)) - ) - ) - ] - ) - ) - if (classDef.super) { - addTrackablePropsFromParent(trackedPropsIdent, interfaceName, methodName, body) - } - body.push(arkts.factory.createReturnStatement( - arkts.factory.createETSNewClassInstanceExpression( - createETSTypeReference("Set", ["string"]), - [arkts.factory.createIdentifier(trackedPropsIdent)] - ) - )) - createMethodDefinition(methodName, - arkts.factory.createBlockStatement(body), - createETSTypeReference("ReadonlySet", ["string"]), - result - ) -} - -function createImplTypedPropsMethod(classDef: arkts.ClassDefinition, - trackableProps: readonly string[], - result: arkts.ClassElement[]) { - const typedPropsIdent = mangle("typedProps") +function createClassMetadataMethod(classDef: arkts.ClassDefinition, + classMetadataType: arkts.ETSTypeReference, + classMetadataPropName: string, + result: arkts.ClassElement[]) { const body: arkts.Statement[] = [] body.push( - arkts.factory.createVariableDeclaration( - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [ - arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier(typedPropsIdent, createETSTypeReference("Array", ["string"])), - arkts.factory.createArrayExpression( - trackableProps.map(it => arkts.factory.createStringLiteral(it)) - ) - ) - ] + arkts.factory.createReturnStatement( + fieldOf(createETSTypeReference(classDef.ident?.name!), classMetadataPropName) ) ) - - body.push(arkts.factory.createReturnStatement( - arkts.factory.createETSNewClassInstanceExpression( - createETSTypeReference("Set", ["string"]), - [arkts.factory.createIdentifier(typedPropsIdent)] - ) - )) - createMethodDefinition("typedProps", + createMethodDefinition("getClassMetadata", arkts.factory.createBlockStatement(body), - createETSTypeReference("ReadonlySet", ["string"]), + arkts.factory.createETSUnionType([classMetadataType, + arkts.factory.createETSUndefinedType()]), result ) } -function createImplObservedFlagMethod(result: arkts.ClassElement[]) { - result.push( - arkts.factory.createClassProperty( - arkts.factory.createIdentifier(mangle("isCalleeCurrentClass")), - createCheckThisCurrentClass(), - arkts.factory.createETSPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_BOOLEAN), - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_READONLY, - false - ) - ) - createMethodDefinition("isObserved", - arkts.factory.createBlockStatement([arkts.factory.createReturnStatement( - fieldOf(arkts.factory.createThisExpression(), mangle("isCalleeCurrentClass")) - )]), - arkts.factory.createETSPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_BOOLEAN), - result) -} - function createMethodDefinition(methodName: string, body: arkts.AstNode, returnTypeNode: arkts.TypeNode, -- Gitee From 4d846f95f10f528aa4f5c1a4fb0f86b1553cb7d6 Mon Sep 17 00:00:00 2001 From: vadimdolgachev Date: Thu, 24 Jul 2025 17:32:00 +0700 Subject: [PATCH 4/6] Fixed tests --- incremental/compat/src/arkts/observable.ts | 24 ++++++++++--------- .../compat/src/typescript/observable.ts | 15 ++++++++++-- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/incremental/compat/src/arkts/observable.ts b/incremental/compat/src/arkts/observable.ts index 7ff0861f0..57b5560dc 100644 --- a/incremental/compat/src/arkts/observable.ts +++ b/incremental/compat/src/arkts/observable.ts @@ -1132,18 +1132,16 @@ class ObservableDate extends Date { } } +function tryGetClassMetadata(value: T): ClassMetadata | undefined { + return value instanceof ObservableClass ? value.getClassMetadata() : undefined +} + function isObservableClass(value: Object): boolean { - if (value instanceof ObservableClass) { - return value.getClassMetadata()?.isObserved() ?? false - } - return false + return tryGetClassMetadata(value)?.isObserved() ?? false } -function hasTrackableProperties(value: T): boolean { - if (value instanceof ObservableClass) { - return value.getClassMetadata()?.hasTrackableProperties() ?? false - } - return false +function hasTrackableProperties(value: Object): boolean { + return tryGetClassMetadata(value)?.hasTrackableProperties() ?? false } /** @@ -1154,14 +1152,18 @@ export interface ObservableClass { } /** - * Interface for getting the observed properties of a class + * Interface for checking the observed properties of a class */ export interface TrackableProperties { isTrackable(propertyName: string): boolean } +/** + * If value is a class, then returns a list of trackable properties + * @param value + */ export function trackableProperties(value: T): TrackableProperties | undefined { - return value instanceof ClassMetadata ? value : undefined + return tryGetClassMetadata(value) } export class ClassMetadata implements TrackableProperties { diff --git a/incremental/compat/src/typescript/observable.ts b/incremental/compat/src/typescript/observable.ts index 40275c8a2..429cbbec7 100644 --- a/incremental/compat/src/typescript/observable.ts +++ b/incremental/compat/src/typescript/observable.ts @@ -522,6 +522,13 @@ function clearObservable(data: any) { } } +function tryGetClassMetadata(value: any): ClassMetadata | undefined { + if (value !== undefined && typeof value.getClassMetadata === 'function') { + return (value as ObservableClass).getClassMetadata() + } + return undefined +} + /** * Interface for getting the observability status of a class */ @@ -530,14 +537,18 @@ export interface ObservableClass { } /** - * Interface for getting the observed properties of a class + * Interface for checking the observed properties of a class */ export interface TrackableProperties { isTrackable(propertyName: string): boolean } +/** + * If value is a class, then returns a list of trackable properties + * @param value + */ export function trackableProperties(value: T): TrackableProperties | undefined { - return value instanceof ClassMetadata ? value : undefined + return tryGetClassMetadata(value) } export class ClassMetadata implements TrackableProperties { -- Gitee From 4fc43a0ba4b7d5de1fc9a2394e1f2b157da9a5e2 Mon Sep 17 00:00:00 2001 From: vadimdolgachev Date: Thu, 24 Jul 2025 20:45:00 +0700 Subject: [PATCH 5/6] Fix determining of ObservedV1 --- .../arkui/src/ArkStateProperties.ets | 2 +- incremental/compat/src/arkts/observable.ts | 32 ++++++---- .../compat/src/typescript/observable.ts | 16 ++--- ui2abc/ui-plugins/src/class-transformer.ts | 59 +++++++++---------- 4 files changed, 56 insertions(+), 53 deletions(-) diff --git a/arkoala-arkts/arkui/src/ArkStateProperties.ets b/arkoala-arkts/arkui/src/ArkStateProperties.ets index f91e2e22b..5f6ea73ad 100644 --- a/arkoala-arkts/arkui/src/ArkStateProperties.ets +++ b/arkoala-arkts/arkui/src/ArkStateProperties.ets @@ -60,7 +60,7 @@ class StatableHolder { private isStatable(value: Value | undefined): boolean { if (value instanceof ObservableClass) { - return value.getClassMetadata()?.isObservedV2() ?? false + return value.getClassMetadata()?.isObservedV2(value) ?? false } return false } diff --git a/incremental/compat/src/arkts/observable.ts b/incremental/compat/src/arkts/observable.ts index 57b5560dc..76dd36444 100644 --- a/incremental/compat/src/arkts/observable.ts +++ b/incremental/compat/src/arkts/observable.ts @@ -232,7 +232,7 @@ export function observableProxy(value: Value, parent?: ObservableHandler, const valueType = Type.of(value) if (valueType instanceof ClassType && !(value instanceof BaseEnum)) { - const isObservable = isObservableClass(value as Object) + const isObservable = isObservedV1Class(value as Object) if (!hasTrackableProperties(value as Object) && !isObservable) { return value as Value } @@ -1136,11 +1136,11 @@ function tryGetClassMetadata(value: T): ClassMetadata | undefined { return value instanceof ObservableClass ? value.getClassMetadata() : undefined } -function isObservableClass(value: Object): boolean { - return tryGetClassMetadata(value)?.isObserved() ?? false +function isObservedV1Class(value: Object): boolean { + return tryGetClassMetadata(value)?.isObservedV1(value) ?? false } -function hasTrackableProperties(value: Object): boolean { +function hasTrackableProperties(value: Object): boolean { return tryGetClassMetadata(value)?.hasTrackableProperties() ?? false } @@ -1162,14 +1162,15 @@ export interface TrackableProperties { * If value is a class, then returns a list of trackable properties * @param value */ -export function trackableProperties(value: T): TrackableProperties | undefined { +export function trackableProperties(value: T): TrackableProperties | undefined { return tryGetClassMetadata(value) } export class ClassMetadata implements TrackableProperties { private readonly parent: ClassMetadata | undefined - private readonly markAsObserved: boolean + private readonly markAsObservedV1: boolean private readonly markAsObservedV2: boolean + private readonly targetClass: Class /** * Class property names marked with the @Track or @Trace decorator @@ -1185,12 +1186,17 @@ export class ClassMetadata implements TrackableProperties { private readonly typedProperties: ReadonlyMap | undefined constructor(parent: ClassMetadata | undefined, - markAsObserved: boolean, + markAsObservedV1: boolean, markAsObservedV2: boolean, trackable: string[] | undefined, typed: [string, string][] | undefined) { + const target = Class.ofCaller() + if (target == undefined) { + throw new Error("ClassMetadata must be created in the class context") + } + this.targetClass = target! this.parent = parent - this.markAsObserved = markAsObserved + this.markAsObservedV1 = markAsObservedV1 this.markAsObservedV2 = markAsObservedV2 if (trackable) { this.trackableProperties = new Set(trackable) @@ -1200,12 +1206,12 @@ export class ClassMetadata implements TrackableProperties { } } - isObserved(): boolean { - return this.markAsObserved + isObservedV1(value: Object): boolean { + return this.markAsObservedV1 && Class.of(value) == this.targetClass } - isObservedV2(): boolean { - return this.markAsObservedV2 + isObservedV2(value: Object): boolean { + return this.markAsObservedV2 && Class.of(value) == this.targetClass } isTrackable(propertyName: string): boolean { @@ -1229,7 +1235,7 @@ export class ClassMetadata implements TrackableProperties { return undefined } - static getParent(type: Object): ClassMetadata | undefined { + static findClassMetadata(type: Type): ClassMetadata | undefined { if (type instanceof ClassType) { const fieldsNum = type.getFieldsNum() for (let i = 0; i < fieldsNum; i++) { diff --git a/incremental/compat/src/typescript/observable.ts b/incremental/compat/src/typescript/observable.ts index 429cbbec7..6e3c8bfa5 100644 --- a/incremental/compat/src/typescript/observable.ts +++ b/incremental/compat/src/typescript/observable.ts @@ -553,7 +553,7 @@ export function trackableProperties(value: T): TrackableProperties | undefine export class ClassMetadata implements TrackableProperties { private readonly parent: ClassMetadata | undefined - private readonly markAsObserved: boolean + private readonly markAsObservedV1: boolean private readonly markAsObservedV2: boolean /** @@ -570,12 +570,12 @@ export class ClassMetadata implements TrackableProperties { private readonly typedProperties: ReadonlyMap | undefined constructor(parent: ClassMetadata | undefined, - markAsObserved: boolean, + markAsObservedV1: boolean, markAsObservedV2: boolean, trackable: string[] | undefined, typed: [string, string][] | undefined) { this.parent = parent - this.markAsObserved = markAsObserved + this.markAsObservedV1 = markAsObservedV1 this.markAsObservedV2 = markAsObservedV2 if (trackable) { this.trackableProperties = new Set(trackable) @@ -585,11 +585,11 @@ export class ClassMetadata implements TrackableProperties { } } - isObserved(): boolean { - return this.markAsObserved + isObservedV1(value: Object): boolean { + return this.markAsObservedV1 } - isObservedV2(): boolean { + isObservedV2(value: Object): boolean { return this.markAsObservedV2 } @@ -614,8 +614,8 @@ export class ClassMetadata implements TrackableProperties { return undefined } - static getParent(clazz: any): ClassMetadata | undefined { - let prototype = Object.getPrototypeOf(clazz) + private static findClassMetadata(type: any): ClassMetadata | undefined { + let prototype = Object.getPrototypeOf(type) while (prototype) { if (prototype.hasOwnProperty("__classMetadata") && prototype.__classMetadata !== undefined) { return prototype.__classMetadata diff --git a/ui2abc/ui-plugins/src/class-transformer.ts b/ui2abc/ui-plugins/src/class-transformer.ts index 9547d7942..80bd35d4c 100644 --- a/ui2abc/ui-plugins/src/class-transformer.ts +++ b/ui2abc/ui-plugins/src/class-transformer.ts @@ -116,28 +116,6 @@ export class ClassTransformer extends arkts.AbstractVisitor { this.importer.add(classMetadataName, getRuntimePackage()) const classMetadataType = createETSTypeReference(classMetadataName) - - const metaDataCtorArgs: arkts.Expression[] = [] - if (classDef.super && arkts.isETSTypeReference(classDef.super)) { - metaDataCtorArgs.push(arkts.factory.createCallExpression( - fieldOf(classMetadataType, "getParent"), - [ - arkts.factory.createCallExpression( - fieldOf(arkts.factory.createIdentifier("Type"), "from"), - [], - arkts.factory.createTSTypeParameterInstantiation([createETSTypeReference(classDef.super.baseName?.name!)]), - false, - false, - ) - ], - undefined, - false, - false - )) - } else { - metaDataCtorArgs.push(arkts.factory.createUndefinedLiteral()) - } - let trackableProps: string[] | undefined if (classContext.trackedPropertyNames.length > 0) { trackableProps = classContext.trackedPropertyNames @@ -152,9 +130,9 @@ export class ClassTransformer extends arkts.AbstractVisitor { typedProps.push([prop[0], prop[1][0].args[0]]) } } - - metaDataCtorArgs.push( - arkts.factory.createBooleanLiteral(classContext.isObserved()), + const metadataCtorArgs: arkts.Expression[] = [ + this.findClassMetadata(classMetadataType, classDef.super), + arkts.factory.createBooleanLiteral(classContext.isObservedV1()), arkts.factory.createBooleanLiteral(classContext.isObservedV2()), trackableProps ? arkts.factory.createArrayExpression(trackableProps.map(it => arkts.factory.createStringLiteral(it))) @@ -164,12 +142,11 @@ export class ClassTransformer extends arkts.AbstractVisitor { arkts.factory.createArrayExpression([arkts.factory.createStringLiteral(it[0]), arkts.factory.createStringLiteral(it[1])]))) : arkts.factory.createUndefinedLiteral() - ) - + ] result.push( arkts.factory.createClassProperty( arkts.factory.createIdentifier(classMetadataPropName), - arkts.factory.createETSNewClassInstanceExpression(classMetadataType, metaDataCtorArgs), + arkts.factory.createETSNewClassInstanceExpression(classMetadataType, metadataCtorArgs), arkts.factory.createETSUnionType([classMetadataType, arkts.factory.createETSUndefinedType()]), arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE | arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_READONLY @@ -180,6 +157,26 @@ export class ClassTransformer extends arkts.AbstractVisitor { createClassMetadataMethod(classDef, classMetadataType, classMetadataPropName, result) } + findClassMetadata(classMetadataType: arkts.ETSTypeReference, type: arkts.Expression | undefined): arkts.Expression { + return arkts.isETSTypeReference(type) + ? arkts.factory.createCallExpression( + fieldOf(classMetadataType, "findClassMetadata"), + [ + arkts.factory.createCallExpression( + fieldOf(arkts.factory.createIdentifier("Type"), "from"), + [], + arkts.factory.createTSTypeParameterInstantiation([createETSTypeReference(type.baseName?.name!)]), + false, + false, + ) + ], + undefined, + false, + false + ) + : arkts.factory.createUndefinedLiteral() + } + rewriteProperty(property: arkts.ClassProperty, classContext: ClassContext, className: string, @@ -499,7 +496,7 @@ function createMethodDefinition(methodName: string, function observePropIfNeeded(propertyName: string, propertyValue: arkts.Expression | undefined, classContext: ClassContext) { - const isClassFullyObserved = classContext.isObserved() && classContext.trackedPropertyNames.length == 0 + const isClassFullyObserved = classContext.isObservedV1() && classContext.trackedPropertyNames.length == 0 const isTrackedProp = classContext.tracedPropertyNames.includes(propertyName) const isTracedProp = classContext.trackedPropertyNames.includes(propertyName) if (propertyValue && (isClassFullyObserved || isTrackedProp || isTracedProp)) { @@ -619,7 +616,7 @@ class ClassContext { } } - isObserved(): boolean { + isObservedV1(): boolean { return this.decoratorType == DecoratorNames.OBSERVED } @@ -632,7 +629,7 @@ class ClassContext { } needRewriteProperty(property: arkts.ClassProperty): boolean { - if (this.isObserved() || this.trackedPropertyNames.length) { + if (this.isObservedV1() || this.trackedPropertyNames.length) { return !isStaticProperty(property) } return this.isObservedV2() && this.tracedPropertyNames.includes(property.id?.name!) -- Gitee From 61fe2674895055bcb84d09450da65558bdb45ed3 Mon Sep 17 00:00:00 2001 From: vadimdolgachev Date: Tue, 29 Jul 2025 10:53:21 +0700 Subject: [PATCH 6/6] Merge fix --- arkoala-arkts/arkui/src/ArkUIEntry.ets | 5 -- .../pages/observedv2/TypeDecorator.ets | 60 +++++++++++++++++++ ets-tests/ets/suites/TraceDecoratorTests.ets | 0 incremental/compat/src/arkts/observable.ts | 11 ++-- .../compat/src/typescript/observable.ts | 7 ++- ui2abc/ui-plugins/src/class-transformer.ts | 58 ++++-------------- .../ui-plugins/src/property-transformers.ts | 4 +- 7 files changed, 83 insertions(+), 62 deletions(-) delete mode 100644 ets-tests/ets/suites/TraceDecoratorTests.ets diff --git a/arkoala-arkts/arkui/src/ArkUIEntry.ets b/arkoala-arkts/arkui/src/ArkUIEntry.ets index e35ef8154..9015daf17 100644 --- a/arkoala-arkts/arkui/src/ArkUIEntry.ets +++ b/arkoala-arkts/arkui/src/ArkUIEntry.ets @@ -392,7 +392,6 @@ export class Application implements UserApplicationControl { } } } - this.aboutToExit() return this.currentCrash ? nullptr : root!.peer.ptr } @@ -700,10 +699,6 @@ export class Application implements UserApplicationControl { registerNativeModuleLibraryName("ArkUIGeneratedNativeModule", "ArkoalaNative_ark.z") registerNativeModuleLibraryName("TestNativeModule", "ArkoalaNative_ark.z") } - - protected aboutToExit(): void { - this.manager?.reset() - } } function makeClickEvent(x: number, y: number): ClickEvent { diff --git a/ets-tests/ets/ets-tests/pages/observedv2/TypeDecorator.ets b/ets-tests/ets/ets-tests/pages/observedv2/TypeDecorator.ets index e69de29bb..18b1bd33b 100644 --- a/ets-tests/ets/ets-tests/pages/observedv2/TypeDecorator.ets +++ b/ets-tests/ets/ets-tests/pages/observedv2/TypeDecorator.ets @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TestComponent, uiLog } from '@ohos.arkui' + +@ObservedV2 +class BaseSample { + @Trace + baseField1: int | undefined = 0 + @Trace + baseField2: int | undefined = 0 +} + +@ObservedV2 +class Sample extends BaseSample { + @Trace + data: int | undefined = 0 +} + +@ObservedV2 +class Foo { + +} + +@ObservedV2 +class Info { + @Type(Sample) + @Trace + sample: Sample = new Sample() + + @Type(Foo) + @Trace + foo: Foo = new Foo() +} + +@Entry +@Component +struct TypeDecorator { + info: Info = new Info() + build() { + TestComponent({ id: 100 }).onChange(() => { + this.info.sample.data!++ + }) + TestComponent({}) { + uiLog(`sample.data: ${this.info.sample!.data}`) + } + } +} \ No newline at end of file diff --git a/ets-tests/ets/suites/TraceDecoratorTests.ets b/ets-tests/ets/suites/TraceDecoratorTests.ets deleted file mode 100644 index e69de29bb..000000000 diff --git a/incremental/compat/src/arkts/observable.ts b/incremental/compat/src/arkts/observable.ts index 76dd36444..33d25ead5 100644 --- a/incremental/compat/src/arkts/observable.ts +++ b/incremental/compat/src/arkts/observable.ts @@ -1132,16 +1132,16 @@ class ObservableDate extends Date { } } -function tryGetClassMetadata(value: T): ClassMetadata | undefined { +function getClassMetadata(value: T): ClassMetadata | undefined { return value instanceof ObservableClass ? value.getClassMetadata() : undefined } function isObservedV1Class(value: Object): boolean { - return tryGetClassMetadata(value)?.isObservedV1(value) ?? false + return getClassMetadata(value)?.isObservedV1(value) ?? false } function hasTrackableProperties(value: Object): boolean { - return tryGetClassMetadata(value)?.hasTrackableProperties() ?? false + return getClassMetadata(value)?.hasTrackableProperties() ?? false } /** @@ -1163,7 +1163,7 @@ export interface TrackableProperties { * @param value */ export function trackableProperties(value: T): TrackableProperties | undefined { - return tryGetClassMetadata(value) + return getClassMetadata(value) } export class ClassMetadata implements TrackableProperties { @@ -1171,6 +1171,7 @@ export class ClassMetadata implements TrackableProperties { private readonly markAsObservedV1: boolean private readonly markAsObservedV2: boolean private readonly targetClass: Class + private static readonly metadataPropName = "__classMetadata" /** * Class property names marked with the @Track or @Trace decorator @@ -1240,7 +1241,7 @@ export class ClassMetadata implements TrackableProperties { const fieldsNum = type.getFieldsNum() for (let i = 0; i < fieldsNum; i++) { const field = type.getField(i) - if (field.isStatic() && field.getName() == "__classMetadata") { + if (field.isStatic() && field.getName() == ClassMetadata.metadataPropName) { const meta = field.getStaticValue() if (meta != undefined && meta instanceof ClassMetadata) { return meta diff --git a/incremental/compat/src/typescript/observable.ts b/incremental/compat/src/typescript/observable.ts index 6e3c8bfa5..31d9f74cd 100644 --- a/incremental/compat/src/typescript/observable.ts +++ b/incremental/compat/src/typescript/observable.ts @@ -522,7 +522,7 @@ function clearObservable(data: any) { } } -function tryGetClassMetadata(value: any): ClassMetadata | undefined { +function getClassMetadata(value: any): ClassMetadata | undefined { if (value !== undefined && typeof value.getClassMetadata === 'function') { return (value as ObservableClass).getClassMetadata() } @@ -548,13 +548,14 @@ export interface TrackableProperties { * @param value */ export function trackableProperties(value: T): TrackableProperties | undefined { - return tryGetClassMetadata(value) + return getClassMetadata(value) } export class ClassMetadata implements TrackableProperties { private readonly parent: ClassMetadata | undefined private readonly markAsObservedV1: boolean private readonly markAsObservedV2: boolean + private static readonly metadataPropName = "__classMetadata" /** * Class property names marked with the @Track or @Trace decorator @@ -617,7 +618,7 @@ export class ClassMetadata implements TrackableProperties { private static findClassMetadata(type: any): ClassMetadata | undefined { let prototype = Object.getPrototypeOf(type) while (prototype) { - if (prototype.hasOwnProperty("__classMetadata") && prototype.__classMetadata !== undefined) { + if (prototype.hasOwnProperty(ClassMetadata.metadataPropName) && prototype.__classMetadata !== undefined) { return prototype.__classMetadata } prototype = Object.getPrototypeOf(prototype) diff --git a/ui2abc/ui-plugins/src/class-transformer.ts b/ui2abc/ui-plugins/src/class-transformer.ts index 80bd35d4c..f55666746 100644 --- a/ui2abc/ui-plugins/src/class-transformer.ts +++ b/ui2abc/ui-plugins/src/class-transformer.ts @@ -14,17 +14,19 @@ */ import * as arkts from "@koalaui/libarkts" +import { Es2pandaModifierFlags } from "@koalaui/libarkts" import { classProperties } from "./common/arkts-utils" import { createETSTypeReference, + DecoratorNames, getComponentPackage, getDecoratorName, getRuntimePackage, Importer, + isDecoratorAnnotation, mangle } from "./utils" -import { DecoratorNames, hasDecorator, isDecoratorAnnotation } from "./utils"; import { backingFieldNameOf, fieldOf } from "./property-transformers"; type ObservedDecoratorType = DecoratorNames.OBSERVED | DecoratorNames.OBSERVED_V2 @@ -282,26 +284,10 @@ export class ClassTransformer extends arkts.AbstractVisitor { } } -function propertyVerifier(property: arkts.ClassProperty, - disallowedModifiers?: arkts.Es2pandaModifierFlags[]): boolean { - if (disallowedModifiers && disallowedModifiers.some(it => arkts.hasModifierFlag(property, it))) { - return false - } - return arkts.hasModifierFlag(property, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC) -} - -function trackedPropVerifier(property: arkts.ClassProperty): boolean { - return hasDecorator(property, DecoratorNames.TRACK) - && propertyVerifier(property, [arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC]) -} - -function tracedPropVerifier(property: arkts.ClassProperty): boolean { - return hasDecorator(property, DecoratorNames.TRACE) && propertyVerifier(property) -} - -function typedPropVerifier(property: arkts.ClassProperty): boolean { - return hasDecorator(property, DecoratorNames.TYPE) - && propertyVerifier(property, [arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC]) +function propertyVerifier(property: arkts.ClassProperty): boolean { + return ![arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, + Es2pandaModifierFlags.MODIFIER_FLAGS_PROTECTED] + .some(it => arkts.hasModifierFlag(property, it)); } function getDecoratorArgs(decorator: arkts.AnnotationUsage): string[] { @@ -314,13 +300,13 @@ function getDecoratorArgs(decorator: arkts.AnnotationUsage): string[] { return args } -function getPropertyDecorators(properties: readonly arkts.ClassProperty[]): ReadonlyMap { +function getDecoratedProperties(properties: readonly arkts.ClassProperty[]): ReadonlyMap { const propertyInfo = new Map() for (const prop of properties) { - if (prop.id?.name == undefined) { + if (!propertyVerifier(prop)) { continue } - const propName = prop.id.name + const propName = prop.id!.name! const decorators = propertyInfo.get(propName) ?? [] for (const anno of prop.annotations) { @@ -340,7 +326,7 @@ function getPropertyDecorators(properties: readonly arkts.ClassProperty[]): Read function extractClassContext(clazz: arkts.ClassDeclaration, properties: readonly arkts.ClassProperty[]): ClassContext | undefined { if (clazz.definition != undefined) { - return new ClassContext(getObservedDecoratorType(clazz.definition), getPropertyDecorators(properties)) + return new ClassContext(getObservedDecoratorType(clazz.definition), getDecoratedProperties(properties)) } return undefined } @@ -523,28 +509,6 @@ function getObservedDecoratorType(definition: arkts.ClassDefinition): ObservedDe return undefined } -function createCheckThisCurrentClass(): arkts.Expression { - return arkts.factory.createBinaryExpression( - arkts.factory.createCallExpression( - fieldOf(arkts.factory.createIdentifier("Class"), "of"), - [arkts.factory.createThisExpression()], - undefined, - false, - false, - undefined, - ), - arkts.factory.createCallExpression( - fieldOf(arkts.factory.createIdentifier("Class"), "current"), - [], - undefined, - false, - false, - undefined, - ), - arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_EQUAL - ) -} - function isStaticProperty(property: arkts.ClassProperty): boolean { return arkts.hasModifierFlag(property, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_STATIC) } diff --git a/ui2abc/ui-plugins/src/property-transformers.ts b/ui2abc/ui-plugins/src/property-transformers.ts index 816f4e444..5a31c05c2 100644 --- a/ui2abc/ui-plugins/src/property-transformers.ts +++ b/ui2abc/ui-plugins/src/property-transformers.ts @@ -514,10 +514,10 @@ export class ObjectLinkTransformer extends PropertyTransformerBase { } } -export function fieldOf(base: arkts.Expression, name: string | arkts.Identifier, optional: boolean = false): arkts.Expression { +export function fieldOf(base: arkts.Expression, name: string, optional: boolean = false): arkts.Expression { const result = arkts.factory.createMemberExpression( base, - typeof name == "string" ? arkts.factory.createIdentifier(name) : name, + arkts.factory.createIdentifier(name), arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, false, optional -- Gitee