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 { CmdOptions } from "./cmdOptions"; 18import { DiagnosticCode, DiagnosticError } from "./diagnostic"; 19import { findInnerExprOfParenthesis } from "./expression/parenthesizedExpression"; 20import * as jshelpers from "./jshelpers"; 21import { checkStrictModeStatementList } from "./strictMode"; 22import { 23 isAssignmentOperator, 24 isEvalOrArgumentsIdentifier, 25 isInBlockScope, 26 isIncludeOctalEscapeSequence, 27 isLeftHandSideExpression, 28 isOctalNumber, 29 isOriginalKeyword, 30 stringLiteralIsInRegExp 31} from "./syntaxCheckHelper"; 32 33function checkDeleteStatement(node: ts.DeleteExpression) { 34 let unaryExpr = node.expression; 35 if (ts.isIdentifier(unaryExpr)) { 36 throw new DiagnosticError(unaryExpr, DiagnosticCode.A_delete_cannot_be_called_on_an_identifier_in_strict_mode); 37 } 38} 39 40function checkNumericLiteral(node: ts.NumericLiteral) { 41 let num = jshelpers.getTextOfNode(node); 42 if (!isOctalNumber(num)) { 43 return; 44 } 45 46 throw new DiagnosticError(node, DiagnosticCode.Octal_literals_are_not_allowed_in_strict_mode); 47} 48 49function checkString(node: ts.Node, text: string) { 50 51 if (isIncludeOctalEscapeSequence(text)) { 52 throw new DiagnosticError(node, DiagnosticCode.Octal_escape_sequences_are_not_allowed_in_strict_mode); 53 } 54 55 if (isIncludeOctalEscapeSequence(text)) { 56 throw new DiagnosticError(node, DiagnosticCode._8_and_9_are_not_allowed_in_strict_mode); 57 } 58} 59 60function checkStringLiteral(node: ts.StringLiteral) { 61 // Octal escape has been deprecated in ES5, but it can be used in regular expressions 62 if (stringLiteralIsInRegExp(node)) { 63 return; 64 } 65 66 let text = jshelpers.getTextOfNode(node); 67 checkString(node, text); 68} 69 70function checkEvalOrArgumentsOrOriginalKeyword(contextNode: ts.Node, name: ts.Node | undefined) { 71 if (!name || !ts.isIdentifier(name)) { 72 return; 73 } 74 75 let identifier = <ts.Identifier>name; 76 if (!isEvalOrArgumentsIdentifier(identifier) && !isOriginalKeyword(identifier)) { 77 return; 78 } 79 80 let file = jshelpers.getSourceFileOfNode(name); 81 let args = [ts.idText(identifier)]; 82 throw new DiagnosticError(name, getStrictModeEvalOrArgumentsDiagnosticCode(contextNode), file, args); 83} 84 85 86function getStrictModeEvalOrArgumentsDiagnosticCode(node: ts.Node) { 87 if (jshelpers.getContainingClass(node)) { 88 return DiagnosticCode.Invalid_use_of_0_Class_definitions_are_automatically_in_strict_mode; 89 } 90 91 return DiagnosticCode.Invalid_use_of_0_in_strict_mode; 92} 93 94function getStrictModeIdentifierDiagnosticCode(node: ts.Node) { 95 if (jshelpers.getContainingClass(node)) { 96 return DiagnosticCode.Identifier_expected_0_is_a_reserved_word_in_strict_mode_Class_definitions_are_automatically_in_strict_mode; 97 } 98 99 return DiagnosticCode.Identifier_expected_0_is_a_reserved_word_in_strict_mode; 100} 101 102function checkBinaryExpression(node: ts.BinaryExpression) { 103 if (!isLeftHandSideExpression(node.left) || !isAssignmentOperator(node.operatorToken.kind)) { 104 return; 105 } 106 107 let contextNode = <ts.Node>node; 108 let name = node.left; 109 switch (node.left.kind) { 110 case ts.SyntaxKind.ParenthesizedExpression: { 111 let expr = findInnerExprOfParenthesis(<ts.ParenthesizedExpression>(node.left)); 112 contextNode = <ts.Node>expr; 113 name = expr; 114 break; 115 } 116 default: 117 break; 118 } 119 120 checkEvalOrArgumentsOrOriginalKeyword(contextNode, <ts.Identifier>name); 121} 122 123function checkContextualIdentifier(node: ts.Identifier) { 124 let file = jshelpers.getSourceFileOfNode(node); 125 if (jshelpers.getTextOfIdentifierOrLiteral(node) == 'await' && CmdOptions.isModules()) { 126 throw new DiagnosticError(node, DiagnosticCode.Identifier_expected_0_is_a_reserved_word_at_the_top_level_of_a_module, file, ['await']); 127 } 128 129 if (jshelpers.isIdentifierName(node)) { 130 return; 131 } 132 133 if (isOriginalKeyword(node)) { 134 throw new DiagnosticError(node, getStrictModeIdentifierDiagnosticCode(node), file, jshelpers.declarationNameToString(node)); 135 } 136 137} 138 139function checkParameters(decl: ts.FunctionLikeDeclaration | ts.FunctionExpression) { 140 let parameters: ts.NodeArray<ts.ParameterDeclaration> = decl.parameters; 141 let obj = new Map(); 142 for (let i = 0; i < parameters.length; i++) { 143 let param = parameters[i]; 144 checkEvalOrArgumentsOrOriginalKeyword(param, param.name); 145 let name = jshelpers.getTextOfIdentifierOrLiteral(<ts.Identifier>param.name); 146 if (obj.has(name)) { 147 let args: (string | number)[] = [jshelpers.declarationNameToString(param.name)]; 148 throw new DiagnosticError(param.name, DiagnosticCode.Duplicate_identifier_0, undefined, args); 149 } 150 151 if (name) { 152 obj.set(name, true); 153 } 154 155 if (param.initializer || param.dotDotDotToken) { 156 if (checkStrictModeStatementList(decl)) { 157 throw new DiagnosticError(param, DiagnosticCode.use_strict_directive_cannot_be_used_with_non_simple_parameter_list); 158 } 159 } 160 } 161} 162 163function checkWithStatement(node: ts.WithStatement) { 164 let file = jshelpers.getSourceFileOfNode(node); 165 throw new DiagnosticError(node, DiagnosticCode.A_with_statements_are_not_allowed_in_strict_mode, file); 166} 167 168function checkNoSubstitutionTemplateLiteral(expr: ts.NoSubstitutionTemplateLiteral) { 169 let text = jshelpers.getTextOfNode(expr); 170 checkString(expr, text.substring(1, text.length - 1)); 171} 172 173function checkFunctionDeclaration(node: ts.FunctionDeclaration) { 174 175 checkEvalOrArgumentsOrOriginalKeyword(node, node.name); 176 checkParameters(node); 177 if (!isInBlockScope(node.parent!)) { 178 throw new DiagnosticError(node, DiagnosticCode.In_strict_mode_code_functions_can_only_be_declared_at_top_level_or_inside_a_block); 179 } 180} 181 182export function checkSyntaxErrorForStrictMode(node: ts.Node) { 183 switch (node.kind) { 184 case ts.SyntaxKind.NumericLiteral: 185 checkNumericLiteral(<ts.NumericLiteral>node); 186 break; 187 case ts.SyntaxKind.StringLiteral: 188 checkStringLiteral(<ts.StringLiteral>node); 189 break; 190 case ts.SyntaxKind.FunctionDeclaration: 191 checkFunctionDeclaration(<ts.FunctionDeclaration>node); 192 break; 193 case ts.SyntaxKind.FunctionExpression: 194 let funcNode = <ts.FunctionExpression>node; 195 checkEvalOrArgumentsOrOriginalKeyword(funcNode, funcNode.name); 196 checkParameters(funcNode); 197 break; 198 case ts.SyntaxKind.SetAccessor: 199 case ts.SyntaxKind.ArrowFunction: 200 checkParameters(<ts.FunctionLikeDeclaration | ts.FunctionExpression>node); 201 break; 202 case ts.SyntaxKind.VariableDeclaration: 203 let varNode = <ts.VariableDeclaration>node; 204 checkEvalOrArgumentsOrOriginalKeyword(varNode, varNode.name); 205 break; 206 case ts.SyntaxKind.BindingElement: 207 let bindNode = <ts.BindingElement>node; 208 checkEvalOrArgumentsOrOriginalKeyword(node, bindNode.name); 209 break; 210 case ts.SyntaxKind.BinaryExpression: 211 checkBinaryExpression(<ts.BinaryExpression>node); 212 break; 213 case ts.SyntaxKind.PrefixUnaryExpression: 214 case ts.SyntaxKind.PostfixUnaryExpression: 215 let unaryNode = <ts.PrefixUnaryExpression | ts.PostfixUnaryExpression>node; 216 checkEvalOrArgumentsOrOriginalKeyword(node, unaryNode.operand); 217 break; 218 case ts.SyntaxKind.DeleteExpression: 219 checkDeleteStatement(<ts.DeleteExpression>node); 220 break; 221 case ts.SyntaxKind.WithStatement: 222 checkWithStatement(<ts.WithStatement>node); 223 break; 224 case ts.SyntaxKind.Identifier: 225 checkContextualIdentifier(<ts.Identifier>node); 226 break; 227 case ts.SyntaxKind.NoSubstitutionTemplateLiteral: 228 case ts.SyntaxKind.FirstTemplateToken: 229 case ts.SyntaxKind.LastLiteralToken: 230 checkNoSubstitutionTemplateLiteral(<ts.NoSubstitutionTemplateLiteral>node); 231 break; 232 default: 233 break; 234 } 235}