/** * Copyright 2014 Shape Security, Inc. * * Licensed under the Apache License, Version 2.0 (the "License") * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ const { reduce, MonoidalReducer } = require('shift-reducer'); const { isStrictModeReservedWord } = require('./utils'); const { ErrorMessages } = require('./errors'); const { EarlyErrorState, EarlyError } = require('./early-error-state'); function isStrictFunctionBody({ directives }) { return directives.some(directive => directive.rawValue === 'use strict'); } function isLabelledFunction(node) { return node.type === 'LabeledStatement' && (node.body.type === 'FunctionDeclaration' || isLabelledFunction(node.body)); } function isIterationStatement(node) { switch (node.type) { case 'LabeledStatement': return isIterationStatement(node.body); case 'DoWhileStatement': case 'ForInStatement': case 'ForOfStatement': case 'ForStatement': case 'WhileStatement': return true; } return false; } function isSpecialMethod(methodDefinition) { if (methodDefinition.name.type !== 'StaticPropertyName' || methodDefinition.name.value !== 'constructor') { return false; } switch (methodDefinition.type) { case 'Getter': case 'Setter': return true; case 'Method': return methodDefinition.isGenerator || methodDefinition.isAsync; } /* istanbul ignore next */ throw new Error('not reached'); } function enforceDuplicateConstructorMethods(node, s) { let ctors = node.elements.filter(e => !e.isStatic && e.method.type === 'Method' && !e.method.isGenerator && e.method.name.type === 'StaticPropertyName' && e.method.name.value === 'constructor' ); if (ctors.length > 1) { ctors.slice(1).forEach(ctor => { s = s.addError(new EarlyError(ctor, 'Duplicate constructor method in class')); }); } return s; } const SUPERCALL_ERROR = node => new EarlyError(node, ErrorMessages.ILLEGAL_SUPER_CALL); const SUPERPROPERTY_ERROR = node => new EarlyError(node, 'Member access on super must be in a method'); const DUPLICATE_BINDING = node => new EarlyError(node, `Duplicate binding ${JSON.stringify(node.name)}`); const FREE_CONTINUE = node => new EarlyError(node, 'Continue statement must be nested within an iteration statement'); const UNBOUND_CONTINUE = node => new EarlyError(node, `Continue statement must be nested within an iteration statement with label ${JSON.stringify(node.label)}`); const FREE_BREAK = node => new EarlyError(node, 'Break statement must be nested within an iteration statement or a switch statement'); const UNBOUND_BREAK = node => new EarlyError(node, `Break statement must be nested within a statement with label ${JSON.stringify(node.label)}`); class EarlyErrorChecker extends MonoidalReducer { constructor() { super(EarlyErrorState); } reduceAssignmentExpression() { return super.reduceAssignmentExpression(...arguments).clearBoundNames(); } reduceAssignmentTargetIdentifier(node) { let s = this.identity; if (node.name === 'eval' || node.name === 'arguments' || isStrictModeReservedWord(node.name)) { s = s.addStrictError(new EarlyError(node, `The identifier ${JSON.stringify(node.name)} must not be in binding position in strict mode`)); } return s; } reduceArrowExpression(node, { params, body }) { let isSimpleParameterList = node.params.rest == null && node.params.items.every(i => i.type === 'BindingIdentifier'); params = params.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); if (node.body.type === 'FunctionBody') { body = body.enforceConflictingLexicallyDeclaredNames(params.lexicallyDeclaredNames, DUPLICATE_BINDING); if (isStrictFunctionBody(node.body)) { params = params.enforceStrictErrors(); body = body.enforceStrictErrors(); } } params.yieldExpressions.forEach(n => { params = params.addError(new EarlyError(n, 'Arrow parameters must not contain yield expressions')); }); params.awaitExpressions.forEach(n => { params = params.addError(new EarlyError(n, 'Arrow parameters must not contain await expressions')); }); let s = super.reduceArrowExpression(node, { params, body }); if (!isSimpleParameterList && node.body.type === 'FunctionBody' && isStrictFunctionBody(node.body)) { s = s.addError(new EarlyError(node, 'Functions with non-simple parameter lists may not contain a "use strict" directive')); } s = s.clearYieldExpressions(); s = s.clearAwaitExpressions(); s = s.observeVarBoundary(); return s; } reduceAwaitExpression(node, { expression }) { return expression.observeAwaitExpression(node); } reduceBindingIdentifier(node) { let s = this.identity; if (node.name === 'eval' || node.name === 'arguments' || isStrictModeReservedWord(node.name)) { s = s.addStrictError(new EarlyError(node, `The identifier ${JSON.stringify(node.name)} must not be in binding position in strict mode`)); } s = s.bindName(node.name, node); return s; } reduceBlock() { let s = super.reduceBlock(...arguments); s = s.functionDeclarationNamesAreLexical(); s = s.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); s = s.enforceConflictingLexicallyDeclaredNames(s.varDeclaredNames, DUPLICATE_BINDING); s = s.observeLexicalBoundary(); return s; } reduceBreakStatement(node) { let s = super.reduceBreakStatement(...arguments); s = node.label == null ? s.addFreeBreakStatement(node) : s.addFreeLabeledBreakStatement(node); return s; } reduceCallExpression(node) { let s = super.reduceCallExpression(...arguments); if (node.callee.type === 'Super') { s = s.observeSuperCallExpression(node); } return s; } reduceCatchClause(node, { binding, body }) { if (binding != null) { binding = binding.observeLexicalDeclaration(); binding = binding.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); binding = binding.enforceConflictingLexicallyDeclaredNames(body.previousLexicallyDeclaredNames, DUPLICATE_BINDING); } let s = super.reduceCatchClause(node, { binding, body }); s = s.observeLexicalBoundary(); return s; } reduceClassDeclaration(node, { name, super: _super, elements }) { let s = name.enforceStrictErrors(); let sElements = this.append(...elements); sElements = sElements.enforceStrictErrors(); if (node.super != null) { _super = _super.enforceStrictErrors(); s = this.append(s, _super); sElements = sElements.clearSuperCallExpressionsInConstructorMethod(); } s = this.append(s, sElements); s = enforceDuplicateConstructorMethods(node, s); s = s.observeLexicalDeclaration(); return s; } reduceClassElement(node) { let s = super.reduceClassElement(...arguments); if (!node.isStatic && isSpecialMethod(node.method)) { s = s.addError(new EarlyError(node, ErrorMessages.ILLEGAL_CONSTRUCTORS)); } if (node.isStatic && node.method.name.type === 'StaticPropertyName' && node.method.name.value === 'prototype') { s = s.addError(new EarlyError(node, 'Static class methods cannot be named "prototype"')); } return s; } reduceClassExpression(node, { name, super: _super, elements }) { let s = node.name == null ? this.identity : name.enforceStrictErrors(); let sElements = this.append(...elements); sElements = sElements.enforceStrictErrors(); if (node.super != null) { _super = _super.enforceStrictErrors(); s = this.append(s, _super); sElements = sElements.clearSuperCallExpressionsInConstructorMethod(); } s = this.append(s, sElements); s = enforceDuplicateConstructorMethods(node, s); s = s.clearBoundNames(); return s; } reduceCompoundAssignmentExpression() { return super.reduceCompoundAssignmentExpression(...arguments).clearBoundNames(); } reduceComputedMemberExpression(node) { let s = super.reduceComputedMemberExpression(...arguments); if (node.object.type === 'Super') { s = s.observeSuperPropertyExpression(node); } return s; } reduceContinueStatement(node) { let s = super.reduceContinueStatement(...arguments); s = node.label == null ? s.addFreeContinueStatement(node) : s.addFreeLabeledContinueStatement(node); return s; } reduceDoWhileStatement(node) { let s = super.reduceDoWhileStatement(...arguments); if (isLabelledFunction(node.body)) { s = s.addError(new EarlyError(node.body, 'The body of a do-while statement must not be a labeled function declaration')); } s = s.clearFreeContinueStatements(); s = s.clearFreeBreakStatements(); return s; } reduceExport() { let s = super.reduceExport(...arguments); s = s.functionDeclarationNamesAreLexical(); s = s.exportDeclaredNames(); return s; } reduceExportFrom() { let s = super.reduceExportFrom(...arguments); s = s.clearExportedBindings(); return s; } reduceExportFromSpecifier(node) { let s = super.reduceExportFromSpecifier(...arguments); s = s.exportName(node.exportedName || node.name, node); s = s.exportBinding(node.name, node); return s; } reduceExportLocalSpecifier(node) { let s = super.reduceExportLocalSpecifier(...arguments); s = s.exportName(node.exportedName || node.name.name, node); s = s.exportBinding(node.name.name, node); return s; } reduceExportDefault(node) { let s = super.reduceExportDefault(...arguments); s = s.functionDeclarationNamesAreLexical(); s = s.exportName('default', node); return s; } reduceFormalParameters() { let s = super.reduceFormalParameters(...arguments); s = s.observeLexicalDeclaration(); return s; } reduceForStatement(node, { init, test, update, body }) { if (init != null) { init = init.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); init = init.enforceConflictingLexicallyDeclaredNames(body.varDeclaredNames, DUPLICATE_BINDING); } let s = super.reduceForStatement(node, { init, test, update, body }); if (node.init != null && node.init.type === 'VariableDeclaration' && node.init.kind === 'const') { node.init.declarators.forEach(declarator => { if (declarator.init == null) { s = s.addError(new EarlyError(declarator, 'Constant lexical declarations must have an initialiser')); } }); } if (isLabelledFunction(node.body)) { s = s.addError(new EarlyError(node.body, 'The body of a for statement must not be a labeled function declaration')); } s = s.clearFreeContinueStatements(); s = s.clearFreeBreakStatements(); s = s.observeLexicalBoundary(); return s; } reduceForInStatement(node, { left, right, body }) { left = left.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); left = left.enforceConflictingLexicallyDeclaredNames(body.varDeclaredNames, DUPLICATE_BINDING); let s = super.reduceForInStatement(node, { left, right, body }); if (isLabelledFunction(node.body)) { s = s.addError(new EarlyError(node.body, 'The body of a for-in statement must not be a labeled function declaration')); } s = s.clearFreeContinueStatements(); s = s.clearFreeBreakStatements(); s = s.observeLexicalBoundary(); return s; } reduceForOfStatement(node, { left, right, body }) { left = left.recordForOfVars(); left = left.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); left = left.enforceConflictingLexicallyDeclaredNames(body.varDeclaredNames, DUPLICATE_BINDING); let s = super.reduceForOfStatement(node, { left, right, body }); if (isLabelledFunction(node.body)) { s = s.addError(new EarlyError(node.body, 'The body of a for-of statement must not be a labeled function declaration')); } s = s.clearFreeContinueStatements(); s = s.clearFreeBreakStatements(); s = s.observeLexicalBoundary(); return s; } reduceForAwaitStatement(node, { left, right, body }) { left = left.recordForOfVars(); left = left.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); left = left.enforceConflictingLexicallyDeclaredNames(body.varDeclaredNames, DUPLICATE_BINDING); let s = super.reduceForOfStatement(node, { left, right, body }); if (isLabelledFunction(node.body)) { s = s.addError(new EarlyError(node.body, 'The body of a for-await statement must not be a labeled function declaration')); } s = s.clearFreeContinueStatements(); s = s.clearFreeBreakStatements(); s = s.observeLexicalBoundary(); return s; } reduceFunctionBody(node) { let s = super.reduceFunctionBody(...arguments); s = s.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); s = s.enforceConflictingLexicallyDeclaredNames(s.varDeclaredNames, DUPLICATE_BINDING); s = s.enforceFreeContinueStatementErrors(FREE_CONTINUE); s = s.enforceFreeLabeledContinueStatementErrors(UNBOUND_CONTINUE); s = s.enforceFreeBreakStatementErrors(FREE_BREAK); s = s.enforceFreeLabeledBreakStatementErrors(UNBOUND_BREAK); s = s.clearUsedLabelNames(); s = s.clearYieldExpressions(); s = s.clearAwaitExpressions(); if (isStrictFunctionBody(node)) { s = s.enforceStrictErrors(); } return s; } reduceFunctionDeclaration(node, { name, params, body }) { let isSimpleParameterList = node.params.rest == null && node.params.items.every(i => i.type === 'BindingIdentifier'); let addError = !isSimpleParameterList || node.isGenerator ? 'addError' : 'addStrictError'; params.lexicallyDeclaredNames.forEachEntry(nodes => { if (nodes.length > 1) { nodes.slice(1).forEach(dupeNode => { params = params[addError](DUPLICATE_BINDING(dupeNode)); }); } }); body = body.enforceConflictingLexicallyDeclaredNames(params.lexicallyDeclaredNames, DUPLICATE_BINDING); body = body.enforceSuperCallExpressions(SUPERCALL_ERROR); body = body.enforceSuperPropertyExpressions(SUPERPROPERTY_ERROR); params = params.enforceSuperCallExpressions(SUPERCALL_ERROR); params = params.enforceSuperPropertyExpressions(SUPERPROPERTY_ERROR); if (node.isGenerator) { params.yieldExpressions.forEach(n => { params = params.addError(new EarlyError(n, 'Generator parameters must not contain yield expressions')); }); } if (node.isAsync) { params.awaitExpressions.forEach(n => { params = params.addError(new EarlyError(n, 'Async function parameters must not contain await expressions')); }); } params = params.clearNewTargetExpressions(); body = body.clearNewTargetExpressions(); if (isStrictFunctionBody(node.body)) { params = params.enforceStrictErrors(); body = body.enforceStrictErrors(); } let s = super.reduceFunctionDeclaration(node, { name, params, body }); if (!isSimpleParameterList && isStrictFunctionBody(node.body)) { s = s.addError(new EarlyError(node, 'Functions with non-simple parameter lists may not contain a "use strict" directive')); } s = s.clearYieldExpressions(); s = s.clearAwaitExpressions(); s = s.observeFunctionDeclaration(); return s; } reduceFunctionExpression(node, { name, params, body }) { let isSimpleParameterList = node.params.rest == null && node.params.items.every(i => i.type === 'BindingIdentifier'); let addError = !isSimpleParameterList || node.isGenerator ? 'addError' : 'addStrictError'; params.lexicallyDeclaredNames.forEachEntry((nodes, bindingName) => { if (nodes.length > 1) { nodes.slice(1).forEach(dupeNode => { params = params[addError](new EarlyError(dupeNode, `Duplicate binding ${JSON.stringify(bindingName)}`)); }); } }); body = body.enforceConflictingLexicallyDeclaredNames(params.lexicallyDeclaredNames, DUPLICATE_BINDING); body = body.enforceSuperCallExpressions(SUPERCALL_ERROR); body = body.enforceSuperPropertyExpressions(SUPERPROPERTY_ERROR); params = params.enforceSuperCallExpressions(SUPERCALL_ERROR); params = params.enforceSuperPropertyExpressions(SUPERPROPERTY_ERROR); if (node.isGenerator) { params.yieldExpressions.forEach(n => { params = params.addError(new EarlyError(n, 'Generator parameters must not contain yield expressions')); }); } if (node.isAsync) { params.awaitExpressions.forEach(n => { params = params.addError(new EarlyError(n, 'Async function parameters must not contain await expressions')); }); } params = params.clearNewTargetExpressions(); body = body.clearNewTargetExpressions(); if (isStrictFunctionBody(node.body)) { params = params.enforceStrictErrors(); body = body.enforceStrictErrors(); } let s = super.reduceFunctionExpression(node, { name, params, body }); if (!isSimpleParameterList && isStrictFunctionBody(node.body)) { s = s.addError(new EarlyError(node, 'Functions with non-simple parameter lists may not contain a "use strict" directive')); } s = s.clearBoundNames(); s = s.clearYieldExpressions(); s = s.clearAwaitExpressions(); s = s.observeVarBoundary(); return s; } reduceGetter(node, { name, body }) { body = body.enforceSuperCallExpressions(SUPERCALL_ERROR); body = body.clearSuperPropertyExpressions(); body = body.clearNewTargetExpressions(); if (isStrictFunctionBody(node.body)) { body = body.enforceStrictErrors(); } let s = super.reduceGetter(node, { name, body }); s = s.observeVarBoundary(); return s; } reduceIdentifierExpression(node) { let s = this.identity; if (isStrictModeReservedWord(node.name)) { s = s.addStrictError(new EarlyError(node, `The identifier ${JSON.stringify(node.name)} must not be in expression position in strict mode`)); } return s; } reduceIfStatement(node, { test, consequent, alternate }) { if (isLabelledFunction(node.consequent)) { consequent = consequent.addError(new EarlyError(node.consequent, 'The consequent of an if statement must not be a labeled function declaration')); } if (node.alternate != null && isLabelledFunction(node.alternate)) { alternate = alternate.addError(new EarlyError(node.alternate, 'The alternate of an if statement must not be a labeled function declaration')); } if (node.consequent.type === 'FunctionDeclaration') { consequent = consequent.addStrictError(new EarlyError(node.consequent, 'FunctionDeclarations in IfStatements are disallowed in strict mode')); consequent = consequent.observeLexicalBoundary(); } if (node.alternate != null && node.alternate.type === 'FunctionDeclaration') { alternate = alternate.addStrictError(new EarlyError(node.alternate, 'FunctionDeclarations in IfStatements are disallowed in strict mode')); alternate = alternate.observeLexicalBoundary(); } return super.reduceIfStatement(node, { test, consequent, alternate }); } reduceImport() { let s = super.reduceImport(...arguments); s = s.observeLexicalDeclaration(); return s; } reduceImportNamespace() { let s = super.reduceImportNamespace(...arguments); s = s.observeLexicalDeclaration(); return s; } reduceLabeledStatement(node) { let s = super.reduceLabeledStatement(...arguments); if (node.label === 'yield' || isStrictModeReservedWord(node.label)) { s = s.addStrictError(new EarlyError(node, `The identifier ${JSON.stringify(node.label)} must not be in label position in strict mode`)); } if (s.usedLabelNames.indexOf(node.label) >= 0) { s = s.addError(new EarlyError(node, `Label ${JSON.stringify(node.label)} has already been declared`)); } if (node.body.type === 'FunctionDeclaration') { s = s.addStrictError(new EarlyError(node, 'Labeled FunctionDeclarations are disallowed in strict mode')); } s = isIterationStatement(node.body) ? s.observeIterationLabel(node.label) : s.observeNonIterationLabel(node.label); return s; } reduceLiteralRegExpExpression() { let s = this.identity; // NOTE: the RegExp pattern acceptor is disabled until we have more confidence in its correctness (more tests) // if (!PatternAcceptor.test(node.pattern, node.flags.indexOf("u") >= 0)) { // s = s.addError(new EarlyError(node, "Invalid regular expression pattern")); // } return s; } reduceMethod(node, { name, params, body }) { let isSimpleParameterList = node.params.rest == null && node.params.items.every(i => i.type === 'BindingIdentifier'); params = params.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); body = body.enforceConflictingLexicallyDeclaredNames(params.lexicallyDeclaredNames, DUPLICATE_BINDING); if (node.name.type === 'StaticPropertyName' && node.name.value === 'constructor') { body = body.observeConstructorMethod(); params = params.observeConstructorMethod(); } else { body = body.enforceSuperCallExpressions(SUPERCALL_ERROR); params = params.enforceSuperCallExpressions(SUPERCALL_ERROR); } if (node.isGenerator) { params.yieldExpressions.forEach(n => { params = params.addError(new EarlyError(n, 'Generator parameters must not contain yield expressions')); }); } if (node.isAsync) { params.awaitExpressions.forEach(n => { params = params.addError(new EarlyError(n, 'Async function parameters must not contain await expressions')); }); } body = body.clearSuperPropertyExpressions(); params = params.clearSuperPropertyExpressions(); params = params.clearNewTargetExpressions(); body = body.clearNewTargetExpressions(); if (isStrictFunctionBody(node.body)) { params = params.enforceStrictErrors(); body = body.enforceStrictErrors(); } let s = super.reduceMethod(node, { name, params, body }); if (!isSimpleParameterList && isStrictFunctionBody(node.body)) { s = s.addError(new EarlyError(node, 'Functions with non-simple parameter lists may not contain a "use strict" directive')); } s = s.clearYieldExpressions(); s = s.clearAwaitExpressions(); s = s.observeVarBoundary(); return s; } reduceModule() { let s = super.reduceModule(...arguments); s = s.functionDeclarationNamesAreLexical(); s = s.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); s = s.enforceConflictingLexicallyDeclaredNames(s.varDeclaredNames, DUPLICATE_BINDING); s.exportedNames.forEachEntry((nodes, bindingName) => { if (nodes.length > 1) { nodes.slice(1).forEach(dupeNode => { s = s.addError(new EarlyError(dupeNode, `Duplicate export ${JSON.stringify(bindingName)}`)); }); } }); s.exportedBindings.forEachEntry((nodes, bindingName) => { if (!s.lexicallyDeclaredNames.has(bindingName) && !s.varDeclaredNames.has(bindingName)) { nodes.forEach(undeclaredNode => { s = s.addError(new EarlyError(undeclaredNode, `Exported binding ${JSON.stringify(bindingName)} is not declared`)); }); } }); s.newTargetExpressions.forEach(node => { s = s.addError(new EarlyError(node, 'new.target must be within function (but not arrow expression) code')); }); s = s.enforceFreeContinueStatementErrors(FREE_CONTINUE); s = s.enforceFreeLabeledContinueStatementErrors(UNBOUND_CONTINUE); s = s.enforceFreeBreakStatementErrors(FREE_BREAK); s = s.enforceFreeLabeledBreakStatementErrors(UNBOUND_BREAK); s = s.enforceSuperCallExpressions(SUPERCALL_ERROR); s = s.enforceSuperPropertyExpressions(SUPERPROPERTY_ERROR); s = s.enforceStrictErrors(); return s; } reduceNewTargetExpression(node) { return this.identity.observeNewTargetExpression(node); } reduceObjectExpression(node) { let s = super.reduceObjectExpression(...arguments); s = s.enforceSuperCallExpressionsInConstructorMethod(SUPERCALL_ERROR); let protos = node.properties.filter(p => p.type === 'DataProperty' && p.name.type === 'StaticPropertyName' && p.name.value === '__proto__'); protos.slice(1).forEach(n => { s = s.addError(new EarlyError(n, 'Duplicate __proto__ property in object literal not allowed')); }); return s; } reduceUpdateExpression() { let s = super.reduceUpdateExpression(...arguments); s = s.clearBoundNames(); return s; } reduceUnaryExpression(node) { let s = super.reduceUnaryExpression(...arguments); if (node.operator === 'delete' && node.operand.type === 'IdentifierExpression') { s = s.addStrictError(new EarlyError(node, 'Identifier expressions must not be deleted in strict mode')); } return s; } reduceScript(node) { let s = super.reduceScript(...arguments); s = s.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); s = s.enforceConflictingLexicallyDeclaredNames(s.varDeclaredNames, DUPLICATE_BINDING); s.newTargetExpressions.forEach(n => { s = s.addError(new EarlyError(n, 'new.target must be within function (but not arrow expression) code')); }); s = s.enforceFreeContinueStatementErrors(FREE_CONTINUE); s = s.enforceFreeLabeledContinueStatementErrors(UNBOUND_CONTINUE); s = s.enforceFreeBreakStatementErrors(FREE_BREAK); s = s.enforceFreeLabeledBreakStatementErrors(UNBOUND_BREAK); s = s.enforceSuperCallExpressions(SUPERCALL_ERROR); s = s.enforceSuperPropertyExpressions(SUPERPROPERTY_ERROR); if (isStrictFunctionBody(node)) { s = s.enforceStrictErrors(); } return s; } reduceSetter(node, { name, param, body }) { let isSimpleParameterList = node.param.type === 'BindingIdentifier'; param = param.observeLexicalDeclaration(); param = param.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); body = body.enforceConflictingLexicallyDeclaredNames(param.lexicallyDeclaredNames, DUPLICATE_BINDING); param = param.enforceSuperCallExpressions(SUPERCALL_ERROR); body = body.enforceSuperCallExpressions(SUPERCALL_ERROR); param = param.clearSuperPropertyExpressions(); body = body.clearSuperPropertyExpressions(); param = param.clearNewTargetExpressions(); body = body.clearNewTargetExpressions(); if (isStrictFunctionBody(node.body)) { param = param.enforceStrictErrors(); body = body.enforceStrictErrors(); } let s = super.reduceSetter(node, { name, param, body }); if (!isSimpleParameterList && isStrictFunctionBody(node.body)) { s = s.addError(new EarlyError(node, 'Functions with non-simple parameter lists may not contain a "use strict" directive')); } s = s.observeVarBoundary(); return s; } reduceStaticMemberExpression(node) { let s = super.reduceStaticMemberExpression(...arguments); if (node.object.type === 'Super') { s = s.observeSuperPropertyExpression(node); } return s; } reduceSwitchStatement(node, { discriminant, cases }) { let sCases = this.append(...cases); sCases = sCases.functionDeclarationNamesAreLexical(); sCases = sCases.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); sCases = sCases.enforceConflictingLexicallyDeclaredNames(sCases.varDeclaredNames, DUPLICATE_BINDING); sCases = sCases.observeLexicalBoundary(); let s = this.append(discriminant, sCases); s = s.clearFreeBreakStatements(); return s; } reduceSwitchStatementWithDefault(node, { discriminant, preDefaultCases, defaultCase, postDefaultCases }) { let sCases = this.append(defaultCase, ...preDefaultCases, ...postDefaultCases); sCases = sCases.functionDeclarationNamesAreLexical(); sCases = sCases.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING); sCases = sCases.enforceConflictingLexicallyDeclaredNames(sCases.varDeclaredNames, DUPLICATE_BINDING); sCases = sCases.observeLexicalBoundary(); let s = this.append(discriminant, sCases); s = s.clearFreeBreakStatements(); return s; } reduceVariableDeclaration(node) { let s = super.reduceVariableDeclaration(...arguments); switch (node.kind) { case 'const': case 'let': { s = s.observeLexicalDeclaration(); if (s.lexicallyDeclaredNames.has('let')) { s.lexicallyDeclaredNames.get('let').forEach(n => { s = s.addError(new EarlyError(n, 'Lexical declarations must not have a binding named "let"')); }); } break; } case 'var': s = s.observeVarDeclaration(); break; } return s; } reduceVariableDeclarationStatement(node) { let s = super.reduceVariableDeclarationStatement(...arguments); if (node.declaration.kind === 'const') { node.declaration.declarators.forEach(declarator => { if (declarator.init == null) { s = s.addError(new EarlyError(declarator, 'Constant lexical declarations must have an initialiser')); } }); } return s; } reduceWhileStatement(node) { let s = super.reduceWhileStatement(...arguments); if (isLabelledFunction(node.body)) { s = s.addError(new EarlyError(node.body, 'The body of a while statement must not be a labeled function declaration')); } s = s.clearFreeContinueStatements().clearFreeBreakStatements(); return s; } reduceWithStatement(node) { let s = super.reduceWithStatement(...arguments); if (isLabelledFunction(node.body)) { s = s.addError(new EarlyError(node.body, 'The body of a with statement must not be a labeled function declaration')); } s = s.addStrictError(new EarlyError(node, 'Strict mode code must not include a with statement')); return s; } reduceYieldExpression(node) { let s = super.reduceYieldExpression(...arguments); s = s.observeYieldExpression(node); return s; } reduceYieldGeneratorExpression(node) { let s = super.reduceYieldGeneratorExpression(...arguments); s = s.observeYieldExpression(node); return s; } static check(node) { return reduce(new EarlyErrorChecker, node).errors; } } module.exports = { EarlyErrorChecker };