• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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}