13af6ab5fSopenharmony_ci/* 23af6ab5fSopenharmony_ci * Copyright (c) 2023-2024 Huawei Device Co., Ltd. 33af6ab5fSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 43af6ab5fSopenharmony_ci * you may not use this file except in compliance with the License. 53af6ab5fSopenharmony_ci * You may obtain a copy of the License at 63af6ab5fSopenharmony_ci * 73af6ab5fSopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 83af6ab5fSopenharmony_ci * 93af6ab5fSopenharmony_ci * Unless required by applicable law or agreed to in writing, software 103af6ab5fSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 113af6ab5fSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 123af6ab5fSopenharmony_ci * See the License for the specific language governing permissions and 133af6ab5fSopenharmony_ci * limitations under the License. 143af6ab5fSopenharmony_ci */ 153af6ab5fSopenharmony_ci 163af6ab5fSopenharmony_ciimport * as ts from 'typescript'; 173af6ab5fSopenharmony_ciimport type { TsUtils } from '../TsUtils'; 183af6ab5fSopenharmony_ci 193af6ab5fSopenharmony_citype CheckStdCallApi = (callExpr: ts.CallExpression) => boolean; 203af6ab5fSopenharmony_citype StdCallApiEntry = Map<string, CheckStdCallApi>; 213af6ab5fSopenharmony_ci 223af6ab5fSopenharmony_ciexport class SupportedStdCallApiChecker { 233af6ab5fSopenharmony_ci tsUtils: TsUtils; 243af6ab5fSopenharmony_ci typeChecker: ts.TypeChecker; 253af6ab5fSopenharmony_ci 263af6ab5fSopenharmony_ci constructor(tsUtils: TsUtils, typeChecker: ts.TypeChecker) { 273af6ab5fSopenharmony_ci this.tsUtils = tsUtils; 283af6ab5fSopenharmony_ci this.typeChecker = typeChecker; 293af6ab5fSopenharmony_ci } 303af6ab5fSopenharmony_ci 313af6ab5fSopenharmony_ci stdObjectEntry = new Map<string, CheckStdCallApi>([['assign', this.checkObjectAssignCall]]); 323af6ab5fSopenharmony_ci 333af6ab5fSopenharmony_ci StdCallApi = new Map<string | undefined, StdCallApiEntry>([ 343af6ab5fSopenharmony_ci ['Object', this.stdObjectEntry], 353af6ab5fSopenharmony_ci ['ObjectConstructor', this.stdObjectEntry] 363af6ab5fSopenharmony_ci ]); 373af6ab5fSopenharmony_ci 383af6ab5fSopenharmony_ci private static getCallExprNode(node: ts.Identifier): ts.CallExpression | undefined { 393af6ab5fSopenharmony_ci let callExpr: ts.CallExpression | undefined; 403af6ab5fSopenharmony_ci if (ts.isCallExpression(node.parent)) { 413af6ab5fSopenharmony_ci callExpr = node.parent; 423af6ab5fSopenharmony_ci } else if (ts.isPropertyAccessExpression(node.parent) && ts.isCallExpression(node.parent.parent)) { 433af6ab5fSopenharmony_ci callExpr = node.parent.parent; 443af6ab5fSopenharmony_ci } 453af6ab5fSopenharmony_ci return callExpr; 463af6ab5fSopenharmony_ci } 473af6ab5fSopenharmony_ci 483af6ab5fSopenharmony_ci isSupportedStdCallAPI( 493af6ab5fSopenharmony_ci node: ts.Identifier | ts.CallExpression, 503af6ab5fSopenharmony_ci parentSymName: string | undefined, 513af6ab5fSopenharmony_ci symName: string 523af6ab5fSopenharmony_ci ): boolean { 533af6ab5fSopenharmony_ci const callExpr = ts.isIdentifier(node) ? SupportedStdCallApiChecker.getCallExprNode(node) : node; 543af6ab5fSopenharmony_ci if (!callExpr) { 553af6ab5fSopenharmony_ci return false; 563af6ab5fSopenharmony_ci } 573af6ab5fSopenharmony_ci 583af6ab5fSopenharmony_ci const entry = this.StdCallApi.get(parentSymName); 593af6ab5fSopenharmony_ci if (entry) { 603af6ab5fSopenharmony_ci const stdCallApiCheckCb = entry.get(symName); 613af6ab5fSopenharmony_ci return !!stdCallApiCheckCb && stdCallApiCheckCb.call(this, callExpr); 623af6ab5fSopenharmony_ci } 633af6ab5fSopenharmony_ci 643af6ab5fSopenharmony_ci return false; 653af6ab5fSopenharmony_ci } 663af6ab5fSopenharmony_ci 673af6ab5fSopenharmony_ci private checkObjectAssignCall(callExpr: ts.CallExpression): boolean { 683af6ab5fSopenharmony_ci 693af6ab5fSopenharmony_ci /* 703af6ab5fSopenharmony_ci * 'Object.assign' is allowed only with signature like following: 713af6ab5fSopenharmony_ci * assign(target: Record<string, V>, ...source: Object[]>): Record<String, V> 723af6ab5fSopenharmony_ci * 733af6ab5fSopenharmony_ci * Note: For 'return' type, check the contextual type of call expression, as the 743af6ab5fSopenharmony_ci * return type of actual call signature will be deduced as an intersection of all 753af6ab5fSopenharmony_ci * types of the 'target' and 'source' arguments. 763af6ab5fSopenharmony_ci */ 773af6ab5fSopenharmony_ci 783af6ab5fSopenharmony_ci if (callExpr.typeArguments || callExpr.arguments.length === 0) { 793af6ab5fSopenharmony_ci return false; 803af6ab5fSopenharmony_ci } 813af6ab5fSopenharmony_ci const targetArgType = this.typeChecker.getTypeAtLocation(callExpr.arguments[0]); 823af6ab5fSopenharmony_ci if (!this.isValidObjectAssignRecordType(targetArgType)) { 833af6ab5fSopenharmony_ci return false; 843af6ab5fSopenharmony_ci } 853af6ab5fSopenharmony_ci const contextualType = this.typeChecker.getContextualType(callExpr); 863af6ab5fSopenharmony_ci if (!contextualType || !this.isValidObjectAssignRecordType(contextualType)) { 873af6ab5fSopenharmony_ci return false; 883af6ab5fSopenharmony_ci } 893af6ab5fSopenharmony_ci 903af6ab5fSopenharmony_ci return true; 913af6ab5fSopenharmony_ci } 923af6ab5fSopenharmony_ci 933af6ab5fSopenharmony_ci private isValidObjectAssignRecordType(type: ts.Type): boolean { 943af6ab5fSopenharmony_ci if (this.tsUtils.isStdRecordType(type) && type.aliasTypeArguments?.length) { 953af6ab5fSopenharmony_ci const typeArg = type.aliasTypeArguments[0]; 963af6ab5fSopenharmony_ci if (typeArg.getFlags() & (ts.TypeFlags.String | ts.TypeFlags.StringLiteral)) { 973af6ab5fSopenharmony_ci return true; 983af6ab5fSopenharmony_ci } 993af6ab5fSopenharmony_ci } 1003af6ab5fSopenharmony_ci return false; 1013af6ab5fSopenharmony_ci } 1023af6ab5fSopenharmony_ci} 103