• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 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
16import {
17  isBinaryExpression,
18  isCallExpression,
19  isClassDeclaration,
20  isComputedPropertyName,
21  isConstructorDeclaration,
22  isEnumDeclaration,
23  isIdentifier,
24  isObjectLiteralExpression,
25  isParameter,
26  isPropertyAccessExpression,
27  isPropertyAssignment,
28  isPropertyDeclaration,
29  isStructDeclaration,
30  isStringLiteral,
31  isTypeLiteralNode,
32  isVariableStatement,
33  SyntaxKind,
34  isExpressionStatement,
35  isClassExpression,
36  getModifiers,
37} from 'typescript';
38
39import type {
40  ClassDeclaration,
41  ClassExpression,
42  ElementAccessExpression,
43  EnumDeclaration,
44  Expression,
45  HeritageClause,
46  InterfaceDeclaration,
47  Modifier,
48  NodeArray,
49  ObjectLiteralExpression,
50  PropertyName,
51  Statement,
52  StructDeclaration,
53  TypeAliasDeclaration,
54} from 'typescript';
55
56import { OhPackType } from './TransformUtil';
57import { ApiExtractor } from '../common/ApiExtractor';
58
59export const stringPropsSet: Set<string> = new Set();
60/**
61 * The struct properties may be initialized in other files, but the properties in the struct definition are not obfuscated.
62 * So the whitelist of struct properties is collected during the project scanning process.
63 */
64export const structPropsSet: Set<string> = new Set();
65
66/**
67 * find openHarmony module import statement
68 * example:
69 *  jsbundle - var _ohos = _interopRequireDefault(requireModule('@ohos.hilog'));
70 *  esmodule - var hilog = globalThis.requireNapi('hilog') || ...
71 *
72 * @param node
73 * @param moduleName full name of imported module, must check format before called, example:
74 *  - '@ohos.hilog'
75 *  - '@ohos.application.Ability'
76 */
77export function findOhImportStatement(node: Statement, moduleName: string): OhPackType {
78  if (!isVariableStatement(node) || node.declarationList.declarations.length !== 1) {
79    return OhPackType.NONE;
80  }
81
82  const initializer: Expression = node.declarationList.declarations[0].initializer;
83  if (initializer === undefined) {
84    return OhPackType.NONE;
85  }
86
87  /** esmodule */
88  if (isBinaryExpression(initializer)) {
89    if (initializer.operatorToken.kind !== SyntaxKind.BarBarToken) {
90      return OhPackType.NONE;
91    }
92
93    if (!isCallExpression(initializer.left)) {
94      return OhPackType.NONE;
95    }
96
97    if (!isPropertyAccessExpression(initializer.left.expression)) {
98      return OhPackType.NONE;
99    }
100
101    if (!isIdentifier(initializer.left.expression.expression) ||
102      initializer.left.expression.expression.text !== 'globalThis') {
103      return OhPackType.NONE;
104    }
105
106    if (!isIdentifier(initializer.left.expression.name) ||
107      initializer.left.expression.name.text !== 'requireNapi') {
108      return OhPackType.NONE;
109    }
110
111    if (initializer.left.arguments.length !== 1) {
112      return OhPackType.NONE;
113    }
114
115    const arg: Expression = initializer.left.arguments[0];
116    if (isStringLiteral(arg) && arg.text === moduleName.substring('@ohos.'.length)) {
117      return OhPackType.ES_MODULE;
118    }
119  }
120
121  /** jsbundle */
122  if (isCallExpression(initializer)) {
123    if (initializer.arguments.length !== 1) {
124      return OhPackType.NONE;
125    }
126
127    if (!isIdentifier(initializer.expression) ||
128      initializer.expression.text !== '_interopRequireDefault') {
129      return OhPackType.NONE;
130    }
131
132    const arg: Expression = initializer.arguments[0];
133    if (!isCallExpression(arg)) {
134      return OhPackType.NONE;
135    }
136
137    if (!isIdentifier(arg.expression) || arg.expression.text !== 'requireModule') {
138      return OhPackType.NONE;
139    }
140
141    const innerArg: Expression = arg.arguments[0];
142    if (!isStringLiteral(innerArg) || innerArg.text !== moduleName) {
143      return OhPackType.NONE;
144    }
145
146    return OhPackType.JS_BUNDLE;
147  }
148
149  return OhPackType.NONE;
150}
151
152function containViewPU(heritageClauses: NodeArray<HeritageClause>): boolean {
153  if (!heritageClauses) {
154    return false;
155  }
156
157  let hasViewPU: boolean = false;
158  heritageClauses.forEach(
159    (heritageClause) => {
160      if (!heritageClause || !heritageClause.types) {
161        return;
162      }
163
164      const types = heritageClause.types;
165      types.forEach((typeExpression) => {
166        if (!typeExpression || !typeExpression.expression) {
167          return;
168        }
169
170        const expression = typeExpression.expression;
171        if (isIdentifier(expression) && expression.text === 'ViewPU') {
172          hasViewPU = true;
173        }
174      });
175    });
176
177  return hasViewPU;
178}
179
180/**
181 * used to ignore user defined ui component class property name
182 * @param classNode
183 */
184export function isViewPUBasedClass(classNode: ClassDeclaration): boolean {
185  if (!classNode) {
186    return false;
187  }
188
189  if (!isClassDeclaration(classNode)) {
190    return false;
191  }
192
193  const heritageClause = classNode.heritageClauses;
194  return containViewPU(heritageClause);
195}
196
197export function collectPropertyNamesAndStrings(memberName: PropertyName, propertySet: Set<string>): void {
198  if (isIdentifier(memberName)) {
199    propertySet.add(memberName.text);
200  }
201
202  if (isStringLiteral(memberName)) {
203    propertySet.add(memberName.text);
204    stringPropsSet.add(memberName.text);
205  }
206
207  if (isComputedPropertyName(memberName) && isStringLiteral(memberName.expression)) {
208    propertySet.add(memberName.expression.text);
209    stringPropsSet.add(memberName.expression.text);
210  }
211}
212
213export function getElementAccessExpressionProperties(elementAccessExpressionNode: ElementAccessExpression, propertySet: Set<string>): void {
214  if (!elementAccessExpressionNode || !elementAccessExpressionNode.argumentExpression) {
215    return;
216  }
217
218  if (isStringLiteral(elementAccessExpressionNode.argumentExpression)) {
219    stringPropsSet.add(elementAccessExpressionNode.argumentExpression.text);
220  }
221}
222
223export function getTypeAliasProperties(typeAliasNode: TypeAliasDeclaration, propertySet: Set<string>): void {
224  if (!typeAliasNode || !typeAliasNode.type || !isTypeLiteralNode(typeAliasNode.type)) {
225    return;
226  }
227
228  typeAliasNode.type.members.forEach((member) => {
229    if (!member || !member.name) {
230      return;
231    }
232    let memberName: PropertyName = member.name;
233    collectPropertyNamesAndStrings(memberName, propertySet);
234  });
235}
236
237/**
238 * export interface interfaceName {
239 *  a1: number;
240 *  "a2": number;
241 *  ["a3"]: number;
242 * }
243 */
244
245export function getInterfaceProperties(interfaceNode: InterfaceDeclaration, propertySet: Set<string>): void {
246  if (!interfaceNode || !interfaceNode.members) {
247    return;
248  }
249
250  interfaceNode.members.forEach((member) => {
251    if (!member || !member.name) {
252      return;
253    }
254
255    let memberName: PropertyName = member.name;
256    collectPropertyNamesAndStrings(memberName, propertySet);
257  });
258}
259
260export function isParameterPropertyModifier(modifier: Modifier): boolean {
261  if (modifier.kind === SyntaxKind.PublicKeyword ||
262    modifier.kind === SyntaxKind.PrivateKeyword ||
263    modifier.kind === SyntaxKind.ProtectedKeyword ||
264    modifier.kind === SyntaxKind.ReadonlyKeyword) {
265    return true;
266  }
267  return false;
268}
269
270export function getClassProperties(classNode: ClassDeclaration | ClassExpression | StructDeclaration, propertySet: Set<string>): void {
271  if (!classNode || !classNode.members) {
272    return;
273  }
274
275  if (isStructDeclaration(classNode)) {
276    getStructProperties(classNode, structPropsSet);
277  }
278  traverseMembersOfClass(classNode, propertySet);
279  return;
280}
281
282function traverseMembersOfClass(classNode: ClassDeclaration | ClassExpression | StructDeclaration, propertySet: Set<string>): void {
283  classNode.members.forEach((member) => {
284    if (!member) {
285      return;
286    }
287
288    const memberName: PropertyName = member.name;
289    if (memberName) {
290      collectPropertyNamesAndStrings(memberName, propertySet);
291    }
292
293    if (isConstructorDeclaration(member) && member.parameters) {
294      member.parameters.forEach((parameter) => {
295        const modifiers = getModifiers(parameter);
296        if (isParameter(parameter) && modifiers && modifiers.length > 0) {
297          if (parameter.name && isIdentifier(parameter.name)) {
298            let hasParameterPropertyModifier = modifiers.find(modifier => isParameterPropertyModifier(modifier)) !== undefined;
299            if (hasParameterPropertyModifier) {
300              propertySet.add(parameter.name.text);
301              ApiExtractor.mConstructorPropertySet?.add(parameter.name.text);
302            }
303          }
304          processMemberInitializer(parameter.initializer, propertySet);
305        }
306      });
307
308      if (member.body) {
309        member.body.statements.forEach((statement) => {
310          if (isExpressionStatement(statement) && isBinaryExpression(statement.expression) &&
311            statement.expression.operatorToken.kind === SyntaxKind.EqualsToken) {
312            processMemberInitializer(statement.expression.right, propertySet);
313          }
314        });
315      }
316    }
317
318    if (!isPropertyDeclaration(member) || !member.initializer) {
319      return;
320    }
321    processMemberInitializer(member.initializer, propertySet);
322  });
323  return;
324}
325
326function processMemberInitializer(memberInitializer: Expression | undefined, propertySet: Set<string>): void {
327  if (!memberInitializer) {
328    return;
329  }
330
331  if (isObjectLiteralExpression(memberInitializer)) {
332    getObjectProperties(memberInitializer, propertySet);
333    return;
334  }
335
336  if (isClassDeclaration(memberInitializer) || isClassExpression(memberInitializer) || isStructDeclaration(memberInitializer)) {
337    getClassProperties(memberInitializer, propertySet);
338    return;
339  }
340
341  if (isEnumDeclaration(memberInitializer)) {
342    getEnumProperties(memberInitializer, propertySet);
343    return;
344  }
345}
346
347export function getEnumProperties(enumNode: EnumDeclaration, propertySet: Set<string>): void {
348  if (!enumNode || !enumNode.members) {
349    return;
350  }
351
352  enumNode.members.forEach((member) => {
353    if (!member || !member.name) {
354      return;
355    }
356
357    const memberName: PropertyName = member.name;
358    collectPropertyNamesAndStrings(memberName, propertySet);
359    //other kind ignore
360  });
361
362  return;
363}
364
365export function getObjectProperties(objNode: ObjectLiteralExpression, propertySet: Set<string>): void {
366  if (!objNode || !objNode.properties) {
367    return;
368  }
369
370  objNode.properties.forEach((propertyElement) => {
371    if (!propertyElement || !propertyElement.name) {
372      return;
373    }
374
375    const propertyName: PropertyName = propertyElement.name;
376    collectPropertyNamesAndStrings(propertyName, propertySet);
377
378    //extract class element's property, example: export const hello = {info={read: {}}}
379    if (!isPropertyAssignment(propertyElement) || !propertyElement.initializer) {
380      return;
381    }
382
383    if (isObjectLiteralExpression(propertyElement.initializer)) {
384      getObjectProperties(propertyElement.initializer, propertySet);
385      return;
386    }
387
388    if (isClassDeclaration(propertyElement.initializer)) {
389      getClassProperties(propertyElement.initializer, propertySet);
390      return;
391    }
392
393    if (isEnumDeclaration(propertyElement.initializer)) {
394      getEnumProperties(propertyElement.initializer, propertySet);
395      return;
396    }
397  });
398
399  return;
400}
401
402export function getStructProperties(structNode: StructDeclaration, propertySet: Set<string>): void {
403  structNode?.members?.forEach((member) => {
404    const memberName: PropertyName = member?.name;
405    if (!memberName) {
406      return;
407    }
408    collectPropertyNamesAndStrings(memberName, propertySet);
409  });
410}