Merge branch 'master' into 7.0

# Conflicts:
#	src/parser/statement.js
#	src/plugins/jsx/index.js
This commit is contained in:
Daniel Tschinder
2017-01-27 23:12:20 +01:00
294 changed files with 8543 additions and 177 deletions

View File

@@ -20,4 +20,13 @@ export function parse(input, options) {
return new Parser(options, input).parse();
}
export function parseExpression(input, options) {
const parser = new Parser(options, input);
if (parser.options.strictMode) {
parser.state.strict = true;
}
return parser.getExpression();
}
export { tokTypes };

View File

@@ -1,4 +1,3 @@
/* eslint indent: 0 */
/* eslint max-len: 0 */
// A recursive descent parser operates by defining functions for all
@@ -31,31 +30,28 @@ const pp = Parser.prototype;
// strict mode, init properties are also not allowed to be repeated.
pp.checkPropClash = function (prop, propHash) {
if (prop.computed) return;
if (prop.computed || prop.kind) return;
const key = prop.key;
let name;
switch (key.type) {
case "Identifier":
name = key.name;
break;
// It is either an Identifier or a String/NumericLiteral
const name = key.type === "Identifier" ? key.name : String(key.value);
case "StringLiteral":
case "NumericLiteral":
name = String(key.value);
break;
// istanbul ignore next: non-computed property keys are always one of the above
default:
return;
}
if (name === "__proto__" && !prop.kind) {
if (name === "__proto__") {
if (propHash.proto) this.raise(key.start, "Redefinition of __proto__ property");
propHash.proto = true;
}
};
// Convenience method to parse an Expression only
pp.getExpression = function() {
this.nextToken();
const expr = this.parseExpression();
if (!this.match(tt.eof)) {
this.unexpected();
}
return expr;
};
// ### Expression parsing
// These nest, from the most general expression type at the top to
@@ -807,36 +803,61 @@ pp.parseObj = function (isPattern, refShorthandDefaultPos) {
return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression");
};
pp.parseObjPropValue = function (prop, startPos, startLoc, isGenerator, isAsync, isPattern, refShorthandDefaultPos) {
pp.isGetterOrSetterMethod = function (prop, isPattern) {
return !isPattern &&
!prop.computed &&
prop.key.type === "Identifier" &&
(prop.key.name === "get" || prop.key.name === "set") &&
(
this.match(tt.string) || // get "string"() {}
this.match(tt.num) || // get 1() {}
this.match(tt.bracketL) || // get ["string"]() {}
this.match(tt.name) || // get foo() {}
this.state.type.keyword // get debugger() {}
);
};
// get methods aren't allowed to have any parameters
// set methods must have exactly 1 parameter
pp.checkGetterSetterParamCount = function (method) {
const paramCount = method.kind === "get" ? 0 : 1;
if (method.params.length !== paramCount) {
const start = method.start;
if (method.kind === "get") {
this.raise(start, "getter should have no params");
} else {
this.raise(start, "setter should have exactly one param");
}
}
};
pp.parseObjectMethod = function (prop, isGenerator, isAsync, isPattern) {
if (isAsync || isGenerator || this.match(tt.parenL)) {
if (isPattern) this.unexpected();
prop.kind = "method";
prop.method = true;
this.parseMethod(prop, isGenerator, isAsync);
return this.finishNode(prop, "ObjectMethod");
}
if (this.eat(tt.colon)) {
prop.value = isPattern ? this.parseMaybeDefault(this.state.start, this.state.startLoc) : this.parseMaybeAssign(false, refShorthandDefaultPos);
return this.finishNode(prop, "ObjectProperty");
}
if (!isPattern && !prop.computed && prop.key.type === "Identifier" && (prop.key.name === "get" || prop.key.name === "set") && (!this.match(tt.comma) && !this.match(tt.braceR))) {
if (this.isGetterOrSetterMethod(prop, isPattern)) {
if (isGenerator || isAsync) this.unexpected();
prop.kind = prop.key.name;
this.parsePropertyName(prop);
this.parseMethod(prop, false);
const paramCount = prop.kind === "get" ? 0 : 1;
if (prop.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");
}
}
this.parseMethod(prop);
this.checkGetterSetterParamCount(prop);
return this.finishNode(prop, "ObjectMethod");
}
};
pp.parseObjectProperty = function (prop, startPos, startLoc, isPattern, refShorthandDefaultPos) {
if (this.eat(tt.colon)) {
prop.value = isPattern ? this.parseMaybeDefault(this.state.start, this.state.startLoc) : this.parseMaybeAssign(false, refShorthandDefaultPos);
return this.finishNode(prop, "ObjectProperty");
}
if (!prop.computed && prop.key.type === "Identifier") {
if (isPattern) {
@@ -850,12 +871,20 @@ pp.parseObjPropValue = function (prop, startPos, startLoc, isGenerator, isAsync,
} else {
prop.value = prop.key.__clone();
}
prop.shorthand = true;
return this.finishNode(prop, "ObjectProperty");
}
};
this.unexpected();
pp.parseObjPropValue = function (prop, startPos, startLoc, isGenerator, isAsync, isPattern, refShorthandDefaultPos) {
const node =
this.parseObjectMethod(prop, isGenerator, isAsync, isPattern) ||
this.parseObjectProperty(prop, startPos, startLoc, isPattern, refShorthandDefaultPos);
if (!node) this.unexpected();
return node;
};
pp.parsePropertyName = function (prop) {
@@ -890,7 +919,7 @@ pp.parseMethod = function (node, isGenerator, isAsync) {
this.initFunction(node, isAsync);
this.expect(tt.parenL);
node.params = this.parseBindingList(tt.parenR);
node.generator = isGenerator;
node.generator = !!isGenerator;
this.parseFunctionBody(node);
this.state.inMethod = oldInMethod;
return node;
@@ -905,8 +934,19 @@ pp.parseArrowExpression = function (node, params, isAsync) {
return this.finishNode(node, "ArrowFunctionExpression");
};
// Parse function body and check parameters.
pp.isStrictBody = function (node, isExpression) {
if (!isExpression && node.body.directives.length) {
for (const directive of (node.body.directives: Array<Object>)) {
if (directive.value.value === "use strict") {
return true;
}
}
}
return false;
};
// Parse function body and check parameters.
pp.parseFunctionBody = function (node, allowExpression) {
const isExpression = allowExpression && !this.match(tt.braceL);
@@ -931,24 +971,10 @@ pp.parseFunctionBody = function (node, allowExpression) {
// If this is a strict mode function, verify that argument names
// are not repeated, and it does not try to bind the words `eval`
// or `arguments`.
let checkLVal = this.state.strict;
let isStrict = false;
const isStrict = this.isStrictBody(node, isExpression);
// Also check when allowExpression === true for arrow functions
const checkLVal = this.state.strict || allowExpression || isStrict;
// arrow function
if (allowExpression) checkLVal = true;
// normal function
if (!isExpression && node.body.directives.length) {
for (const directive of (node.body.directives: Array<Object>)) {
if (directive.value.value === "use strict") {
isStrict = true;
checkLVal = true;
break;
}
}
}
//
if (isStrict && node.id && node.id.type === "Identifier" && node.id.name === "yield") {
this.raise(node.id.start, "Binding yield in strict mode");
}

View File

@@ -1,5 +1,3 @@
/* eslint indent: 0 */
import { types as tt } from "../tokenizer/types";
import Parser from "./index";

View File

@@ -1,4 +1,3 @@
/* eslint indent: 0 */
/* eslint max-len: 0 */
import { types as tt } from "../tokenizer/types";
@@ -26,7 +25,7 @@ pp.parseTopLevel = function (file, program) {
return this.finishNode(file, "File");
};
const loopLabel = {kind: "loop"}, switchLabel = {kind: "switch"};
const loopLabel = { kind: "loop" }, switchLabel = { kind: "switch" };
// TODO
@@ -231,8 +230,8 @@ pp.parseForStatement = function (node) {
let forAwait = false;
if (this.hasPlugin("asyncGenerators") && this.state.inAsync && this.isContextual("await")) {
forAwait = true;
this.next();
forAwait = true;
this.next();
}
this.expect(tt.parenL);
@@ -261,7 +260,7 @@ pp.parseForStatement = function (node) {
return this.parseFor(node, init);
}
const refShorthandDefaultPos = {start: 0};
const refShorthandDefaultPos = { start: 0 };
const init = this.parseExpression(true, refShorthandDefaultPos);
if (this.match(tt._in) || this.isContextual("of")) {
const description = this.isContextual("of") ? "for-of statement" : "for-in statement";
@@ -441,7 +440,7 @@ pp.parseLabeledStatement = function (node, maybeName, expr) {
}
}
this.state.labels.push({name: maybeName, kind: kind, statementStart: this.state.start});
this.state.labels.push({ name: maybeName, kind: kind, statementStart: this.state.start });
node.body = this.parseStatement(true);
this.state.labels.pop();
node.label = expr;
@@ -465,7 +464,11 @@ pp.parseBlock = function (allowDirectives?) {
return this.finishNode(node, "BlockStatement");
};
// TODO
pp.isValidDirective = function (stmt) {
return stmt.type === "ExpressionStatement" &&
stmt.expression.type === "StringLiteral" &&
!stmt.expression.extra.parenthesized;
};
pp.parseBlockBody = function (node, allowDirectives, topLevel, end) {
node.body = [];
@@ -482,9 +485,7 @@ pp.parseBlockBody = function (node, allowDirectives, topLevel, end) {
const stmt = this.parseStatement(true, topLevel);
if (allowDirectives && !parsedNonDirective &&
stmt.type === "ExpressionStatement" && stmt.expression.type === "StringLiteral" &&
!stmt.expression.extra.parenthesized) {
if (allowDirectives && !parsedNonDirective && this.isValidDirective(stmt)) {
const directive = this.stmtToDirective(stmt);
node.directives.push(directive);
@@ -704,8 +705,8 @@ pp.parseClassBody = function (node) {
// disallow invalid constructors
const isConstructor = !method.static && (
(key.type === "Identifier" && key.name === "constructor") ||
(key.type === "StringLiteral" && key.value === "constructor")
(key.name === "constructor") || // Identifier
(key.value === "constructor") // Literal
);
if (isConstructor) {
if (hadConstructor) this.raise(key.start, "Duplicate constructor in the same class");
@@ -718,8 +719,8 @@ pp.parseClassBody = function (node) {
// disallow static prototype method
const isStaticPrototype = method.static && (
(key.type === "Identifier" && key.name === "prototype") ||
(key.type === "StringLiteral" && key.value === "prototype")
(key.name === "prototype") || // Identifier
(key.value === "prototype") // Literal
);
if (isStaticPrototype) {
this.raise(key.start, "Classes may not have static property named prototype");
@@ -733,18 +734,8 @@ pp.parseClassBody = function (node) {
this.parseClassMethod(classBody, method, isGenerator, isAsync);
// get methods aren't allowed to have any parameters
// set methods must have exactly 1 parameter
if (isGetSet) {
const paramCount = method.kind === "get" ? 0 : 1;
if (method.params.length !== paramCount) {
const start = method.start;
if (method.kind === "get") {
this.raise(start, "getter should have no params");
} else {
this.raise(start, "setter should have exactly one param");
}
}
this.checkGetterSetterParamCount(method);
}
}
@@ -758,8 +749,13 @@ pp.parseClassBody = function (node) {
};
pp.parseClassProperty = function (node) {
const noPluginMsg = "You can only use Class Properties when the 'classProperties' plugin is enabled.";
if (!node.typeAnnotation && !this.hasPlugin("classProperties")) {
this.raise(node.start, noPluginMsg);
}
if (this.match(tt.eq)) {
if (!this.hasPlugin("classProperties")) this.unexpected();
if (!this.hasPlugin("classProperties")) this.raise(this.state.start, noPluginMsg);
this.next();
node.value = this.parseMaybeAssign();
} else {
@@ -827,6 +823,8 @@ pp.parseExport = function (node) {
let needsSemi = false;
if (this.eat(tt._function)) {
expr = this.parseFunction(expr, true, false, false, true);
} else if (this.eatContextual("async") && this.eat(tt._function)) {
expr = this.parseFunction(expr, true, false, true, true);
} else if (this.match(tt._class)) {
expr = this.parseClass(expr, true, true);
} else {
@@ -1003,7 +1001,7 @@ pp.parseExportSpecifiers = function () {
// Parses import declaration.
pp.parseImport = function (node) {
this.next();
this.eat(tt._import);
// import '...'
if (this.match(tt.string)) {
@@ -1046,6 +1044,11 @@ pp.parseImportSpecifiers = function (node) {
if (first) {
first = false;
} else {
// Detect an attempt to deep destructure
if (this.eat(tt.colon)) {
this.unexpected(null, "ES2015 named imports do not destructure. Use another statement for destructuring after the import.");
}
this.expect(tt.comma);
if (this.eat(tt.braceR)) break;
}

View File

@@ -1,10 +1,21 @@
/* eslint indent: 0 */
/* eslint max-len: 0 */
import { types as tt } from "../tokenizer/types";
import { types as ct } from "../tokenizer/context";
import Parser from "../parser";
const primitiveTypes = [
"any",
"mixed",
"empty",
"bool",
"boolean",
"number",
"string",
"void",
"null"
];
const pp = Parser.prototype;
pp.flowParseTypeInitialiser = function (tok) {
@@ -96,11 +107,22 @@ pp.flowParseDeclareModule = function (node) {
const body = bodyNode.body = [];
this.expect(tt.braceL);
while (!this.match(tt.braceR)) {
const node2 = this.startNode();
let bodyNode = this.startNode();
this.expectContextual("declare", "Unexpected token. Only declares are allowed inside declare module");
if (this.match(tt._import)) {
const lookahead = this.lookahead();
if (lookahead.value !== "type" && lookahead.value !== "typeof") {
this.unexpected(null, "Imports within a `declare module` body must always be `import type` or `import typeof`");
}
body.push(this.flowParseDeclare(node2));
this.parseImport(bodyNode);
} else {
this.expectContextual("declare", "Only declares and type imports are allowed inside declare module");
bodyNode = this.flowParseDeclare(bodyNode, true);
}
body.push(bodyNode);
}
this.expect(tt.braceR);
@@ -178,10 +200,18 @@ pp.flowParseInterface = function (node) {
return this.finishNode(node, "InterfaceDeclaration");
};
pp.flowParseRestrictedIdentifier = function(liberal) {
if (primitiveTypes.indexOf(this.state.value) > -1) {
this.raise(this.state.start, `Cannot overwrite primitive type ${this.state.value}`);
}
return this.parseIdentifier(liberal);
};
// Type aliases
pp.flowParseTypeAlias = function (node) {
node.id = this.parseIdentifier();
node.id = this.flowParseRestrictedIdentifier();
if (this.isRelational("<")) {
node.typeParameters = this.flowParseTypeParameterDeclaration();
@@ -209,7 +239,7 @@ pp.flowParseTypeParameter = function () {
if (this.match(tt.eq)) {
this.eat(tt.eq);
node.default = this.flowParseType ();
node.default = this.flowParseType();
}
return this.finishNode(node, "TypeParameter");
@@ -379,7 +409,7 @@ pp.flowParseObjectType = function (allowStatic, allowExact) {
if (variance) {
this.unexpected(variancePos);
}
nodeStart.callProperties.push(this.flowParseObjectTypeCallProperty(node, allowStatic));
nodeStart.callProperties.push(this.flowParseObjectTypeCallProperty(node, isStatic));
} else {
propertyKey = this.flowParseObjectPropertyKey();
if (this.isRelational("<") || this.match(tt.parenL)) {
@@ -763,7 +793,7 @@ pp.flowParseTypeAnnotation = function () {
};
pp.flowParseTypeAnnotatableIdentifier = function () {
const ident = this.parseIdentifier();
const ident = this.flowParseRestrictedIdentifier();
if (this.match(tt.colon)) {
ident.typeAnnotation = this.flowParseTypeAnnotation();
this.finishNode(ident, ident.type);
@@ -1055,8 +1085,8 @@ export default function (instance) {
});
// parse type parameters for class methods
instance.extend("parseClassMethod", function () {
return function (classBody, method, isGenerator, isAsync) {
instance.extend("parseClassMethod", function (inner) {
return function (classBody, method, ...args) {
if (method.variance) {
this.unexpected(method.variancePos);
}
@@ -1065,8 +1095,8 @@ export default function (instance) {
if (this.isRelational("<")) {
method.typeParameters = this.flowParseTypeParameterDeclaration();
}
this.parseMethod(method, isGenerator, isAsync);
classBody.body.push(this.finishNode(method, "ClassMethod"));
inner.call(this, classBody, method, ...args);
};
});
@@ -1084,9 +1114,9 @@ export default function (instance) {
const node = this.startNode();
node.id = this.parseIdentifier();
if (this.isRelational("<")) {
node.typeParameters = this.flowParseTypeParameterInstantiation();
node.typeParameters = this.flowParseTypeParameterInstantiation();
} else {
node.typeParameters = null;
node.typeParameters = null;
}
implemented.push(this.finishNode(node, "ClassImplements"));
} while (this.eat(tt.comma));
@@ -1227,6 +1257,13 @@ export default function (instance) {
specifier.local = specifier.imported.__clone();
}
if (
(node.importKind === "type" || node.importKind === "typeof") &&
(specifier.importKind === "type" || specifier.importKind === "typeof")
) {
this.raise(firstIdentLoc, "`The `type` and `typeof` keywords on named imports can only be used on regular `import` statements. It cannot be used with `import type` or `import typeof` statements`");
}
this.checkLVal(specifier.local, true, undefined, "import specifier");
node.specifiers.push(this.finishNode(specifier, "ImportSpecifier"));
};

View File

@@ -1,5 +1,3 @@
/* eslint indent: 0 */
import XHTMLEntities from "./xhtml";
import { TokenType, types as tt } from "../../tokenizer/types";
import { TokContext, types as tc } from "../../tokenizer/context";

View File

@@ -1,5 +1,4 @@
/* eslint max-len: 0 */
/* eslint indent: 0 */
import type { TokenType } from "./types";
import { isIdentifierStart, isIdentifierChar, isKeyword } from "../util/identifier";

View File

@@ -49,7 +49,9 @@ nonASCIIidentifierStartChars = nonASCIIidentifierChars = null;
// offset starts at 0x10000, and each pair of numbers represents an
// offset to the next range, and then a size of the range. They were
// generated by `bin/generate-identifier-regex.js`.
// eslint-disable-next-line comma-spacing
const astralIdentifierStartCodes = [0,11,2,25,2,18,2,1,2,14,3,13,35,122,70,52,268,28,4,48,48,31,17,26,6,37,11,29,3,35,5,7,2,4,43,157,19,35,5,35,5,39,9,51,157,310,10,21,11,7,153,5,3,0,2,43,2,1,4,0,3,22,11,22,10,30,66,18,2,1,11,21,11,25,71,55,7,1,65,0,16,3,2,2,2,26,45,28,4,28,36,7,2,27,28,53,11,21,11,18,14,17,111,72,56,50,14,50,785,52,76,44,33,24,27,35,42,34,4,0,13,47,15,3,22,0,2,0,36,17,2,24,85,6,2,0,2,3,2,14,2,9,8,46,39,7,3,1,3,21,2,6,2,1,2,4,4,0,19,0,13,4,159,52,19,3,54,47,21,1,2,0,185,46,42,3,37,47,21,0,60,42,86,25,391,63,32,0,449,56,264,8,2,36,18,0,50,29,881,921,103,110,18,195,2749,1070,4050,582,8634,568,8,30,114,29,19,47,17,3,32,20,6,18,881,68,12,0,67,12,65,0,32,6124,20,754,9486,1,3071,106,6,12,4,8,8,9,5991,84,2,70,2,1,3,0,3,1,3,3,2,11,2,0,2,6,2,64,2,3,3,7,2,6,2,27,2,3,2,4,2,0,4,6,2,339,3,24,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,7,4149,196,60,67,1213,3,2,26,2,1,2,0,3,0,2,9,2,3,2,0,2,0,7,0,5,0,2,0,2,0,2,2,2,1,2,0,3,0,2,0,2,0,2,0,2,0,2,1,2,0,3,3,2,6,2,3,2,3,2,0,2,9,2,16,6,2,2,4,2,16,4421,42710,42,4148,12,221,3,5761,10591,541];
// eslint-disable-next-line comma-spacing
const astralIdentifierCodes = [509,0,227,0,150,4,294,9,1368,2,2,1,6,3,41,2,5,0,166,1,1306,2,54,14,32,9,16,3,46,10,54,9,7,2,37,13,2,9,52,0,13,2,49,13,10,2,4,9,83,11,7,0,161,11,6,9,7,3,57,0,2,6,3,1,3,2,10,0,11,1,3,6,4,4,193,17,10,9,87,19,13,9,214,6,3,8,28,1,83,16,16,9,82,12,9,9,84,14,5,9,423,9,838,7,2,7,17,9,57,21,2,13,19882,9,135,4,60,6,26,9,1016,45,17,3,19723,1,5319,4,4,5,9,7,3,6,31,3,149,2,1418,49,513,54,5,49,9,0,15,0,23,4,2,14,1361,6,2,16,3,6,2,1,2,4,2214,6,110,6,6,9,792487,239];
// This has a complexity linear to the value of the code. The