161847f8eSopenharmony_ci/* 261847f8eSopenharmony_ci * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 361847f8eSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 461847f8eSopenharmony_ci * you may not use this file except in compliance with the License. 561847f8eSopenharmony_ci * You may obtain a copy of the License at 661847f8eSopenharmony_ci * 761847f8eSopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 861847f8eSopenharmony_ci * 961847f8eSopenharmony_ci * Unless required by applicable law or agreed to in writing, software 1061847f8eSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 1161847f8eSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1261847f8eSopenharmony_ci * See the License for the specific language governing permissions and 1361847f8eSopenharmony_ci * limitations under the License. 1461847f8eSopenharmony_ci */ 1561847f8eSopenharmony_ci 1661847f8eSopenharmony_ciconst { ErrorType, ErrorLevel, LogType, requireTypescriptModule, checkVersionNeedCheck, createErrorInfo, ErrorValueInfo } = require('./utils'); 1761847f8eSopenharmony_ciconst { addAPICheckErrorLogs } = require('./compile_info'); 1861847f8eSopenharmony_ciconst { checkSmallHump } = require('./check_hump'); 1961847f8eSopenharmony_ciconst ts = requireTypescriptModule(); 2061847f8eSopenharmony_ci 2161847f8eSopenharmony_ci// check if on and off functions show in pair 2261847f8eSopenharmony_cifunction checkOnAndOffAppearInPair(node, sourcefile, fileName, onEventAllNames, onEventCheckNames, offEventAllNames, offEventCheckNames) { 2361847f8eSopenharmony_ci for (const value of onEventCheckNames) { 2461847f8eSopenharmony_ci if (!offEventAllNames.has(value)) { 2561847f8eSopenharmony_ci const checkErrorResult = createErrorInfo(ErrorValueInfo.ERROR_EVENT_ON_AND_OFF_PAIR, []); 2661847f8eSopenharmony_ci addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.API_PAIR_ERRORS, checkErrorResult, LogType.LOG_API, 2761847f8eSopenharmony_ci ErrorLevel.MIDDLE); 2861847f8eSopenharmony_ci } 2961847f8eSopenharmony_ci } 3061847f8eSopenharmony_ci for (const value of offEventCheckNames) { 3161847f8eSopenharmony_ci if (!onEventAllNames.has(value)) { 3261847f8eSopenharmony_ci const checkErrorResult = createErrorInfo(ErrorValueInfo.ERROR_EVENT_ON_AND_OFF_PAIR, []); 3361847f8eSopenharmony_ci addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.API_PAIR_ERRORS, checkErrorResult, LogType.LOG_API, 3461847f8eSopenharmony_ci ErrorLevel.MIDDLE); 3561847f8eSopenharmony_ci } 3661847f8eSopenharmony_ci } 3761847f8eSopenharmony_ci} 3861847f8eSopenharmony_ci 3961847f8eSopenharmony_cifunction checkTheFirstParameter(node, sourcefile, fileName) { 4061847f8eSopenharmony_ci // check the version 4161847f8eSopenharmony_ci if (!checkVersionNeedCheck(node)) { 4261847f8eSopenharmony_ci return; 4361847f8eSopenharmony_ci } 4461847f8eSopenharmony_ci if (node.parameters && node.parameters.length > 0 && node.parameters[0].type) { 4561847f8eSopenharmony_ci const firstParameterType = node.parameters[0].type; 4661847f8eSopenharmony_ci // check the type of first parameter 4761847f8eSopenharmony_ci if ((firstParameterType.kind === ts.SyntaxKind.LiteralType && firstParameterType.literal.kind === 4861847f8eSopenharmony_ci ts.SyntaxKind.StringLiteral)) { 4961847f8eSopenharmony_ci // if the first parameter is string 5061847f8eSopenharmony_ci const parameterName = firstParameterType.literal.text; 5161847f8eSopenharmony_ci if (!checkSmallHump(parameterName)) { 5261847f8eSopenharmony_ci const checkErrorResult = createErrorInfo(ErrorValueInfo.ERROR_EVENT_NAME_SMALL_HUMP, [parameterName]); 5361847f8eSopenharmony_ci addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.PARAMETER_ERRORS, checkErrorResult, LogType.LOG_API, 5461847f8eSopenharmony_ci ErrorLevel.MIDDLE); 5561847f8eSopenharmony_ci return; 5661847f8eSopenharmony_ci } 5761847f8eSopenharmony_ci } else if (firstParameterType.kind === ts.SyntaxKind.StringKeyword) { 5861847f8eSopenharmony_ci // if the first parameter is 'string' 5961847f8eSopenharmony_ci return; 6061847f8eSopenharmony_ci } else { 6161847f8eSopenharmony_ci let checkErrorResult = createErrorInfo(ErrorValueInfo.ERROR_EVENT_NAME_STRING, []); 6261847f8eSopenharmony_ci if (firstParameterType.typeName && firstParameterType.typeName.escapedText === '') { 6361847f8eSopenharmony_ci checkErrorResult = createErrorInfo(ErrorValueInfo.ERROR_EVENT_NAME_NULL, []); 6461847f8eSopenharmony_ci } 6561847f8eSopenharmony_ci addAPICheckErrorLogs(node, sourcefile, fileName, ErrorType.PARAMETER_ERRORS, checkErrorResult, LogType.LOG_API, 6661847f8eSopenharmony_ci ErrorLevel.MIDDLE); 6761847f8eSopenharmony_ci } 6861847f8eSopenharmony_ci } 6961847f8eSopenharmony_ci} 7061847f8eSopenharmony_ci 7161847f8eSopenharmony_cifunction isBasicType(node) { 7261847f8eSopenharmony_ci if (node.type !== undefined) { 7361847f8eSopenharmony_ci const nodeKind = node.type.kind; 7461847f8eSopenharmony_ci const basicTypes = new Set([ts.SyntaxKind.NumberKeyword, ts.SyntaxKind.StringKeyword, ts.SyntaxKind.BooleanKeyword, ts.SyntaxKind.UndefinedKeyword, 7561847f8eSopenharmony_ci ts.SyntaxKind.LiteralType]); 7661847f8eSopenharmony_ci if (basicTypes.has(nodeKind)) { 7761847f8eSopenharmony_ci return true; 7861847f8eSopenharmony_ci } 7961847f8eSopenharmony_ci } 8061847f8eSopenharmony_ci return false; 8161847f8eSopenharmony_ci} 8261847f8eSopenharmony_ci 8361847f8eSopenharmony_ci// check if the callback parameter of off function is optional 8461847f8eSopenharmony_cifunction checkOffFunctions(nodes, sourcefile, fileName) { 8561847f8eSopenharmony_ci let isAllCallbackMandatory = true; 8661847f8eSopenharmony_ci let someoneMissingCallback = false; 8761847f8eSopenharmony_ci let someoneHasCallback = false; 8861847f8eSopenharmony_ci for (let node of nodes) { 8961847f8eSopenharmony_ci if (node.parameters.length === 0) { 9061847f8eSopenharmony_ci continue; 9161847f8eSopenharmony_ci } 9261847f8eSopenharmony_ci const lastParameter = node.parameters[node.parameters.length - 1]; 9361847f8eSopenharmony_ci if (isBasicType(lastParameter)) { 9461847f8eSopenharmony_ci someoneMissingCallback = true; 9561847f8eSopenharmony_ci } else { 9661847f8eSopenharmony_ci someoneHasCallback = true; 9761847f8eSopenharmony_ci if (lastParameter.questionToken !== undefined) { 9861847f8eSopenharmony_ci isAllCallbackMandatory = false; 9961847f8eSopenharmony_ci } 10061847f8eSopenharmony_ci } 10161847f8eSopenharmony_ci } 10261847f8eSopenharmony_ci // has off fucntion with callback parameter which is not optional, and doesn't have off function without callback parameter 10361847f8eSopenharmony_ci if (isAllCallbackMandatory && !someoneMissingCallback) { 10461847f8eSopenharmony_ci const checkErrorResult = createErrorInfo(ErrorValueInfo.ERROR_EVENT_CALLBACK_OPTIONAL, []); 10561847f8eSopenharmony_ci addAPICheckErrorLogs(nodes[0], sourcefile, fileName, ErrorType.PARAMETER_ERRORS, checkErrorResult, LogType.LOG_API, 10661847f8eSopenharmony_ci ErrorLevel.MIDDLE); 10761847f8eSopenharmony_ci } 10861847f8eSopenharmony_ci} 10961847f8eSopenharmony_ci 11061847f8eSopenharmony_cifunction extendEventNames(node, allNames, checkNames) { 11161847f8eSopenharmony_ci const nodeType = node.parameters[0].type; 11261847f8eSopenharmony_ci let eventName = ''; 11361847f8eSopenharmony_ci if (nodeType.kind === ts.SyntaxKind.LiteralType) { 11461847f8eSopenharmony_ci eventName = nodeType.literal.text; 11561847f8eSopenharmony_ci allNames.add(eventName); 11661847f8eSopenharmony_ci } else if (nodeType.kind === ts.SyntaxKind.StringKeyword) { 11761847f8eSopenharmony_ci eventName = 'string'; 11861847f8eSopenharmony_ci allNames.add(eventName); 11961847f8eSopenharmony_ci } 12061847f8eSopenharmony_ci if (checkVersionNeedCheck(node) && eventName !== '') { 12161847f8eSopenharmony_ci checkNames.add(eventName); 12261847f8eSopenharmony_ci } 12361847f8eSopenharmony_ci return eventName; 12461847f8eSopenharmony_ci} 12561847f8eSopenharmony_ci 12661847f8eSopenharmony_cifunction extendEventNodes(node, eventName, nodesSet) { 12761847f8eSopenharmony_ci// store the off function node based on their names 12861847f8eSopenharmony_ci if (!nodesSet.get(eventName)) { 12961847f8eSopenharmony_ci nodesSet.set(eventName, [node]); 13061847f8eSopenharmony_ci } else { 13161847f8eSopenharmony_ci let curNodes = nodesSet.get(eventName); 13261847f8eSopenharmony_ci curNodes.push(node); 13361847f8eSopenharmony_ci nodesSet.set(eventName, curNodes); 13461847f8eSopenharmony_ci } 13561847f8eSopenharmony_ci} 13661847f8eSopenharmony_ci 13761847f8eSopenharmony_ci// handle event subscription node 13861847f8eSopenharmony_cifunction handleVariousEventSubscriptionAPI(childNode, childNodeName, sourcefile, fileName, onEventAllNames, onEventCheckNames, 13961847f8eSopenharmony_ci offEventAllNames, offEventCheckNames, offEventNodes) { 14061847f8eSopenharmony_ci if (childNode.parameters && childNode.parameters.length > 0 && childNode.parameters[0].type) { 14161847f8eSopenharmony_ci // judge the event subscription api type 14261847f8eSopenharmony_ci if (childNodeName === 'on') { 14361847f8eSopenharmony_ci extendEventNames(childNode, onEventAllNames, onEventCheckNames); 14461847f8eSopenharmony_ci checkTheFirstParameter(childNode, sourcefile, fileName, childNodeName); 14561847f8eSopenharmony_ci } else if (childNodeName === 'off') { 14661847f8eSopenharmony_ci let eventName = extendEventNames(childNode, offEventAllNames, offEventCheckNames); 14761847f8eSopenharmony_ci extendEventNodes(childNode, eventName, offEventNodes); 14861847f8eSopenharmony_ci checkTheFirstParameter(childNode, sourcefile, fileName, childNodeName); 14961847f8eSopenharmony_ci } else if (childNodeName === 'once' || childNodeName === 'emit') { 15061847f8eSopenharmony_ci checkTheFirstParameter(childNode, sourcefile, fileName, childNodeName); 15161847f8eSopenharmony_ci } 15261847f8eSopenharmony_ci } 15361847f8eSopenharmony_ci} 15461847f8eSopenharmony_ci 15561847f8eSopenharmony_cifunction checkEventSubscription(node, sourcefile, fileName) { 15661847f8eSopenharmony_ci // if the node is namespace or interface 15761847f8eSopenharmony_ci if ((ts.isInterfaceDeclaration(node)) || ts.isModuleBlock(node) || ts.isModuleDeclaration(node) || 15861847f8eSopenharmony_ci ts.isClassDeclaration(node) || node === sourcefile) { 15961847f8eSopenharmony_ci const onEventAllNames = new Set(); 16061847f8eSopenharmony_ci const onEventCheckNames = new Set(); 16161847f8eSopenharmony_ci const offEventAllNames = new Set(); 16261847f8eSopenharmony_ci const offEventCheckNames = new Set(); 16361847f8eSopenharmony_ci const offEventNodes = new Map(); 16461847f8eSopenharmony_ci let childNodes = node.members; 16561847f8eSopenharmony_ci if (ts.isModuleDeclaration(node)) { 16661847f8eSopenharmony_ci childNodes = node.body.statements; 16761847f8eSopenharmony_ci } 16861847f8eSopenharmony_ci if (childNodes === undefined) { 16961847f8eSopenharmony_ci return; 17061847f8eSopenharmony_ci } 17161847f8eSopenharmony_ci childNodes.forEach((childNode) => { 17261847f8eSopenharmony_ci // if the node is method or function 17361847f8eSopenharmony_ci if (ts.isFunctionDeclaration(childNode) || ts.isMethodDeclaration(childNode) || ts.isMethodSignature(childNode)) { 17461847f8eSopenharmony_ci // if the version needed to be check 17561847f8eSopenharmony_ci let childNodeName = (childNode.name && ts.isIdentifier(childNode.name)) ? 17661847f8eSopenharmony_ci childNode.name.getText() : 17761847f8eSopenharmony_ci ''; 17861847f8eSopenharmony_ci handleVariousEventSubscriptionAPI(childNode, childNodeName, sourcefile, fileName, onEventAllNames, 17961847f8eSopenharmony_ci onEventCheckNames, offEventAllNames, offEventCheckNames, offEventNodes); 18061847f8eSopenharmony_ci } 18161847f8eSopenharmony_ci }); 18261847f8eSopenharmony_ci // check the callback parameter of off function is optional 18361847f8eSopenharmony_ci for (const event of offEventCheckNames) { 18461847f8eSopenharmony_ci checkOffFunctions(offEventNodes.get(event), sourcefile, fileName); 18561847f8eSopenharmony_ci } 18661847f8eSopenharmony_ci // check if the on and off functions of one event shows in pair 18761847f8eSopenharmony_ci checkOnAndOffAppearInPair(node, sourcefile, fileName, onEventAllNames, onEventCheckNames, offEventAllNames, offEventCheckNames); 18861847f8eSopenharmony_ci } 18961847f8eSopenharmony_ci} 19061847f8eSopenharmony_ciexports.checkEventSubscription = checkEventSubscription; 191