• 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 _ from 'lodash';
17import ts from 'typescript';
18import { StringConstant } from '../../utils/Constant';
19import {
20  ApiInfo,
21  ApiType,
22  BasicApiInfo,
23  ContainerApiInfo,
24  containerApiTypes,
25} from '../../typedef/parser/ApiInfoDefination';
26import { ApiDiffType, ApiStatusCode, BasicDiffInfo, DiffTypeInfo } from '../../typedef/diff/ApiInfoDiff';
27import { ApiInfosMap, FileInfoMap, FilesMap, Parser } from '../parser/parser';
28import { apiStatisticsType } from '../../typedef/statistics/ApiStatistics';
29import { DiffProcessorHelper } from './DiffProcessor';
30import { FunctionUtils } from '../../utils/FunctionUtils';
31import { Comment } from '../../typedef/parser/Comment';
32import { notJsDocApiTypes } from '../../typedef/parser/ApiInfoDefination';
33import { StringUtils } from '../../utils/StringUtils';
34import { CommentHelper } from '../parser/JsDocProcessor';
35
36export class DiffHelper {
37  /**
38   * 根据解析后的新旧版本SDK结果,得到对比差异
39   *
40   * @param { FilesMap } oldSDKApiMap 旧版本SDK解析后的结果
41   * @param { FilesMap } newSDKApiMap 新版本SDK解析后的结果
42   * @returns { BasicDiffInfo[] } 差异结果集
43   */
44  static diffSDK(
45    oldSDKApiMap: FilesMap,
46    newSDKApiMap: FilesMap,
47    isAllSheet: boolean,
48    isCheck?: boolean
49  ): BasicDiffInfo[] {
50    const clonedOldSDKApiMap: FilesMap = _.cloneDeep(oldSDKApiMap);
51    const clonedNewSDKApiMap: FilesMap = _.cloneDeep(newSDKApiMap);
52    const diffInfos: BasicDiffInfo[] = [];
53    const oldSDKApiLocations: Map<string, string[]> = DiffHelper.getApiLocations(clonedOldSDKApiMap, isCheck);
54    const newSDKApiLocations: Map<string, string[]> = DiffHelper.getApiLocations(clonedNewSDKApiMap, isCheck);
55    DiffHelper.diffKit(clonedOldSDKApiMap, clonedNewSDKApiMap, diffInfos);
56    const oldFilePathSet: Set<string> = new Set(Array.from(clonedOldSDKApiMap.keys()));
57    // 先以旧版本为基础进行对比
58    for (const key of oldSDKApiLocations.keys()) {
59      const apiLocation: string[] = oldSDKApiLocations.get(key) as string[];
60      const oldApiInfos: ApiInfo[] = Parser.getApiInfo(apiLocation, clonedOldSDKApiMap, isAllSheet) as ApiInfo[];
61      // 如果旧版本中的API在新版本中不存在,则为删除
62      if (!newSDKApiLocations.has(key)) {
63        oldApiInfos.forEach((oldApiInfo: ApiInfo) => {
64          diffInfos.push(
65            DiffProcessorHelper.wrapDiffInfo(
66              oldApiInfo,
67              undefined,
68              new DiffTypeInfo(ApiStatusCode.DELETE, ApiDiffType.REDUCE, oldApiInfo.getDefinedText())
69            )
70          );
71        });
72        continue;
73      }
74      // 新旧版本均存在,则进行对比
75      const newApiInfos: ApiInfo[] = Parser.getApiInfo(apiLocation, clonedNewSDKApiMap, isAllSheet) as ApiInfo[];
76      DiffHelper.diffApis(oldApiInfos, newApiInfos, diffInfos, isAllSheet, isCheck);
77      // 对比完则将新版本中的对应API进行删除
78      newSDKApiLocations.delete(key);
79    }
80    // 对比完还剩下的新版本中的API即为新增API
81    for (const key of newSDKApiLocations.keys()) {
82      const locations: string[] = newSDKApiLocations.get(key) as string[];
83      const newApiInfos: ApiInfo[] = Parser.getApiInfo(locations, clonedNewSDKApiMap, isAllSheet) as ApiInfo[];
84      newApiInfos.forEach((newApiInfo: ApiInfo) => {
85        let isNewFile: boolean = true;
86        if (oldFilePathSet.has(newApiInfo.getFilePath())) {
87          isNewFile = false;
88        }
89        diffInfos.push(
90          DiffProcessorHelper.wrapDiffInfo(
91            undefined,
92            newApiInfo,
93            new DiffTypeInfo(ApiStatusCode.NEW_API, ApiDiffType.ADD, undefined, newApiInfo.getDefinedText()),
94            isNewFile
95          )
96        );
97      });
98    }
99    return diffInfos;
100  }
101
102  static diffKit(clonedOldSDKApiMap: FilesMap, clonedNewSDKApiMap: FilesMap, diffInfos: BasicDiffInfo[]): void {
103    for (const key of clonedOldSDKApiMap.keys()) {
104      const oldSourceFileInfo: ApiInfo | undefined = DiffHelper.getSourceFileInfo(clonedOldSDKApiMap.get(key));
105      oldSourceFileInfo?.setSyscap(DiffHelper.getSyscapField(oldSourceFileInfo));
106      const oldKitInfo: string | undefined = oldSourceFileInfo?.getLastJsDocInfo()?.getKit();
107      //文件在新版本中被删除
108      if (!clonedNewSDKApiMap.get(key) && oldKitInfo !== 'NA') {
109        diffInfos.push(
110          DiffProcessorHelper.wrapDiffInfo(
111            oldSourceFileInfo,
112            undefined,
113            new DiffTypeInfo(ApiStatusCode.KIT_CHANGE, ApiDiffType.KIT_HAVE_TO_NA, oldKitInfo, 'NA')
114          )
115        );
116      } else if (clonedNewSDKApiMap.get(key)) {
117        const newSourceFileInfo: ApiInfo | undefined = DiffHelper.getSourceFileInfo(clonedNewSDKApiMap.get(key));
118        const newKitInfo: string | undefined = newSourceFileInfo?.getLastJsDocInfo()?.getKit();
119        if (oldKitInfo !== newKitInfo) {
120          diffInfos.push(
121            DiffProcessorHelper.wrapDiffInfo(
122              oldSourceFileInfo,
123              newSourceFileInfo,
124              new DiffTypeInfo(ApiStatusCode.KIT_CHANGE, DiffHelper.getKitDiffType(oldKitInfo, newKitInfo), oldKitInfo, newKitInfo)
125            )
126          );
127        }
128      }
129    }
130
131    for (const key of clonedNewSDKApiMap.keys()) {
132      const newSourceFileInfo: ApiInfo | undefined = DiffHelper.getSourceFileInfo(clonedNewSDKApiMap.get(key));
133      const newKitInfo: string | undefined = newSourceFileInfo?.getLastJsDocInfo()?.getKit();
134      if (!clonedOldSDKApiMap.get(key) && newKitInfo !== 'NA') {
135        diffInfos.push(
136          DiffProcessorHelper.wrapDiffInfo(
137            undefined,
138            newSourceFileInfo,
139            new DiffTypeInfo(ApiStatusCode.KIT_CHANGE, ApiDiffType.KIT_NA_TO_HAVE, 'NA', newKitInfo)
140          )
141        );
142      }
143    }
144  }
145
146  static getKitDiffType(oldKitInfo: string | undefined, newKitInfo: string | undefined): ApiDiffType {
147    if (oldKitInfo === 'NA' && newKitInfo === '') {
148      return ApiDiffType.KIT_NA_TO_HAVE;
149    } else if (oldKitInfo === '' && newKitInfo === 'NA') {
150      return ApiDiffType.KIT_HAVE_TO_NA;
151    } else if (oldKitInfo === 'NA' || oldKitInfo === '') {
152      return ApiDiffType.KIT_NA_TO_HAVE;
153    } else if (newKitInfo === 'NA' || newKitInfo === '') {
154      return ApiDiffType.KIT_HAVE_TO_NA;
155    }
156    return ApiDiffType.KIT_CHANGE;
157  }
158
159  static getSourceFileInfo(fileMap: FileInfoMap | undefined): ApiInfo | undefined {
160    if (!fileMap) {
161      return undefined;
162    }
163    let sourceFileInfos: ApiInfo[] = [];
164    for (const apiKey of fileMap.keys()) {
165      if (apiKey === StringConstant.SELF) {
166        sourceFileInfos = fileMap.get(apiKey) as ApiInfo[];
167      }
168    }
169    return sourceFileInfos[0];
170  }
171
172  /**
173   * 通过数组长度判断是否为同名函数
174   *
175   * @param apiInfos
176   */
177  static judgeIsSameNameFunction(apiInfos: BasicApiInfo[]): boolean {
178    let isSameNameFunction: boolean = false;
179    if (apiInfos.length > 1 && apiInfos[0].getApiType() === ApiType.METHOD) {
180      isSameNameFunction = true;
181    }
182    return isSameNameFunction;
183  }
184
185  /**
186   * 对比新旧版本API差异,类型为数组是由于同名函数的存在,因此统一为数组方便处理
187   *
188   * @param { ApiInfo[] } oldApiInfos 老版本API信息
189   * @param { ApiInfo[] } newApiInfos 新版本API信息
190   * @param { BasicDiffInfo[] } diffInfos api差异结果集
191   */
192  static diffApis(
193    oldApiInfos: ApiInfo[],
194    newApiInfos: ApiInfo[],
195    diffInfos: BasicDiffInfo[],
196    isAllSheet: boolean,
197    isCheck?: boolean
198  ): void {
199    const diffSets: Map<string, ApiInfo>[] = DiffHelper.getDiffSet(oldApiInfos, newApiInfos);
200    const oldReduceNewMap: Map<string, ApiInfo> = diffSets[0];
201    const newReduceOldMap: Map<string, ApiInfo> = diffSets[1];
202    if (oldReduceNewMap.size === 0) {
203      newReduceOldMap.forEach((newApiInfo: ApiInfo) => {
204        diffInfos.push(
205          DiffProcessorHelper.wrapDiffInfo(
206            undefined,
207            newApiInfo,
208            new DiffTypeInfo(ApiStatusCode.NEW_API, ApiDiffType.ADD, undefined, newApiInfo.getDefinedText())
209          )
210        );
211      });
212      return;
213    }
214    if (newReduceOldMap.size === 0) {
215      oldReduceNewMap.forEach((oldApiInfo: ApiInfo) => {
216        diffInfos.push(
217          DiffProcessorHelper.wrapDiffInfo(
218            oldApiInfo,
219            undefined,
220            new DiffTypeInfo(ApiStatusCode.DELETE, ApiDiffType.REDUCE, oldApiInfo.getDefinedText(), undefined)
221          )
222        );
223      });
224      return;
225    }
226
227    DiffHelper.diffSameNumberFunction(oldApiInfos, newApiInfos, diffInfos, isCheck);
228  }
229
230  /**
231   * 删除完全一样的API后,进行对比
232   * @param { ApiInfo[] } oldApiInfos
233   * @param { ApiInfo[] } newApiInfos
234   * @param diffInfos
235   * @param { boolean } isCheck 是否是api_check工具进行调用
236   */
237  static diffSameNumberFunction(
238    oldApiInfos: ApiInfo[],
239    newApiInfos: ApiInfo[],
240    diffInfos: BasicDiffInfo[],
241    isCheck?: boolean
242  ): void {
243    //长度为1说明新旧版本有变更的只有1个,可直接进行对比
244    if (oldApiInfos.length === 1 && oldApiInfos.length === newApiInfos.length) {
245      DiffProcessorHelper.JsDocDiffHelper.diffJsDocInfo(oldApiInfos[0], newApiInfos[0], diffInfos);
246      DiffProcessorHelper.ApiDecoratorsDiffHelper.diffDecorator(oldApiInfos[0], newApiInfos[0], diffInfos);
247      DiffProcessorHelper.ApiNodeDiffHelper.diffNodeInfo(oldApiInfos[0], newApiInfos[0], diffInfos, isCheck);
248    } else {
249      const newMethodInfoMap: Map<string, ApiInfo> = DiffHelper.setmethodInfoMap(newApiInfos);
250      const oldMethodInfoMap: Map<string, ApiInfo> = DiffHelper.setmethodInfoMap(oldApiInfos);
251      oldApiInfos.forEach((oldApiInfo: ApiInfo) => {
252        const newApiInfo: ApiInfo | undefined = newMethodInfoMap.get(oldApiInfo.getDefinedText());
253        if (newApiInfo) {
254          DiffProcessorHelper.JsDocDiffHelper.diffJsDocInfo(oldApiInfo, newApiInfo, diffInfos);
255          DiffProcessorHelper.ApiDecoratorsDiffHelper.diffDecorator(oldApiInfo, newApiInfo, diffInfos);
256          newMethodInfoMap.delete(oldApiInfo.getDefinedText());
257          oldMethodInfoMap.delete(oldApiInfo.getDefinedText());
258        }
259      });
260
261      if (oldMethodInfoMap.size === 1 && newMethodInfoMap.size === 1) {
262        const oldMethodInfo: ApiInfo = oldMethodInfoMap.entries().next().value[1];
263        const newMethodInfo: ApiInfo = newMethodInfoMap.entries().next().value[1];
264        DiffProcessorHelper.JsDocDiffHelper.diffJsDocInfo(oldMethodInfo, newMethodInfo, diffInfos);
265        DiffProcessorHelper.ApiDecoratorsDiffHelper.diffDecorator(oldMethodInfo, newMethodInfo, diffInfos);
266        DiffProcessorHelper.ApiNodeDiffHelper.diffNodeInfo(oldMethodInfo, newMethodInfo, diffInfos, isCheck);
267      } else {
268        newMethodInfoMap.forEach((apiInfo: ApiInfo, _) => {
269          diffInfos.push(
270            DiffProcessorHelper.wrapDiffInfo(
271              undefined,
272              apiInfo,
273              new DiffTypeInfo(ApiStatusCode.NEW_API, ApiDiffType.ADD, undefined, apiInfo.getDefinedText()),
274            )
275          );
276        });
277        oldMethodInfoMap.forEach((apiInfo: ApiInfo, _) => {
278          diffInfos.push(
279            DiffProcessorHelper.wrapDiffInfo(
280              apiInfo,
281              undefined,
282              new DiffTypeInfo(ApiStatusCode.DELETE, ApiDiffType.REDUCE, apiInfo.getDefinedText(), undefined),
283            )
284          );
285        });
286      }
287    }
288  }
289
290  static judgeIsAllDeprecated(apiInfos: ApiInfo[]): boolean {
291    let isAllDeprecated: boolean = true;
292    apiInfos.forEach((apiInfo: ApiInfo) => {
293      const deprecatedVersion = apiInfo.getLastJsDocInfo()?.getDeprecatedVersion();
294      if (deprecatedVersion === '-1' || !deprecatedVersion) {
295        isAllDeprecated = false;
296      }
297    });
298    return isAllDeprecated;
299  }
300
301  static handleDeprecatedVersion(apiInfos: ApiInfo[]): void {
302    let isAllDeprecated: boolean = true;
303    apiInfos.forEach((apiInfo: ApiInfo) => {
304      const deprecatedVersion = apiInfo.getLastJsDocInfo()?.getDeprecatedVersion();
305      if (deprecatedVersion === '-1' || !deprecatedVersion) {
306        isAllDeprecated = false;
307      }
308    });
309    if (isAllDeprecated) {
310      return;
311    }
312    apiInfos.forEach((apiInfo: ApiInfo) => {
313      apiInfo.getLastJsDocInfo()?.setDeprecatedVersion('-1');
314    });
315  }
316
317  /**
318   * 拼接同名函数的API声明
319   *
320   * @param methodInfoMap
321   * @returns
322   */
323  static joinApiText(methodInfoMap: Map<string, ApiInfo>): string {
324    const apiTextArr: string[] = [];
325    for (const apiText of methodInfoMap.keys()) {
326      apiTextArr.push(apiText);
327    }
328    return apiTextArr.join(' #&# ');
329  }
330
331  /**
332   * 生成map,key为API声明,value为API信息
333   *
334   * @param apiInfos
335   * @returns
336   */
337  static setmethodInfoMap(apiInfos: ApiInfo[]): Map<string, ApiInfo> {
338    const methodInfoMap: Map<string, ApiInfo> = new Map();
339    apiInfos.forEach((apiInfo: ApiInfo) => {
340      methodInfoMap.set(apiInfo.getDefinedText(), apiInfo);
341    });
342    return methodInfoMap;
343  }
344
345  /**
346   * 删除新旧版本里所有信息一样的API
347   *
348   * @param oldApiInfos
349   * @param newApiInfos
350   * @returns
351   */
352  static getDiffSet(oldApiInfos: ApiInfo[], newApiInfos: ApiInfo[]): Map<string, ApiInfo>[] {
353    const oldApiInfoMap: Map<string, ApiInfo> = new Map();
354    const newApiInfoMap: Map<string, ApiInfo> = new Map();
355    DiffHelper.setApiInfoMap(oldApiInfoMap, oldApiInfos);
356    DiffHelper.setApiInfoMap(newApiInfoMap, newApiInfos);
357    const oldReduceNewMap: Map<string, ApiInfo> = new Map();
358    oldApiInfoMap.forEach((apiInfo: ApiInfo, key: string) => {
359      if (!newApiInfoMap.has(key)) {
360        oldReduceNewMap.set(key, apiInfo);
361      }
362    });
363    const newReduceOldMap: Map<string, ApiInfo> = new Map();
364    newApiInfoMap.forEach((apiInfo: ApiInfo, key: string) => {
365      if (!oldApiInfoMap.has(key)) {
366        newReduceOldMap.set(key, apiInfo);
367      }
368    });
369    return [oldReduceNewMap, newReduceOldMap];
370  }
371
372  static setApiInfoMap(apiInfoMap: Map<string, ApiInfo>, apiInfos: ApiInfo[]): void {
373    apiInfos.forEach((apiInfo: ApiInfo) => {
374      const key = JSON.stringify(apiInfo);
375      apiInfoMap.set(key, apiInfo);
376    });
377  }
378
379  static getApiLocations(apiMap: FilesMap, isCheck?: boolean): Map<string, string[]> {
380    const apiLocations: Map<string, string[]> = new Map();
381    for (const filePath of apiMap.keys()) {
382      const index: number = 0;
383      const fileMap: FileInfoMap = apiMap.get(filePath) as FileInfoMap;
384      DiffHelper.processFileApiMap(fileMap, apiLocations, index, isCheck);
385    }
386    return apiLocations;
387  }
388
389  static processFileApiMap(
390    fileMap: FileInfoMap,
391    apiLocations: Map<string, string[]>,
392    index: number,
393    isCheck?: boolean
394  ): void {
395    for (const apiKey of fileMap.keys()) {
396      if (apiKey === StringConstant.SELF) {
397        continue;
398      }
399      const apiInfoMap: ApiInfosMap = fileMap.get(apiKey) as ApiInfosMap;
400      const apiInfos: BasicApiInfo[] = apiInfoMap.get(StringConstant.SELF) as BasicApiInfo[];
401      apiInfos.forEach((apiInfo: BasicApiInfo) => {
402        DiffHelper.processApiInfo(apiInfo, apiLocations, index, isCheck);
403        index++;
404      });
405    }
406  }
407
408  /**
409   * 删除最外层第一个API的jsdocText中,版权头和kit相关jsdoc
410   *
411   * @param apiInfo
412   * @returns
413   */
414  static deleteUselessJsdoc(apiInfo: BasicApiInfo): string {
415    const jsdocTextArr: string[] = apiInfo.getJsDocText().split('*/');
416    const clonedJsdocTextArr: string[] = jsdocTextArr;
417    if (clonedJsdocTextArr[1] && StringUtils.hasSubstring(clonedJsdocTextArr[1], CommentHelper.fileTag)) {
418      jsdocTextArr.splice(1, 1);
419    }
420
421    if (clonedJsdocTextArr[0] && StringUtils.hasSubstring(clonedJsdocTextArr[0], CommentHelper.fileTag)) {
422      jsdocTextArr.splice(0, 1);
423    }
424
425    if (clonedJsdocTextArr[0] && StringUtils.hasSubstring(clonedJsdocTextArr[0], CommentHelper.licenseKeyword)) {
426      jsdocTextArr.splice(0, 1);
427    }
428
429    return jsdocTextArr.join('*/');
430  }
431
432  static processApiInfo(
433    basicApiInfo: BasicApiInfo,
434    apiLocations: Map<string, string[]>,
435    index: number,
436    isCheck?: boolean
437  ): void {
438    const apiNode: ts.Node | undefined = basicApiInfo.getNode();
439    if (isCheck) {
440      const jsDocText: string | undefined = apiNode?.getFullText().replace(apiNode.getText(), '');
441      if (jsDocText) {
442        basicApiInfo.setJsDocText(jsDocText);
443      }
444    }
445
446    if (index === 0 && basicApiInfo.getParentApiType() === ApiType.SOURCE_FILE) {
447      const jsDocText: string = DiffHelper.deleteUselessJsdoc(basicApiInfo);
448      basicApiInfo.setJsDocText(jsDocText);
449    }
450
451    basicApiInfo.setSyscap(DiffHelper.getSyscapField(basicApiInfo));
452    basicApiInfo.setParentApi(undefined);
453
454    basicApiInfo.removeNode();
455    if (!apiStatisticsType.has(basicApiInfo.getApiType())) {
456      return;
457    }
458    if (basicApiInfo.getApiName() === 'constructor') {
459      return;
460    }
461    const apiInfo: ApiInfo = basicApiInfo as ApiInfo;
462    const locations: string[] = apiInfo.getHierarchicalRelations();
463    apiLocations.set(locations.toString(), locations);
464    if (!containerApiTypes.has(apiInfo.getApiType())) {
465      return;
466    }
467    const containerApiInfo: ContainerApiInfo = apiInfo as ContainerApiInfo;
468    containerApiInfo.getChildApis().forEach((childApiInfo: BasicApiInfo) => {
469      DiffHelper.processApiInfo(childApiInfo, apiLocations, 1, isCheck);
470    });
471  }
472
473  static getSyscapField(apiInfo: BasicApiInfo): string {
474    if (apiInfo.getApiType() === ApiType.SOURCE_FILE) {
475      const sourceFileContent: string = apiInfo.getNode()?.getFullText() as string;
476      let syscap = '';
477      if (/\@[S|s][Y|y][S|s][C|c][A|a][P|p]\s*((\w|\.|\/|\{|\@|\}|\s)+)/g.test(sourceFileContent)) {
478        sourceFileContent.replace(
479          /\@[S|s][Y|y][S|s][C|c][A|a][P|p]\s*((\w|\.|\/|\{|\@|\}|\s)+)/g,
480          (sysCapInfo: string, args: []) => {
481            syscap = sysCapInfo.replace(/\@[S|s][Y|y][S|s][C|c][A|a][P|p]/g, '').trim();
482            return syscap;
483          }
484        );
485      }
486      return FunctionUtils.handleSyscap(syscap);
487    }
488    if (notJsDocApiTypes.has(apiInfo.getApiType())) {
489      return '';
490    }
491    const clonedApiInfo: ApiInfo = apiInfo as ApiInfo;
492    const latestJsDocInfo: Comment.JsDocInfo | undefined = clonedApiInfo.getLastJsDocInfo();
493    if (!latestJsDocInfo) {
494      return DiffHelper.searchSyscapFieldInParent(clonedApiInfo);
495    }
496    let syscap: string | undefined = latestJsDocInfo?.getSyscap();
497    if (!syscap) {
498      return DiffHelper.searchSyscapFieldInParent(clonedApiInfo);
499    }
500    if (syscap) {
501      return FunctionUtils.handleSyscap(syscap);
502    }
503    return '';
504  }
505
506  static searchSyscapFieldInParent(apiInfo: ApiInfo): string {
507    let curApiInfo: ApiInfo = apiInfo;
508    let syscap: string = '';
509    const node: ts.Node | undefined = curApiInfo.getNode();
510    while (node && curApiInfo && !ts.isSourceFile(node)) {
511      syscap = curApiInfo.getSyscap();
512      if (syscap) {
513        return syscap;
514      }
515      curApiInfo = curApiInfo.getParentApi() as ApiInfo;
516    }
517    return syscap;
518  }
519}
520