idlize

Форк
0
/
StyleTransformer.ts 
253 строки · 10.3 Кб
1
/*
2
 * Copyright (c) 2022-2023 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

16
import * as ts from 'ohos-typescript'
17
import { AbstractVisitor } from './AbstractVisitor'
18
import { ExtensionStylesTransformer } from './ExtensionStylesTransformer'
19
import { Importer } from './Importer'
20
import { bindThis, createThisFieldAccess, getDeclarationsByNode, id, isUndefined, undefinedValue } from './ApiUtils'
21
import { CallTable, extendsLikeFunctionName, isBuilderLambdaCall, RewriteNames } from './utils'
22

23

24
enum ExtensionStyleRewrite {
25
    Regular,
26
    StylesFunction,
27
    ExtendFunction,
28
    AnimatableExtendFunction,
29
    StylesMethod,
30
    ExtendMethod,
31
    AnimatableExtendMethod
32
}
33

34
export class StyleTransformer extends AbstractVisitor {
35
    constructor(
36
        sourceFile: ts.SourceFile,
37
        ctx: ts.TransformationContext,
38
        private typechecker: ts.TypeChecker,
39
        public importer: Importer,
40
        private styleFunctionTransformer: ExtensionStylesTransformer,
41
        private callTable: CallTable
42
    ) {
43
        super(sourceFile, ctx)
44
    }
45

46
    private isStylesFunction(name: string): boolean {
47
        return this.styleFunctionTransformer.stylesFunctions.includes(name)
48
    }
49

50
    private isExtendFunction(name: string): boolean {
51
        return this.styleFunctionTransformer.extendFunctions.includes(name)
52
    }
53

54
    private isAnimatableExtendFunction(name: string): boolean {
55
        return this.styleFunctionTransformer.animatableExtendFunctions.includes(name)
56
    }
57

58
    private isStylesMethod(name: ts.MethodDeclaration): boolean {
59
        return this.styleFunctionTransformer.stylesMethods.includes(name)
60
    }
61

62
    private isExtendMethod(name: ts.MethodDeclaration): boolean {
63
        return this.styleFunctionTransformer.extendMethods.includes(name)
64
    }
65

66
    private isAnimatableExtendMethod(name: ts.MethodDeclaration): boolean {
67
        return this.styleFunctionTransformer.animatableExtendMethods.includes(name)
68
    }
69

70
    // Do we need classes here, like we have for property initializers?
71
    private classifyExtensionStyleRewrite(name: ts.Identifier, nameExtension: ts.Identifier): ExtensionStyleRewrite {
72
        const nameString = ts.idText(name)
73
        const nameOfExtendsFunction = extendsLikeFunctionName(nameString, ts.idText(nameExtension))
74
        if (this.isStylesFunction(nameString)) return ExtensionStyleRewrite.StylesFunction
75
        if (this.isExtendFunction(nameOfExtendsFunction)) return ExtensionStyleRewrite.ExtendFunction
76
        if (this.isAnimatableExtendFunction(nameOfExtendsFunction)) return ExtensionStyleRewrite.AnimatableExtendFunction
77

78
        const declaration = getDeclarationsByNode(this.typechecker, name)[0]
79
        if (!declaration) return ExtensionStyleRewrite.Regular
80
        if (!ts.isMethodDeclaration(declaration)) return ExtensionStyleRewrite.Regular
81
        const originalDeclaration = ts.getOriginalNode(declaration) as ts.MethodDeclaration
82

83
        if (this.isStylesMethod(originalDeclaration)) return ExtensionStyleRewrite.StylesMethod
84
        if (this.isExtendMethod(originalDeclaration)) return ExtensionStyleRewrite.ExtendMethod
85
        if (this.isAnimatableExtendMethod(originalDeclaration)) return ExtensionStyleRewrite.AnimatableExtendMethod
86

87
        return ExtensionStyleRewrite.Regular
88
    }
89

90
    transformEtsComponent(
91
        node: ts.CallExpression,
92
        dot: ts.PropertyAccessExpression,
93
        originalNode: ts.EtsComponentExpression | ts.CallExpression,
94
        receiverName: ts.Identifier,
95
        args: ts.NodeArray<ts.Expression>,
96
        isEts: boolean
97
    ): ts.EtsComponentExpression | ts.CallExpression {
98
        const firstEtsArg: ts.Expression = args[0]
99
        const firstEtsArgOrUndefined = isUndefined(firstEtsArg) ? undefined : firstEtsArg
100
        const restEtsArgs = args.slice(1)
101

102
        const rewrite = this.classifyExtensionStyleRewrite(dot.name as ts.Identifier, receiverName)
103

104
        const propertyName = dot.name
105

106
        const maybeSyntheticName = this.maybeSyntheticFunctionName(rewrite, propertyName, receiverName)
107
        const styleApplicatorOrOriginalPropertyName = this.maybeStyleApplicator(rewrite, propertyName)
108

109
        const newDot = ts.factory.updatePropertyAccessExpression(
110
            dot,
111
            firstEtsArgOrUndefined ?? id("instance"),
112
            styleApplicatorOrOriginalPropertyName
113
        )
114

115
        const detachedStyle = ts.factory.updateCallExpression(
116
            node,
117
            newDot,
118
            undefined,
119
            this.maybePrependStyleFunctionArgument(rewrite, maybeSyntheticName, node.arguments)
120
        )
121

122
        if (isEts) {
123
            const ets = originalNode as ts.EtsComponentExpression
124
            return ts.factory.updateEtsComponentExpression(
125
                ets,
126
                receiverName,
127
                [detachedStyle, ...restEtsArgs],
128
                ets.body
129
            )
130
        }
131

132
        const call = originalNode as ts.CallExpression
133
        return ts.factory.updateCallExpression(
134
            call,
135
            receiverName,
136
            undefined,
137
            [detachedStyle, ...restEtsArgs],
138
        )
139
    }
140

141
    private maybeStyleApplicator(rewrite: ExtensionStyleRewrite, propertyName: ts.MemberName): ts.MemberName {
142
        switch (rewrite) {
143
            case ExtensionStyleRewrite.StylesFunction:
144
            case ExtensionStyleRewrite.ExtendFunction:
145
            case ExtensionStyleRewrite.StylesMethod:
146
                return id(RewriteNames.ApplyStyle)
147
            case ExtensionStyleRewrite.AnimatableExtendFunction:
148
                return id(RewriteNames.ApplyAnimatableExtend)
149
            case ExtensionStyleRewrite.ExtendMethod:
150
            case ExtensionStyleRewrite.AnimatableExtendMethod:
151
                console.log("TODO: need an implementatyion of @Extend applied to methods")
152
                return propertyName
153
            default:
154
                return propertyName
155
        }
156
    }
157

158
    private maybeSyntheticFunctionName(rewrite: ExtensionStyleRewrite, propertyName: ts.MemberName, componentName: ts.Identifier): ts.Identifier {
159
        switch (rewrite) {
160
            case ExtensionStyleRewrite.ExtendFunction:
161
            case ExtensionStyleRewrite.AnimatableExtendFunction:
162
            case ExtensionStyleRewrite.ExtendMethod:
163
            case ExtensionStyleRewrite.AnimatableExtendMethod:
164
                const propertyNameString = ts.idText(propertyName)
165
                const componentNameString = ts.idText(componentName)
166
                return id(extendsLikeFunctionName(propertyNameString, componentNameString))
167
            default:
168
                return propertyName as ts.Identifier
169
        }
170
    }
171

172
    private maybePrependStyleFunctionArgument(rewrite: ExtensionStyleRewrite, maybeSyntheticName: ts.MemberName, nodeArguments: ts.NodeArray<ts.Expression>): readonly ts.Expression[] {
173
        switch (rewrite) {
174
            case ExtensionStyleRewrite.StylesFunction:
175
            case ExtensionStyleRewrite.ExtendFunction:
176
            case ExtensionStyleRewrite.AnimatableExtendFunction:
177
                return [maybeSyntheticName, ...nodeArguments]
178
            case ExtensionStyleRewrite.StylesMethod:
179
            case ExtensionStyleRewrite.ExtendMethod:
180
            case ExtensionStyleRewrite.AnimatableExtendMethod:
181
                return [bindThis(createThisFieldAccess(maybeSyntheticName)), ...nodeArguments]
182
            default:
183
                return nodeArguments
184
        }
185
    }
186

187
    // Assumption: Ets nodes were preprocessed to have "undefined"
188
    // as their first argument.
189
    //
190
    // We need to rewrite
191
    //   ets(undefined, some, args).foo().bar().qux()
192
    //   ets(instance.foo().bar().qux(), some, args)
193
    // No style ets is left as is:
194
    //   ets(some, args)
195
    // The trick here is that ets() is the deepest in the PropertyAccessExpression tree.
196
    visitor(beforeChildren: ts.Node): ts.Node {
197
        const node = this.visitEachChild(beforeChildren)
198

199
        // Recognize ets().foo()
200
        if (ts.isCallExpression(node) &&
201
            ts.isPropertyAccessExpression(node.expression) &&
202
            ts.isEtsComponentExpression(node.expression.expression)) {
203
            const dot = node.expression
204
            const ets = node.expression.expression
205
            return this.transformEtsComponent(node, dot, ets, ets.expression as ts.Identifier, ets.arguments, true)
206
        }
207
        // Recognize ets().foo()
208
        if (ts.isCallExpression(node) &&
209
            ts.isPropertyAccessExpression(node.expression) &&
210
            ts.isCallExpression(node.expression.expression) &&
211
            isBuilderLambdaCall(this.callTable, node.expression.expression)) {
212
            const dot = node.expression
213
            const call = node.expression.expression
214
            return this.transformEtsComponent(node, dot, call, call.expression as ts.Identifier, call.arguments, false)
215
        }
216

217
        return node
218
    }
219
}
220

221
// Preprocess Ets nodes by providing an undefined as the first arg
222
// so that we don't have to think if it is already provided later
223
export class EtsFirstArgTransformer extends AbstractVisitor {
224
    constructor(
225
        sourceFile: ts.SourceFile,
226
        ctx: ts.TransformationContext,
227
        public importer: Importer,
228
        private callTable: CallTable
229
    ) {
230
        super(sourceFile, ctx)
231
    }
232

233
    visitor(beforeChildren: ts.Node): ts.Node {
234
        const node = this.visitEachChild(beforeChildren)
235
        if (ts.isEtsComponentExpression(node)) {
236
            return ts.factory.updateEtsComponentExpression(
237
                node,
238
                node.expression as ts.Identifier,
239
                [undefinedValue(), ...node.arguments],
240
                node.body
241
            )
242
        }
243
        if (ts.isCallExpression(node) && isBuilderLambdaCall(this.callTable, node)) {
244
            return ts.factory.updateCallExpression(
245
                node,
246
                node.expression,
247
                node.typeArguments,
248
                [undefinedValue(), ...node.arguments]
249
            )
250
        }
251
        return node
252
    }
253
}
254

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.