1/* 2 * Copyright (c) 2021-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 { hasDefaultKeywordModifier, hasExportKeywordModifier } from "./base/util"; 18import { CmdOptions } from "./cmdOptions"; 19import { DiagnosticCode, DiagnosticError } from "./diagnostic"; 20import { findInnerExprOfParenthesis } from "./expression/parenthesizedExpression"; 21import * as jshelpers from "./jshelpers"; 22import { ModuleScope, Scope } from "./scope"; 23import { checkStrictModeStatementList } from "./strictMode"; 24import { 25 isAssignmentOperator, 26 isEvalOrArgumentsIdentifier, 27 isInBlockScope, 28 isIncludeOctalEscapeSequence, 29 isLeftHandSideExpression, 30 isOctalNumber, 31 isOriginalKeyword, 32 stringLiteralIsInRegExp 33} from "./syntaxCheckHelper"; 34 35function checkDeleteStatement(node: ts.DeleteExpression) { 36 let unaryExpr = node.expression; 37 if (ts.isIdentifier(unaryExpr)) { 38 throw new DiagnosticError(unaryExpr, DiagnosticCode.A_delete_cannot_be_called_on_an_identifier_in_strict_mode); 39 } 40} 41 42function checkNumericLiteral(node: ts.NumericLiteral) { 43 let num = jshelpers.getTextOfNode(node); 44 if (!isOctalNumber(num)) { 45 return; 46 } 47 48 throw new DiagnosticError(node, DiagnosticCode.Octal_literals_are_not_allowed_in_strict_mode); 49} 50 51function checkString(node: ts.Node, text: string) { 52 53 if (isIncludeOctalEscapeSequence(text)) { 54 throw new DiagnosticError(node, DiagnosticCode.Octal_escape_sequences_are_not_allowed_in_strict_mode); 55 } 56 57 if (isIncludeOctalEscapeSequence(text)) { 58 throw new DiagnosticError(node, DiagnosticCode._8_and_9_are_not_allowed_in_strict_mode); 59 } 60} 61 62function checkStringLiteral(node: ts.StringLiteral) { 63 // Octal escape has been deprecated in ES5, but it can be used in regular expressions 64 if (stringLiteralIsInRegExp(node)) { 65 return; 66 } 67 68 let text = jshelpers.getTextOfNode(node); 69 checkString(node, text); 70} 71 72function checkEvalOrArgumentsOrOriginalKeyword(contextNode: ts.Node, name: ts.Node | undefined) { 73 if (!name || !ts.isIdentifier(name)) { 74 return; 75 } 76 77 let identifier = <ts.Identifier>name; 78 if (!isEvalOrArgumentsIdentifier(identifier) && !isOriginalKeyword(identifier)) { 79 return; 80 } 81 82 let file = jshelpers.getSourceFileOfNode(name); 83 let args = [ts.idText(identifier)]; 84 throw new DiagnosticError(name, getStrictModeEvalOrArgumentsDiagnosticCode(contextNode), file, args); 85} 86 87 88function getStrictModeEvalOrArgumentsDiagnosticCode(node: ts.Node) { 89 if (jshelpers.getContainingClass(node)) { 90 return DiagnosticCode.Invalid_use_of_0_Class_definitions_are_automatically_in_strict_mode; 91 } 92 93 return DiagnosticCode.Invalid_use_of_0_in_strict_mode; 94} 95 96function getStrictModeIdentifierDiagnosticCode(node: ts.Node) { 97 if (jshelpers.getContainingClass(node)) { 98 return DiagnosticCode.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Class_definitions_are_automatically_in_strict_mode; 99 } 100 101 return DiagnosticCode.Identifier_expected_0_is_a_reserved_word_in_strict_mode; 102} 103 104function checkBinaryExpression(node: ts.BinaryExpression) { 105 if (!isLeftHandSideExpression(node.left) || !isAssignmentOperator(node.operatorToken.kind)) { 106 return; 107 } 108 109 let contextNode = <ts.Node>node; 110 let name = node.left; 111 switch (node.left.kind) { 112 case ts.SyntaxKind.ParenthesizedExpression: { 113 let expr = findInnerExprOfParenthesis(<ts.ParenthesizedExpression>(node.left)); 114 contextNode = <ts.Node>expr; 115 name = expr; 116 break; 117 } 118 default: 119 break; 120 } 121 122 checkEvalOrArgumentsOrOriginalKeyword(contextNode, <ts.Identifier>name); 123} 124 125function checkContextualIdentifier(node: ts.Identifier) { 126 let file = jshelpers.getSourceFileOfNode(node); 127 if (jshelpers.getTextOfIdentifierOrLiteral(node) == 'await' && CmdOptions.isModules()) { 128 throw new DiagnosticError(node, DiagnosticCode.Identifier_expected_0_is_a_reserved_word_at_the_top_level_of_a_module, file, ['await']); 129 } 130 131 if (jshelpers.isIdentifierName(node)) { 132 return; 133 } 134 135 if (isOriginalKeyword(node)) { 136 throw new DiagnosticError(node, getStrictModeIdentifierDiagnosticCode(node), file, jshelpers.declarationNameToString(node)); 137 } 138 139} 140 141function checkParameters(decl: ts.FunctionLikeDeclaration | ts.FunctionExpression) { 142 let parameters: ts.NodeArray<ts.ParameterDeclaration> = decl.parameters; 143 let obj = new Map(); 144 for (let i = 0; i < parameters.length; i++) { 145 let param = parameters[i]; 146 checkEvalOrArgumentsOrOriginalKeyword(param, param.name); 147 let name = jshelpers.getTextOfIdentifierOrLiteral(<ts.Identifier>param.name); 148 if (obj.has(name)) { 149 let args: (string | number)[] = [jshelpers.declarationNameToString(param.name)]; 150 throw new DiagnosticError(param.name, DiagnosticCode.Duplicate_identifier_0, undefined, args); 151 } 152 153 if (name) { 154 obj.set(name, true); 155 } 156 157 if (param.initializer || param.dotDotDotToken) { 158 if (checkStrictModeStatementList(decl)) { 159 throw new DiagnosticError(param, DiagnosticCode.use_strict_directive_cannot_be_used_with_non_simple_parameter_list); 160 } 161 } 162 } 163} 164 165function checkWithStatement(node: ts.WithStatement) { 166 let file = jshelpers.getSourceFileOfNode(node); 167 throw new DiagnosticError(node, DiagnosticCode.A_with_statements_are_not_allowed_in_strict_mode, file); 168} 169 170function checkNoSubstitutionTemplateLiteral(expr: ts.NoSubstitutionTemplateLiteral) { 171 let text = jshelpers.getTextOfNode(expr); 172 checkString(expr, text.substring(1, text.length - 1)); 173} 174 175function checkFunctionDeclaration(node: ts.FunctionDeclaration) { 176 checkEvalOrArgumentsOrOriginalKeyword(node, node.name); 177 checkParameters(node); 178 if (!isInBlockScope(node.parent!)) { 179 throw new DiagnosticError(node, DiagnosticCode.In_strict_mode_code_functions_can_only_be_declared_at_top_level_or_inside_a_block); 180 } 181} 182 183function checkClassDeclaration(node: ts.ClassDeclaration) { 184 if (!hasExportKeywordModifier(node) && !node.name) { 185 if (!node.name && !hasDefaultKeywordModifier(node)) { 186 throw new DiagnosticError(node, DiagnosticCode.Identifier_expected); 187 } 188 } 189} 190 191function checkImportDeclaration(node: ts.ImportDeclaration, scope: Scope) { 192 if (!(scope instanceof ModuleScope)) { 193 throw new DiagnosticError(node, DiagnosticCode.An_import_declaration_can_only_be_used_in_a_namespace_or_module); 194 } 195 196 if (node.modifiers) { 197 throw new DiagnosticError(node, DiagnosticCode.An_import_declaration_cannot_have_modifiers); 198 } 199 200 if (node.importClause && node.importClause.namedBindings) { 201 let namedBindings = node.importClause.namedBindings; 202 if (ts.isNamedImports(namedBindings)) { 203 namedBindings.elements.forEach((element: any) => { 204 if (jshelpers.getTextOfIdentifierOrLiteral(element.name) == 'arguments' 205 || jshelpers.getTextOfIdentifierOrLiteral(element.name) == 'eval') { 206 throw new DiagnosticError(node, DiagnosticCode.Unexpected_eval_or_arguments_in_strict_mode); 207 } 208 }); 209 } 210 } 211} 212 213function checkExportAssignment(node: ts.ExportAssignment, scope: Scope) { 214 if (!(scope instanceof ModuleScope)) { 215 throw new DiagnosticError(node, DiagnosticCode.An_export_assignment_must_be_at_the_top_level_of_a_file_or_module_declaration); 216 } 217 218 if (node.modifiers) { 219 throw new DiagnosticError(node, DiagnosticCode.An_export_assignment_cannot_have_modifiers); 220 } 221} 222 223function checkExportDeclaration(node: ts.ExportDeclaration, scope: Scope) { 224 if (!(scope instanceof ModuleScope)) { 225 throw new DiagnosticError(node, DiagnosticCode.An_export_declaration_can_only_be_used_in_a_module); 226 } 227 228 if (node.modifiers) { 229 throw new DiagnosticError(node, DiagnosticCode.An_export_declaration_cannot_have_modifiers); 230 } 231} 232 233export function checkSyntaxErrorForStrictMode(node: ts.Node, scope: Scope) { 234 switch (node.kind) { 235 case ts.SyntaxKind.NumericLiteral: 236 checkNumericLiteral(<ts.NumericLiteral>node); 237 break; 238 case ts.SyntaxKind.StringLiteral: 239 checkStringLiteral(<ts.StringLiteral>node); 240 break; 241 case ts.SyntaxKind.FunctionDeclaration: 242 checkFunctionDeclaration(<ts.FunctionDeclaration>node); 243 break; 244 case ts.SyntaxKind.FunctionExpression: 245 let funcNode = <ts.FunctionExpression>node; 246 checkEvalOrArgumentsOrOriginalKeyword(funcNode, funcNode.name); 247 checkParameters(funcNode); 248 break; 249 case ts.SyntaxKind.SetAccessor: 250 case ts.SyntaxKind.ArrowFunction: 251 checkParameters(<ts.FunctionLikeDeclaration | ts.FunctionExpression>node); 252 break; 253 case ts.SyntaxKind.ClassDeclaration: 254 checkClassDeclaration(<ts.ClassDeclaration>node); 255 break; 256 case ts.SyntaxKind.VariableDeclaration: 257 let varNode = <ts.VariableDeclaration>node; 258 checkEvalOrArgumentsOrOriginalKeyword(varNode, varNode.name); 259 break; 260 case ts.SyntaxKind.BindingElement: 261 let bindNode = <ts.BindingElement>node; 262 checkEvalOrArgumentsOrOriginalKeyword(node, bindNode.name); 263 break; 264 case ts.SyntaxKind.BinaryExpression: 265 checkBinaryExpression(<ts.BinaryExpression>node); 266 break; 267 case ts.SyntaxKind.PrefixUnaryExpression: 268 case ts.SyntaxKind.PostfixUnaryExpression: 269 let unaryNode = <ts.PrefixUnaryExpression | ts.PostfixUnaryExpression>node; 270 checkEvalOrArgumentsOrOriginalKeyword(node, unaryNode.operand); 271 break; 272 case ts.SyntaxKind.DeleteExpression: 273 checkDeleteStatement(<ts.DeleteExpression>node); 274 break; 275 case ts.SyntaxKind.WithStatement: 276 checkWithStatement(<ts.WithStatement>node); 277 break; 278 case ts.SyntaxKind.Identifier: 279 checkContextualIdentifier(<ts.Identifier>node); 280 break; 281 case ts.SyntaxKind.NoSubstitutionTemplateLiteral: 282 case ts.SyntaxKind.FirstTemplateToken: 283 case ts.SyntaxKind.LastLiteralToken: 284 checkNoSubstitutionTemplateLiteral(<ts.NoSubstitutionTemplateLiteral>node); 285 break; 286 case ts.SyntaxKind.ImportDeclaration: 287 checkImportDeclaration(<ts.ImportDeclaration>node, scope); 288 break; 289 case ts.SyntaxKind.ExportAssignment: 290 checkExportAssignment(<ts.ExportAssignment>node, scope); 291 break; 292 case ts.SyntaxKind.ExportDeclaration: 293 checkExportDeclaration(<ts.ExportDeclaration>node, scope); 294 break; 295 default: 296 break; 297 } 298} 299