/** * 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 MultiMap = require('multimap'); function addEach(thisMap, ...otherMaps) { otherMaps.forEach(otherMap => { otherMap.forEachEntry((v, k) => { thisMap.set.apply(thisMap, [k].concat(v)); }); }); return thisMap; } let identity; // initialised below EarlyErrorState class EarlyErrorState { constructor() { this.errors = []; // errors that are only errors in strict mode code this.strictErrors = []; // Label values used in LabeledStatement nodes; cleared at function boundaries this.usedLabelNames = []; // BreakStatement nodes; cleared at iteration; switch; and function boundaries this.freeBreakStatements = []; // ContinueStatement nodes; cleared at this.freeContinueStatements = []; // labeled BreakStatement nodes; cleared at LabeledStatement with same Label and function boundaries this.freeLabeledBreakStatements = []; // labeled ContinueStatement nodes; cleared at labeled iteration statement with same Label and function boundaries this.freeLabeledContinueStatements = []; // NewTargetExpression nodes; cleared at function (besides arrow expression) boundaries this.newTargetExpressions = []; // BindingIdentifier nodes; cleared at containing declaration node this.boundNames = new MultiMap; // BindingIdentifiers that were found to be in a lexical binding position this.lexicallyDeclaredNames = new MultiMap; // BindingIdentifiers that were the name of a FunctionDeclaration this.functionDeclarationNames = new MultiMap; // BindingIdentifiers that were found to be in a variable binding position this.varDeclaredNames = new MultiMap; // BindingIdentifiers that were found to be in a variable binding position this.forOfVarDeclaredNames = []; // Names that this module exports this.exportedNames = new MultiMap; // Locally declared names that are referenced in export declarations this.exportedBindings = new MultiMap; // CallExpressions with Super callee this.superCallExpressions = []; // SuperCall expressions in the context of a Method named "constructor" this.superCallExpressionsInConstructorMethod = []; // MemberExpressions with Super object this.superPropertyExpressions = []; // YieldExpression and YieldGeneratorExpression nodes; cleared at function boundaries this.yieldExpressions = []; // AwaitExpression nodes; cleared at function boundaries this.awaitExpressions = []; } addFreeBreakStatement(s) { this.freeBreakStatements.push(s); return this; } addFreeLabeledBreakStatement(s) { this.freeLabeledBreakStatements.push(s); return this; } clearFreeBreakStatements() { this.freeBreakStatements = []; return this; } addFreeContinueStatement(s) { this.freeContinueStatements.push(s); return this; } addFreeLabeledContinueStatement(s) { this.freeLabeledContinueStatements.push(s); return this; } clearFreeContinueStatements() { this.freeContinueStatements = []; return this; } enforceFreeBreakStatementErrors(createError) { [].push.apply(this.errors, this.freeBreakStatements.map(createError)); this.freeBreakStatements = []; return this; } enforceFreeLabeledBreakStatementErrors(createError) { [].push.apply(this.errors, this.freeLabeledBreakStatements.map(createError)); this.freeLabeledBreakStatements = []; return this; } enforceFreeContinueStatementErrors(createError) { [].push.apply(this.errors, this.freeContinueStatements.map(createError)); this.freeContinueStatements = []; return this; } enforceFreeLabeledContinueStatementErrors(createError) { [].push.apply(this.errors, this.freeLabeledContinueStatements.map(createError)); this.freeLabeledContinueStatements = []; return this; } observeIterationLabel(label) { this.usedLabelNames.push(label); this.freeLabeledBreakStatements = this.freeLabeledBreakStatements.filter(s => s.label !== label); this.freeLabeledContinueStatements = this.freeLabeledContinueStatements.filter(s => s.label !== label); return this; } observeNonIterationLabel(label) { this.usedLabelNames.push(label); this.freeLabeledBreakStatements = this.freeLabeledBreakStatements.filter(s => s.label !== label); return this; } clearUsedLabelNames() { this.usedLabelNames = []; return this; } observeSuperCallExpression(node) { this.superCallExpressions.push(node); return this; } observeConstructorMethod() { this.superCallExpressionsInConstructorMethod = this.superCallExpressions; this.superCallExpressions = []; return this; } clearSuperCallExpressionsInConstructorMethod() { this.superCallExpressionsInConstructorMethod = []; return this; } enforceSuperCallExpressions(createError) { [].push.apply(this.errors, this.superCallExpressions.map(createError)); [].push.apply(this.errors, this.superCallExpressionsInConstructorMethod.map(createError)); this.superCallExpressions = []; this.superCallExpressionsInConstructorMethod = []; return this; } enforceSuperCallExpressionsInConstructorMethod(createError) { [].push.apply(this.errors, this.superCallExpressionsInConstructorMethod.map(createError)); this.superCallExpressionsInConstructorMethod = []; return this; } observeSuperPropertyExpression(node) { this.superPropertyExpressions.push(node); return this; } clearSuperPropertyExpressions() { this.superPropertyExpressions = []; return this; } enforceSuperPropertyExpressions(createError) { [].push.apply(this.errors, this.superPropertyExpressions.map(createError)); this.superPropertyExpressions = []; return this; } observeNewTargetExpression(node) { this.newTargetExpressions.push(node); return this; } clearNewTargetExpressions() { this.newTargetExpressions = []; return this; } bindName(name, node) { this.boundNames.set(name, node); return this; } clearBoundNames() { this.boundNames = new MultiMap; return this; } observeLexicalDeclaration() { addEach(this.lexicallyDeclaredNames, this.boundNames); this.boundNames = new MultiMap; return this; } observeLexicalBoundary() { this.previousLexicallyDeclaredNames = this.lexicallyDeclaredNames; this.lexicallyDeclaredNames = new MultiMap; this.functionDeclarationNames = new MultiMap; return this; } enforceDuplicateLexicallyDeclaredNames(createError) { this.lexicallyDeclaredNames.forEachEntry(nodes => { if (nodes.length > 1) { nodes.slice(1).forEach(dupeNode => { this.addError(createError(dupeNode)); }); } }); return this; } enforceConflictingLexicallyDeclaredNames(otherNames, createError) { this.lexicallyDeclaredNames.forEachEntry((nodes, bindingName) => { if (otherNames.has(bindingName)) { nodes.forEach(conflictingNode => { this.addError(createError(conflictingNode)); }); } }); return this; } observeFunctionDeclaration() { this.observeVarBoundary(); addEach(this.functionDeclarationNames, this.boundNames); this.boundNames = new MultiMap; return this; } functionDeclarationNamesAreLexical() { addEach(this.lexicallyDeclaredNames, this.functionDeclarationNames); this.functionDeclarationNames = new MultiMap; return this; } observeVarDeclaration() { addEach(this.varDeclaredNames, this.boundNames); this.boundNames = new MultiMap; return this; } recordForOfVars() { this.varDeclaredNames.forEach(bindingIdentifier => { this.forOfVarDeclaredNames.push(bindingIdentifier); }); return this; } observeVarBoundary() { this.lexicallyDeclaredNames = new MultiMap; this.functionDeclarationNames = new MultiMap; this.varDeclaredNames = new MultiMap; this.forOfVarDeclaredNames = []; return this; } exportName(name, node) { this.exportedNames.set(name, node); return this; } exportDeclaredNames() { addEach(this.exportedNames, this.lexicallyDeclaredNames, this.varDeclaredNames); addEach(this.exportedBindings, this.lexicallyDeclaredNames, this.varDeclaredNames); return this; } exportBinding(name, node) { this.exportedBindings.set(name, node); return this; } clearExportedBindings() { this.exportedBindings = new MultiMap; return this; } observeYieldExpression(node) { this.yieldExpressions.push(node); return this; } clearYieldExpressions() { this.yieldExpressions = []; return this; } observeAwaitExpression(node) { this.awaitExpressions.push(node); return this; } clearAwaitExpressions() { this.awaitExpressions = []; return this; } addError(e) { this.errors.push(e); return this; } addStrictError(e) { this.strictErrors.push(e); return this; } enforceStrictErrors() { [].push.apply(this.errors, this.strictErrors); this.strictErrors = []; return this; } // MONOID IMPLEMENTATION static empty() { return identity; } concat(s) { if (this === identity) return s; if (s === identity) return this; [].push.apply(this.errors, s.errors); [].push.apply(this.strictErrors, s.strictErrors); [].push.apply(this.usedLabelNames, s.usedLabelNames); [].push.apply(this.freeBreakStatements, s.freeBreakStatements); [].push.apply(this.freeContinueStatements, s.freeContinueStatements); [].push.apply(this.freeLabeledBreakStatements, s.freeLabeledBreakStatements); [].push.apply(this.freeLabeledContinueStatements, s.freeLabeledContinueStatements); [].push.apply(this.newTargetExpressions, s.newTargetExpressions); addEach(this.boundNames, s.boundNames); addEach(this.lexicallyDeclaredNames, s.lexicallyDeclaredNames); addEach(this.functionDeclarationNames, s.functionDeclarationNames); addEach(this.varDeclaredNames, s.varDeclaredNames); [].push.apply(this.forOfVarDeclaredNames, s.forOfVarDeclaredNames); addEach(this.exportedNames, s.exportedNames); addEach(this.exportedBindings, s.exportedBindings); [].push.apply(this.superCallExpressions, s.superCallExpressions); [].push.apply(this.superCallExpressionsInConstructorMethod, s.superCallExpressionsInConstructorMethod); [].push.apply(this.superPropertyExpressions, s.superPropertyExpressions); [].push.apply(this.yieldExpressions, s.yieldExpressions); [].push.apply(this.awaitExpressions, s.awaitExpressions); return this; } } identity = new EarlyErrorState; Object.getOwnPropertyNames(EarlyErrorState.prototype).forEach(methodName => { if (methodName === 'constructor') return; Object.defineProperty(identity, methodName, { value() { return EarlyErrorState.prototype[methodName].apply(new EarlyErrorState, arguments); }, enumerable: false, writable: true, configurable: true, }); }); class EarlyError extends Error { constructor(node, message) { super(message); this.node = node; this.message = message; } } module.exports = { EarlyErrorState, EarlyError, };