281 lines
7.9 KiB
JavaScript
281 lines
7.9 KiB
JavaScript
// @flow
|
|
|
|
import { types as tt, TokenType } from "../tokenizer/types";
|
|
import type Parser from "../parser";
|
|
import * as N from "../types";
|
|
import type { Pos, Position } from "../util/location";
|
|
|
|
function isSimpleProperty(node: N.Node): boolean {
|
|
return node != null &&
|
|
node.type === "Property" &&
|
|
node.kind === "init" &&
|
|
node.method === false;
|
|
}
|
|
|
|
export default (superClass: Class<Parser>): Class<Parser> => class extends superClass {
|
|
estreeParseRegExpLiteral({ pattern, flags }: N.RegExpLiteral): N.Node {
|
|
let regex = null;
|
|
try {
|
|
regex = new RegExp(pattern, flags);
|
|
} catch (e) {
|
|
// In environments that don't support these flags value will
|
|
// be null as the regex can't be represented natively.
|
|
}
|
|
const node = this.estreeParseLiteral(regex);
|
|
node.regex = { pattern, flags };
|
|
|
|
return node;
|
|
}
|
|
|
|
estreeParseLiteral(value: any): N.Node {
|
|
return this.parseLiteral(value, "Literal");
|
|
}
|
|
|
|
directiveToStmt(directive: N.Directive): N.ExpressionStatement {
|
|
const directiveLiteral = directive.value;
|
|
|
|
const stmt = this.startNodeAt(directive.start, directive.loc.start);
|
|
const expression = this.startNodeAt(directiveLiteral.start, directiveLiteral.loc.start);
|
|
|
|
expression.value = directiveLiteral.value;
|
|
expression.raw = directiveLiteral.extra.raw;
|
|
|
|
stmt.expression = this.finishNodeAt(
|
|
expression, "Literal", directiveLiteral.end, directiveLiteral.loc.end);
|
|
stmt.directive = directiveLiteral.extra.raw.slice(1, -1);
|
|
|
|
return this.finishNodeAt(stmt, "ExpressionStatement", directive.end, directive.loc.end);
|
|
}
|
|
|
|
// ==================================
|
|
// Overrides
|
|
// ==================================
|
|
|
|
checkDeclaration(node: N.Pattern): void {
|
|
if (isSimpleProperty(node)) {
|
|
// $FlowFixMe
|
|
this.checkDeclaration(node.value);
|
|
} else {
|
|
super.checkDeclaration(node);
|
|
}
|
|
}
|
|
|
|
checkGetterSetterParamCount(prop: N.ObjectMethod | N.ClassMethod): void {
|
|
const paramCount = prop.kind === "get" ? 0 : 1;
|
|
// $FlowFixMe (prop.value present for ObjectMethod, but for ClassMethod should use prop.params?)
|
|
if (prop.value.params.length !== paramCount) {
|
|
const start = prop.start;
|
|
if (prop.kind === "get") {
|
|
this.raise(start, "getter should have no params");
|
|
} else {
|
|
this.raise(start, "setter should have exactly one param");
|
|
}
|
|
}
|
|
}
|
|
|
|
checkLVal(
|
|
expr: N.Expression,
|
|
isBinding: ?boolean,
|
|
checkClashes: ?{ [key: string]: boolean },
|
|
contextDescription: string
|
|
): void {
|
|
switch (expr.type) {
|
|
case "ObjectPattern":
|
|
expr.properties.forEach((prop) => {
|
|
this.checkLVal(
|
|
prop.type === "Property" ? prop.value : prop,
|
|
isBinding,
|
|
checkClashes,
|
|
"object destructuring pattern"
|
|
);
|
|
});
|
|
break;
|
|
default:
|
|
super.checkLVal(expr, isBinding, checkClashes, contextDescription);
|
|
}
|
|
}
|
|
|
|
checkPropClash(prop: N.ObjectMember, propHash: { [key: string]: boolean }): void {
|
|
if (prop.computed || !isSimpleProperty(prop)) return;
|
|
|
|
const key = prop.key;
|
|
// It is either an Identifier or a String/NumericLiteral
|
|
const name = key.type === "Identifier" ? key.name : String(key.value);
|
|
|
|
if (name === "__proto__") {
|
|
if (propHash.proto) this.raise(key.start, "Redefinition of __proto__ property");
|
|
propHash.proto = true;
|
|
}
|
|
}
|
|
|
|
isStrictBody(node: { body: N.BlockStatement }, isExpression?: boolean): boolean {
|
|
if (!isExpression && node.body.body.length > 0) {
|
|
for (const directive of node.body.body) {
|
|
if (directive.type === "ExpressionStatement" && directive.expression.type === "Literal") {
|
|
if (directive.expression.value === "use strict") return true;
|
|
} else {
|
|
// Break for the first non literal expression
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
isValidDirective(stmt: N.Statement): boolean {
|
|
return stmt.type === "ExpressionStatement" &&
|
|
stmt.expression.type === "Literal" &&
|
|
typeof stmt.expression.value === "string" &&
|
|
(!stmt.expression.extra || !stmt.expression.extra.parenthesized);
|
|
}
|
|
|
|
stmtToDirective(stmt: N.Statement): N.Directive {
|
|
const directive = super.stmtToDirective(stmt);
|
|
const value = stmt.expression.value;
|
|
|
|
// Reset value to the actual value as in estree mode we want
|
|
// the stmt to have the real value and not the raw value
|
|
directive.value.value = value;
|
|
|
|
return directive;
|
|
}
|
|
|
|
parseBlockBody(
|
|
node: N.BlockStatementLike,
|
|
allowDirectives: ?boolean,
|
|
topLevel: boolean,
|
|
end: TokenType
|
|
): void {
|
|
super.parseBlockBody(node, allowDirectives, topLevel, end);
|
|
|
|
const directiveStatements = node.directives.map((d) => this.directiveToStmt(d));
|
|
node.body = directiveStatements.concat(node.body);
|
|
delete node.directives;
|
|
}
|
|
|
|
parseClassMethod(
|
|
classBody: N.ClassBody,
|
|
method: N.ClassMethod,
|
|
isGenerator: boolean,
|
|
isAsync: boolean
|
|
): void {
|
|
this.parseMethod(method, isGenerator, isAsync);
|
|
classBody.body.push(this.finishNode(method, "MethodDefinition"));
|
|
}
|
|
|
|
parseExprAtom(refShorthandDefaultPos?: ?Pos): N.Expression {
|
|
switch (this.state.type) {
|
|
case tt.regexp:
|
|
return this.estreeParseRegExpLiteral(this.state.value);
|
|
|
|
case tt.num:
|
|
case tt.string:
|
|
return this.estreeParseLiteral(this.state.value);
|
|
|
|
case tt._null:
|
|
return this.estreeParseLiteral(null);
|
|
|
|
case tt._true:
|
|
return this.estreeParseLiteral(true);
|
|
|
|
case tt._false:
|
|
return this.estreeParseLiteral(false);
|
|
|
|
default:
|
|
return super.parseExprAtom(refShorthandDefaultPos);
|
|
}
|
|
}
|
|
|
|
parseLiteral<T : N.Literal>(
|
|
value: any,
|
|
type: /*T["kind"]*/string,
|
|
startPos?: number,
|
|
startLoc?: Position
|
|
): T {
|
|
const node = super.parseLiteral(value, type, startPos, startLoc);
|
|
node.raw = node.extra.raw;
|
|
delete node.extra;
|
|
|
|
return node;
|
|
}
|
|
|
|
parseMethod(
|
|
node: N.MethodLike,
|
|
isGenerator?: boolean,
|
|
isAsync?: boolean
|
|
): N.MethodLike {
|
|
let funcNode = this.startNode();
|
|
funcNode.kind = node.kind; // provide kind, so super method correctly sets state
|
|
funcNode = super.parseMethod(funcNode, isGenerator, isAsync);
|
|
delete funcNode.kind;
|
|
// $FlowIgnore
|
|
node.value = this.finishNode(funcNode, "FunctionExpression");
|
|
|
|
return node;
|
|
}
|
|
|
|
parseObjectMethod(
|
|
prop: N.ObjectMethod,
|
|
isGenerator: boolean,
|
|
isAsync: boolean,
|
|
isPattern: boolean
|
|
): ?N.ObjectMethod {
|
|
const node: N.EstreeProperty = (super.parseObjectMethod(prop, isGenerator, isAsync, isPattern): any);
|
|
|
|
if (node) {
|
|
node.type = "Property";
|
|
if (node.kind === "method") node.kind = "init";
|
|
node.shorthand = false;
|
|
}
|
|
|
|
return (node: any);
|
|
}
|
|
|
|
parseObjectProperty(
|
|
prop: N.ObjectProperty,
|
|
startPos: ?number,
|
|
startLoc: ?Position,
|
|
isPattern: boolean,
|
|
refShorthandDefaultPos: ?Pos
|
|
): ?N.ObjectProperty {
|
|
const node: N.EstreeProperty = (
|
|
super.parseObjectProperty(prop, startPos, startLoc, isPattern, refShorthandDefaultPos): any
|
|
);
|
|
|
|
if (node) {
|
|
node.kind = "init";
|
|
node.type = "Property";
|
|
}
|
|
|
|
return (node: any);
|
|
}
|
|
|
|
toAssignable(
|
|
node: N.Node,
|
|
isBinding: ?boolean,
|
|
contextDescription: string
|
|
): N.Node {
|
|
if (isSimpleProperty(node)) {
|
|
this.toAssignable(node.value, isBinding, contextDescription);
|
|
|
|
return node;
|
|
} else if (node.type === "ObjectExpression") {
|
|
node.type = "ObjectPattern";
|
|
for (const prop of node.properties) {
|
|
if (prop.kind === "get" || prop.kind === "set") {
|
|
this.raise(prop.key.start, "Object pattern can't contain getter or setter");
|
|
} else if (prop.method) {
|
|
this.raise(prop.key.start, "Object pattern can't contain methods");
|
|
} else {
|
|
this.toAssignable(prop, isBinding, "object destructuring pattern");
|
|
}
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
return super.toAssignable(node, isBinding, contextDescription);
|
|
}
|
|
};
|