1/* 2 * Copyright (c) 2022 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 * as ts from "typescript"; 17import * as jshelpers from "./jshelpers"; 18import { MandatoryArguments } from "./variable"; 19 20export function isOctalNumber(num: string): boolean { 21 if (!num || num.length < 2) { 22 return false; 23 } 24 25 let reg = /^0[0-7]+$/; 26 if (!reg.test(num)) { 27 return false; 28 } 29 30 return true; 31} 32 33export function isNewOrCallExpression(node: ts.Node): boolean { 34 return node.kind === ts.SyntaxKind.NewExpression || node.kind === ts.SyntaxKind.CallExpression; 35} 36 37export function stringLiteralIsInRegExp(node: ts.Node): boolean { 38 let parent = node.parent; 39 if (parent && isNewOrCallExpression(parent)) { 40 let expression = (<ts.NewExpression | ts.CallExpression>parent).expression; 41 if (ts.isIdentifier(expression)) { 42 if (expression.escapedText === "RegExp") { 43 return true; 44 } 45 } 46 } 47 48 return false; 49} 50 51export function isIncludeOctalEscapeSequence(text: string): boolean { 52 let reg = /\\(?:[1-7][0-7]{0,2}|[0-7]{2,3})/g; 53 if (!text.match(reg)) { 54 return false; 55 } 56 57 // Like \\1, should not be treated as an octal escape sequence 58 let index = 0; 59 while (index < text.length) { 60 if (text[index] === '\\' && index != text.length - 1) { 61 if (text[index + 1] === "\\") { 62 index++; 63 } else if (text[index + 1] >= '0' && text[index + 1] <= '7') { 64 return true; 65 } 66 } 67 index++; 68 } 69 70 return false; 71} 72 73export function isEvalOrArgumentsIdentifier(node: ts.Node): boolean { 74 return ts.isIdentifier(node) && (node.escapedText === "eval" || node.escapedText === MandatoryArguments); 75} 76 77export function isLeftHandSideExpressionKind(kind: ts.SyntaxKind): boolean { 78 switch (kind) { 79 case ts.SyntaxKind.NumericLiteral: 80 case ts.SyntaxKind.BigIntLiteral: 81 case ts.SyntaxKind.StringLiteral: 82 case ts.SyntaxKind.RegularExpressionLiteral: 83 case ts.SyntaxKind.NoSubstitutionTemplateLiteral: 84 case ts.SyntaxKind.Identifier: 85 case ts.SyntaxKind.FalseKeyword: 86 case ts.SyntaxKind.ImportKeyword: 87 case ts.SyntaxKind.NullKeyword: 88 case ts.SyntaxKind.SuperKeyword: 89 case ts.SyntaxKind.ThisKeyword: 90 case ts.SyntaxKind.TrueKeyword: 91 case ts.SyntaxKind.ArrayLiteralExpression: 92 case ts.SyntaxKind.ObjectLiteralExpression: 93 case ts.SyntaxKind.PropertyAccessExpression: 94 case ts.SyntaxKind.ElementAccessExpression: 95 case ts.SyntaxKind.CallExpression: 96 case ts.SyntaxKind.NewExpression: 97 case ts.SyntaxKind.TaggedTemplateExpression: 98 case ts.SyntaxKind.ParenthesizedExpression: 99 case ts.SyntaxKind.FunctionExpression: 100 case ts.SyntaxKind.TemplateExpression: 101 case ts.SyntaxKind.ClassExpression: 102 case ts.SyntaxKind.NonNullExpression: 103 case ts.SyntaxKind.MetaProperty: 104 case ts.SyntaxKind.JsxElement: 105 case ts.SyntaxKind.JsxSelfClosingElement: 106 case ts.SyntaxKind.JsxFragment: 107 return true; 108 default: 109 return false; 110 } 111} 112 113export function isLeftHandSideExpression(node: ts.Node): boolean { 114 return isLeftHandSideExpressionKind(ts.skipPartiallyEmittedExpressions(node).kind); 115} 116 117export function isAssignmentOperator(token: ts.SyntaxKind): boolean { 118 return token >= ts.SyntaxKind.FirstAssignment && token <= ts.SyntaxKind.LastAssignment; 119} 120 121export function isOriginalKeyword(node: ts.Identifier): boolean { 122 if (node.originalKeywordKind! >= ts.SyntaxKind.FirstFutureReservedWord && 123 node.originalKeywordKind! <= ts.SyntaxKind.LastFutureReservedWord) { 124 return true; 125 } 126 127 return false; 128} 129 130export function isFunctionLikeDeclaration(node: ts.Node): node is ts.FunctionLikeDeclaration { 131 if (!node) { 132 return false; 133 } 134 135 switch (node.kind) { 136 case ts.SyntaxKind.ArrowFunction: 137 case ts.SyntaxKind.Constructor: 138 case ts.SyntaxKind.FunctionExpression: 139 case ts.SyntaxKind.FunctionDeclaration: 140 case ts.SyntaxKind.GetAccessor: 141 case ts.SyntaxKind.MethodDeclaration: 142 case ts.SyntaxKind.SetAccessor: 143 return true; 144 default: 145 return false; 146 } 147} 148 149export function allowLetAndConstDeclarations(node: ts.Node): boolean { 150 if (!node) { 151 return false; 152 } 153 154 switch (node.kind) { 155 case ts.SyntaxKind.DoStatement: 156 case ts.SyntaxKind.IfStatement: 157 case ts.SyntaxKind.ForStatement: 158 case ts.SyntaxKind.ForInStatement: 159 case ts.SyntaxKind.ForOfStatement: 160 case ts.SyntaxKind.WhileStatement: 161 case ts.SyntaxKind.WithStatement: 162 return false; 163 case ts.SyntaxKind.LabeledStatement: 164 return allowLetAndConstDeclarations(node.parent); 165 default: 166 break; 167 } 168 return true; 169} 170 171export function isGlobalIdentifier(name: string): boolean { 172 switch (name) { 173 case "NaN": 174 case "undefined": 175 case "Infinity": 176 return true; 177 default: 178 return false; 179 } 180} 181 182export function isBindingPattern(node: ts.Node | undefined): boolean { 183 if (!node) { 184 return false; 185 } 186 187 switch (node.kind) { 188 case ts.SyntaxKind.ArrayBindingPattern: 189 case ts.SyntaxKind.ObjectBindingPattern: 190 return true; 191 default: 192 return false; 193 } 194} 195 196export function visibilityToString(flag: ts.ModifierFlags): string | undefined { 197 switch (flag) { 198 case ts.ModifierFlags.Private: 199 return "private"; 200 case ts.ModifierFlags.Protected: 201 return "protected"; 202 default: 203 return "public"; 204 } 205} 206 207export function isDeclInGlobal(id: ts.Identifier): boolean { 208 let parent = id.parent; 209 while ((parent) && (parent.kind != ts.SyntaxKind.Block)) { 210 parent = parent.parent; 211 } 212 213 if (!parent) { 214 return true; 215 } 216 217 return false; 218} 219 220export function isInBlockScope(node: ts.Node): boolean { 221 switch (node.kind) { 222 case ts.SyntaxKind.SourceFile: 223 case ts.SyntaxKind.CaseBlock: 224 case ts.SyntaxKind.DefaultClause: 225 case ts.SyntaxKind.CaseClause: 226 case ts.SyntaxKind.Block: 227 case ts.SyntaxKind.Constructor: 228 case ts.SyntaxKind.MethodDeclaration: 229 return true; 230 default: 231 break; 232 } 233 234 return false; 235} 236 237export function isIncludeBackslash8Or9InString(text: string): boolean { 238 239 // \8 and \9 are not allowed in strict mode 240 let index = 0; 241 while (index < text.length) { 242 if (text[index] === '\\' && index != text.length - 1) { 243 if (text[index + 1] === "\\") { 244 index++; 245 } else if (text[index + 1] === '8' || text[index + 1] === '9') { 246 return true; 247 } 248 } 249 index++; 250 } 251 252 return false; 253} 254 255export function isOptionalParameter(node: ts.ParameterDeclaration): boolean { 256 if (jshelpers.hasQuestionToken(node)) { 257 return true; 258 } 259 260 if (!node.parent || !node.parent.parent) { 261 return false; 262 } 263 264 let iife = jshelpers.getImmediatelyInvokedFunctionExpression(node.parent); 265 if (iife) { 266 return !node.type && !node.dotDotDotToken && node.parent.parameters.indexOf(node) >= iife.arguments.length; 267 } 268 269 return false; 270} 271 272export function isStatement(kind: ts.SyntaxKind): boolean { 273 if (kind >= ts.SyntaxKind.FirstStatement && kind <= ts.SyntaxKind.LastStatement) { 274 return true; 275 } 276 277 return false; 278}