mirror of
https://codeberg.org/uzu/strudel
synced 2026-08-01 21:37:42 -04:00
ast shape shifting pseudo note variables
This commit is contained in:
@@ -0,0 +1,409 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import MultiMap from '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
|
||||
|
||||
export 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,
|
||||
});
|
||||
});
|
||||
|
||||
export class EarlyError extends Error {
|
||||
constructor(node, message) {
|
||||
super(message);
|
||||
this.node = node;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,772 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import reduce, { MonoidalReducer } from '../shift-reducer';
|
||||
import { isStrictModeReservedWord } from './utils';
|
||||
import { ErrorMessages } from './errors';
|
||||
|
||||
import { EarlyErrorState, EarlyError } from './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)}`);
|
||||
|
||||
export 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 }) {
|
||||
binding = binding.observeLexicalDeclaration();
|
||||
binding = binding.enforceDuplicateLexicallyDeclaredNames(DUPLICATE_BINDING);
|
||||
binding = binding.enforceConflictingLexicallyDeclaredNames(body.previousLexicallyDeclaredNames, DUPLICATE_BINDING);
|
||||
binding.lexicallyDeclaredNames.forEachEntry((nodes, bindingName) => {
|
||||
if (body.varDeclaredNames.has(bindingName)) {
|
||||
body.varDeclaredNames.get(bindingName).forEach(conflictingNode => {
|
||||
if (body.forOfVarDeclaredNames.indexOf(conflictingNode) >= 0) {
|
||||
binding = binding.addError(DUPLICATE_BINDING(conflictingNode));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export const ErrorMessages = {
|
||||
UNEXPECTED_TOKEN(id) {
|
||||
return `Unexpected token ${JSON.stringify(id)}`;
|
||||
},
|
||||
UNEXPECTED_ILLEGAL_TOKEN(id) {
|
||||
return `Unexpected ${JSON.stringify(id)}`;
|
||||
},
|
||||
UNEXPECTED_ESCAPED_KEYWORD: 'Unexpected escaped keyword',
|
||||
UNEXPECTED_NUMBER: 'Unexpected number',
|
||||
UNEXPECTED_STRING: 'Unexpected string',
|
||||
UNEXPECTED_IDENTIFIER: 'Unexpected identifier',
|
||||
UNEXPECTED_RESERVED_WORD: 'Unexpected reserved word',
|
||||
UNEXPECTED_TEMPLATE: 'Unexpected template',
|
||||
UNEXPECTED_EOS: 'Unexpected end of input',
|
||||
UNEXPECTED_LINE_TERMINATOR: 'Unexpected line terminator',
|
||||
UNEXPECTED_COMMA_AFTER_REST: 'Unexpected comma after rest',
|
||||
UNEXPECTED_REST_PARAMETERS_INITIALIZATION: 'Rest parameter may not have a default initializer',
|
||||
NEWLINE_AFTER_THROW: 'Illegal newline after throw',
|
||||
UNTERMINATED_REGEXP: 'Invalid regular expression: missing /',
|
||||
INVALID_LAST_REST_PARAMETER: 'Rest parameter must be last formal parameter',
|
||||
INVALID_REST_PARAMETERS_INITIALIZATION: 'Rest parameter may not have a default initializer',
|
||||
INVALID_REGEXP_FLAGS: 'Invalid regular expression flags',
|
||||
INVALID_REGEX: 'Invalid regular expression',
|
||||
INVALID_LHS_IN_ASSIGNMENT: 'Invalid left-hand side in assignment',
|
||||
INVALID_LHS_IN_BINDING: 'Invalid left-hand side in binding', // todo collapse messages?
|
||||
INVALID_LHS_IN_FOR_IN: 'Invalid left-hand side in for-in',
|
||||
INVALID_LHS_IN_FOR_OF: 'Invalid left-hand side in for-of',
|
||||
INVALID_LHS_IN_FOR_AWAIT: 'Invalid left-hand side in for-await',
|
||||
INVALID_UPDATE_OPERAND: 'Increment/decrement target must be an identifier or member expression',
|
||||
INVALID_EXPONENTIATION_LHS: 'Unary expressions as the left operand of an exponentation expression ' +
|
||||
'must be disambiguated with parentheses',
|
||||
MULTIPLE_DEFAULTS_IN_SWITCH: 'More than one default clause in switch statement',
|
||||
NO_CATCH_OR_FINALLY: 'Missing catch or finally after try',
|
||||
ILLEGAL_RETURN: 'Illegal return statement',
|
||||
ILLEGAL_ARROW_FUNCTION_PARAMS: 'Illegal arrow function parameter list',
|
||||
INVALID_ASYNC_PARAMS: 'Async function parameters must not contain await expressions',
|
||||
INVALID_VAR_INIT_FOR_IN: 'Invalid variable declaration in for-in statement',
|
||||
INVALID_VAR_INIT_FOR_OF: 'Invalid variable declaration in for-of statement',
|
||||
INVALID_VAR_INIT_FOR_AWAIT: 'Invalid variable declaration in for-await statement',
|
||||
UNINITIALIZED_BINDINGPATTERN_IN_FOR_INIT: 'Binding pattern appears without initializer in for statement init',
|
||||
ILLEGAL_PROPERTY: 'Illegal property initializer',
|
||||
INVALID_ID_BINDING_STRICT_MODE(id) {
|
||||
return `The identifier ${JSON.stringify(id)} must not be in binding position in strict mode`;
|
||||
},
|
||||
INVALID_ID_IN_LABEL_STRICT_MODE(id) {
|
||||
return `The identifier ${JSON.stringify(id)} must not be in label position in strict mode`;
|
||||
},
|
||||
INVALID_ID_IN_EXPRESSION_STRICT_MODE(id) {
|
||||
return `The identifier ${JSON.stringify(id)} must not be in expression position in strict mode`;
|
||||
},
|
||||
INVALID_CALL_TO_SUPER: 'Calls to super must be in the "constructor" method of a class expression ' +
|
||||
'or class declaration that has a superclass',
|
||||
INVALID_DELETE_STRICT_MODE: 'Identifier expressions must not be deleted in strict mode',
|
||||
DUPLICATE_BINDING(id) {
|
||||
return `Duplicate binding ${JSON.stringify(id)}`;
|
||||
},
|
||||
ILLEGAL_ID_IN_LEXICAL_DECLARATION(id) {
|
||||
return `Lexical declarations must not have a binding named ${JSON.stringify(id)}`;
|
||||
},
|
||||
UNITIALIZED_CONST: 'Constant lexical declarations must have an initialiser',
|
||||
ILLEGAL_LABEL_IN_BODY(stmt) {
|
||||
return `The body of a ${stmt} statement must not be a labeled function declaration`;
|
||||
},
|
||||
ILLEGEAL_LABEL_IN_IF: 'The consequent of an if statement must not be a labeled function declaration',
|
||||
ILLEGAL_LABEL_IN_ELSE: 'The alternate of an if statement must not be a labeled function declaration',
|
||||
ILLEGAL_CONTINUE_WITHOUT_ITERATION_WITH_ID(id) {
|
||||
return `Continue statement must be nested within an iteration statement with label ${JSON.stringify(id)}`;
|
||||
},
|
||||
ILLEGAL_CONTINUE_WITHOUT_ITERATION: 'Continue statement must be nested within an iteration statement',
|
||||
ILLEGAL_BREAK_WITHOUT_ITERATION_OR_SWITCH:
|
||||
'Break statement must be nested within an iteration statement or a switch statement',
|
||||
ILLEGAL_WITH_STRICT_MODE: 'Strict mode code must not include a with statement',
|
||||
ILLEGAL_ACCESS_SUPER_MEMBER: 'Member access on super must be in a method',
|
||||
ILLEGAL_SUPER_CALL: 'Calls to super must be in the "constructor" method of a class expression or class declaration that has a superclass',
|
||||
DUPLICATE_LABEL_DECLARATION(label) {
|
||||
return `Label ${JSON.stringify(label)} has already been declared`;
|
||||
},
|
||||
ILLEGAL_BREAK_WITHIN_LABEL(label) {
|
||||
return `Break statement must be nested within a statement with label ${JSON.stringify(label)}`;
|
||||
},
|
||||
ILLEGAL_YIELD_EXPRESSIONS(paramType) {
|
||||
return `${paramType} parameters must not contain yield expressions`;
|
||||
},
|
||||
ILLEGAL_YIELD_IDENTIFIER: '"yield" may not be used as an identifier in this context',
|
||||
ILLEGAL_AWAIT_IDENTIFIER: '"await" may not be used as an identifier in this context',
|
||||
DUPLICATE_CONSTRUCTOR: 'Duplicate constructor method in class',
|
||||
ILLEGAL_CONSTRUCTORS: 'Constructors cannot be async, generators, getters or setters',
|
||||
ILLEGAL_STATIC_CLASS_NAME: 'Static class methods cannot be named "prototype"',
|
||||
NEW_TARGET_ERROR: 'new.target must be within function (but not arrow expression) code',
|
||||
DUPLICATE_EXPORT(id) {
|
||||
return `Duplicate export ${JSON.stringify(id)}`;
|
||||
},
|
||||
UNDECLARED_BINDING(id) {
|
||||
return `Exported binding ${JSON.stringify(id)} is not declared`;
|
||||
},
|
||||
DUPLICATE_PROPTO_PROP: 'Duplicate __proto__ property in object literal not allowed',
|
||||
ILLEGAL_LABEL_FUNC_DECLARATION: 'Labeled FunctionDeclarations are disallowed in strict mode',
|
||||
ILLEGAL_FUNC_DECL_IF: 'FunctionDeclarations in IfStatements are disallowed in strict mode',
|
||||
ILLEGAL_USE_STRICT: 'Functions with non-simple parameter lists may not contain a "use strict" directive',
|
||||
ILLEGAL_EXPORTED_NAME: 'Names of variables used in an export specifier from the current module must be identifiers',
|
||||
NO_OCTALS_IN_TEMPLATES: 'Template literals may not contain octal escape sequences',
|
||||
NO_AWAIT_IN_ASYNC_PARAMS: 'Async arrow parameters may not contain "await"',
|
||||
};
|
||||
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
import { GenericParser } from './parser';
|
||||
import { JsError } from './tokenizer';
|
||||
import { EarlyErrorChecker } from './early-errors';
|
||||
import { isLineTerminator } from './utils';
|
||||
|
||||
class ParserWithLocation extends GenericParser {
|
||||
constructor(source) {
|
||||
super(source);
|
||||
this.locations = new WeakMap;
|
||||
this.comments = [];
|
||||
}
|
||||
|
||||
startNode() {
|
||||
return this.getLocation();
|
||||
}
|
||||
|
||||
finishNode(node, start) {
|
||||
if (node.type === 'Script' || node.type === 'Module') {
|
||||
this.locations.set(node, {
|
||||
start: { line: 1, column: 0, offset: 0 },
|
||||
end: this.getLocation(),
|
||||
});
|
||||
return node;
|
||||
}
|
||||
if (node.type === 'TemplateExpression') {
|
||||
// Adjust TemplateElements to not include surrounding backticks or braces
|
||||
for (let i = 0; i < node.elements.length; i += 2) {
|
||||
const endAdjustment = i < node.elements.length - 1 ? 2 : 1; // discard '${' or '`' respectively
|
||||
const element = node.elements[i];
|
||||
const location = this.locations.get(element);
|
||||
this.locations.set(element, {
|
||||
start: { line: location.start.line, column: location.start.column + 1, offset: location.start.offset + 1 }, // discard '}' or '`'
|
||||
end: { line: location.end.line, column: location.end.column - endAdjustment, offset: location.end.offset - endAdjustment },
|
||||
});
|
||||
}
|
||||
}
|
||||
this.locations.set(node, {
|
||||
start,
|
||||
end: this.getLastTokenEndLocation(),
|
||||
});
|
||||
return node;
|
||||
}
|
||||
|
||||
copyNode(src, dest) {
|
||||
this.locations.set(dest, this.locations.get(src)); // todo check undefined
|
||||
return dest;
|
||||
}
|
||||
|
||||
skipSingleLineComment(offset) {
|
||||
// We're actually extending the *tokenizer*, here.
|
||||
const start = {
|
||||
line: this.line + 1,
|
||||
column: this.index - this.lineStart,
|
||||
offset: this.index,
|
||||
};
|
||||
const c = this.source[this.index];
|
||||
const type = c === '/' ? 'SingleLine' : c === '<' ? 'HTMLOpen' : 'HTMLClose';
|
||||
|
||||
super.skipSingleLineComment(offset);
|
||||
|
||||
const end = {
|
||||
line: this.line + 1,
|
||||
column: this.index - this.lineStart,
|
||||
offset: this.index,
|
||||
};
|
||||
const trailingLineTerminatorCharacters = this.source[this.index - 2] === '\r' ? 2 : isLineTerminator(this.source.charCodeAt(this.index - 1)) ? 1 : 0;
|
||||
const text = this.source.substring(start.offset + offset, end.offset - trailingLineTerminatorCharacters);
|
||||
|
||||
this.comments.push({ text, type, start, end });
|
||||
}
|
||||
|
||||
skipMultiLineComment() {
|
||||
const start = {
|
||||
line: this.line + 1,
|
||||
column: this.index - this.lineStart,
|
||||
offset: this.index,
|
||||
};
|
||||
const type = 'MultiLine';
|
||||
|
||||
const retval = super.skipMultiLineComment();
|
||||
|
||||
const end = {
|
||||
line: this.line + 1,
|
||||
column: this.index - this.lineStart,
|
||||
offset: this.index,
|
||||
};
|
||||
const text = this.source.substring(start.offset + 2, end.offset - 2);
|
||||
|
||||
this.comments.push({ text, type, start, end });
|
||||
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
function generateInterface(parsingFunctionName) {
|
||||
return function parse(code, { earlyErrors = true } = {}) {
|
||||
let parser = new GenericParser(code);
|
||||
let tree = parser[parsingFunctionName]();
|
||||
if (earlyErrors) {
|
||||
let errors = EarlyErrorChecker.check(tree);
|
||||
// for now, just throw the first error; we will handle multiple errors later
|
||||
if (errors.length > 0) {
|
||||
throw new JsError(0, 1, 0, errors[0].message);
|
||||
}
|
||||
}
|
||||
return tree;
|
||||
};
|
||||
}
|
||||
|
||||
function generateInterfaceWithLocation(parsingFunctionName) {
|
||||
return function parse(code, { earlyErrors = true } = {}) {
|
||||
let parser = new ParserWithLocation(code);
|
||||
let tree = parser[parsingFunctionName]();
|
||||
if (earlyErrors) {
|
||||
let errors = EarlyErrorChecker.check(tree);
|
||||
// for now, just throw the first error; we will handle multiple errors later
|
||||
if (errors.length > 0) {
|
||||
let { node, message } = errors[0];
|
||||
let { offset, line, column } = parser.locations.get(node).start;
|
||||
throw new JsError(offset, line, column, message);
|
||||
}
|
||||
}
|
||||
return { tree, locations: parser.locations, comments: parser.comments };
|
||||
};
|
||||
}
|
||||
|
||||
export const parseModule = generateInterface('parseModule');
|
||||
export const parseScript = generateInterface('parseScript');
|
||||
export const parseModuleWithLocation = generateInterfaceWithLocation('parseModule');
|
||||
export const parseScriptWithLocation = generateInterfaceWithLocation('parseScript');
|
||||
export default parseScript;
|
||||
export { EarlyErrorChecker, GenericParser, ParserWithLocation };
|
||||
export { default as Tokenizer, TokenClass, TokenType } from './tokenizer';
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* Copyright 2017 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.
|
||||
*/
|
||||
|
||||
import { whitespaceArray, whitespaceBool, idStartLargeRegex, idStartBool, idContinueLargeRegex, idContinueBool } from './unicode';
|
||||
|
||||
|
||||
const strictReservedWords = [
|
||||
'null',
|
||||
'true',
|
||||
'false',
|
||||
|
||||
'implements',
|
||||
'interface',
|
||||
'package',
|
||||
'private',
|
||||
'protected',
|
||||
'public',
|
||||
'static',
|
||||
'let',
|
||||
|
||||
'if',
|
||||
'in',
|
||||
'do',
|
||||
'var',
|
||||
'for',
|
||||
'new',
|
||||
'try',
|
||||
'this',
|
||||
'else',
|
||||
'case',
|
||||
'void',
|
||||
'with',
|
||||
'enum',
|
||||
'while',
|
||||
'break',
|
||||
'catch',
|
||||
'throw',
|
||||
'const',
|
||||
'yield',
|
||||
'class',
|
||||
'super',
|
||||
'return',
|
||||
'typeof',
|
||||
'delete',
|
||||
'switch',
|
||||
'export',
|
||||
'import',
|
||||
'default',
|
||||
'finally',
|
||||
'extends',
|
||||
'function',
|
||||
'continue',
|
||||
'debugger',
|
||||
'instanceof',
|
||||
];
|
||||
|
||||
export function isStrictModeReservedWord(id) {
|
||||
return strictReservedWords.indexOf(id) !== -1;
|
||||
}
|
||||
|
||||
export function isWhiteSpace(ch) {
|
||||
return ch < 128 ? whitespaceBool[ch] : ch === 0xA0 || ch > 0x167F && whitespaceArray.indexOf(ch) !== -1;
|
||||
}
|
||||
|
||||
export function isLineTerminator(ch) {
|
||||
return ch === 0x0A || ch === 0x0D || ch === 0x2028 || ch === 0x2029;
|
||||
}
|
||||
|
||||
export function isIdentifierStart(ch) {
|
||||
return ch < 128 ? idStartBool[ch] : idStartLargeRegex.test(String.fromCodePoint(ch));
|
||||
}
|
||||
|
||||
export function isIdentifierPart(ch) {
|
||||
return ch < 128 ? idContinueBool[ch] : idContinueLargeRegex.test(String.fromCodePoint(ch));
|
||||
}
|
||||
|
||||
export function isDecimalDigit(ch) {
|
||||
return ch >= 48 && ch <= 57;
|
||||
}
|
||||
|
||||
export function getHexValue(rune) {
|
||||
if (rune >= '0' && rune <= '9') {
|
||||
return rune.charCodeAt(0) - 48;
|
||||
}
|
||||
if (rune >= 'a' && rune <= 'f') {
|
||||
return rune.charCodeAt(0) - 87;
|
||||
}
|
||||
if (rune >= 'A' && rune <= 'F') {
|
||||
return rune.charCodeAt(0) - 55;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
Reference in New Issue
Block a user