• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import * as arkts from '@koalaui/libarkts';
17
18import {
19    createGetter,
20    DecoratorNames,
21    generateGetOrSetCall,
22    generateThisBacking,
23    generateToRecord,
24    hasDecorator,
25    judgeIfAddWatchFunc,
26} from './utils';
27import { PropertyTranslator } from './base';
28import { GetterSetter, InitializerConstructor } from './types';
29import { backingField, expectName } from '../../common/arkts-utils';
30import { factory } from './factory';
31import { createOptionalClassProperty } from '../utils';
32
33export class ObjectLinkTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter {
34    translateMember(): arkts.AstNode[] {
35        const originalName: string = expectName(this.property.key);
36        const newName: string = backingField(originalName);
37        if (!this.ifObservedDecoratedClass()) {
38            throw new Error('@ObjectLink decorated property only accepts @Observed decorated class instance'); // TODO: replace this with proper error message.
39        }
40
41        this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point...
42        return this.translateWithoutInitializer(newName, originalName);
43    }
44
45    cacheTranslatedInitializer(newName: string, originalName: string): void {
46        const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName);
47        const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName);
48        const updateStruct: arkts.AstNode = this.generateUpdateStruct(newName, originalName);
49        currentStructInfo.initializeBody.push(initializeStruct);
50        currentStructInfo.updateBody.push(updateStruct);
51
52        if (currentStructInfo.isReusable) {
53            const toRecord = generateToRecord(newName, originalName);
54            currentStructInfo.toRecordBody.push(toRecord);
55        }
56
57        arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo);
58    }
59
60    generateInitializeStruct(newName: string, originalName: string): arkts.AstNode {
61        const initializers = arkts.factory.createTSNonNullExpression(
62            factory.createBlockStatementForOptionalExpression(
63                arkts.factory.createIdentifier('initializers'),
64                originalName
65            )
66        );
67
68        const args: arkts.Expression[] = [arkts.factory.create1StringLiteral(originalName), initializers];
69        judgeIfAddWatchFunc(args, this.property);
70
71        const newClass = arkts.factory.createETSNewClassInstanceExpression(
72            arkts.factory.createTypeReference(
73                arkts.factory.createTypeReferencePart(
74                    arkts.factory.createIdentifier('ObjectLinkDecoratedVariable'),
75                    arkts.factory.createTSTypeParameterInstantiation(
76                        this.property.typeAnnotation ? [this.property.typeAnnotation] : []
77                    )
78                )
79            ),
80            args
81        );
82
83        return arkts.factory.createAssignmentExpression(
84            generateThisBacking(newName),
85            arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION,
86            newClass
87        );
88    }
89
90    generateUpdateStruct(newName: string, originalName: string): arkts.AstNode {
91        const binaryItem = arkts.factory.createBinaryExpression(
92            factory.createBlockStatementForOptionalExpression(
93                arkts.factory.createIdentifier('initializers'),
94                originalName
95            ),
96            arkts.factory.createUndefinedLiteral(),
97            arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_NOT_STRICT_EQUAL
98        );
99        const member: arkts.MemberExpression = arkts.factory.createMemberExpression(
100            generateThisBacking(newName, false, true),
101            arkts.factory.createIdentifier('update'),
102            arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS,
103            false,
104            false
105        );
106        const nonNullItem = arkts.factory.createTSNonNullExpression(
107            factory.createNonNullOrOptionalMemberExpression('initializers', originalName, false, true)
108        );
109        return arkts.factory.createIfStatement(
110            binaryItem,
111            arkts.factory.createBlock([
112                arkts.factory.createExpressionStatement(
113                    arkts.factory.createCallExpression(member, undefined, [nonNullItem])
114                ),
115            ])
116        );
117    }
118
119    translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] {
120        const field: arkts.ClassProperty = createOptionalClassProperty(
121            newName,
122            this.property,
123            'ObjectLinkDecoratedVariable',
124            arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE
125        );
126        const thisValue: arkts.Expression = generateThisBacking(newName, false, true);
127        const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, 'get');
128        const getter: arkts.MethodDefinition = this.translateGetter(
129            originalName,
130            this.property.typeAnnotation,
131            thisGet
132        );
133        return [field, getter];
134    }
135
136    translateGetter(
137        originalName: string,
138        typeAnnotation: arkts.TypeNode | undefined,
139        returnValue: arkts.Expression
140    ): arkts.MethodDefinition {
141        return createGetter(originalName, typeAnnotation, returnValue);
142    }
143
144    ifObservedDecoratedClass(): boolean {
145        if (this.property.typeAnnotation && arkts.isETSTypeReference(this.property.typeAnnotation)) {
146            const decl = arkts.getDecl(this.property.typeAnnotation.part?.name!);
147            if (arkts.isClassDefinition(decl!) && hasDecorator(decl, DecoratorNames.OBSERVED)) {
148                return true;
149            }
150        }
151        return false;
152    }
153}
154