Merge pull request #587 from peey/decorators-stage-2

Decorators Stage 2 Parsing
This commit is contained in:
Henry Zhu
2017-06-22 10:20:08 -04:00
committed by GitHub
48 changed files with 1572 additions and 4 deletions

View File

@@ -47,6 +47,11 @@ const parserClassCache: { [key: string]: Class<Parser> } = {};
/** Get a Parser class with plugins applied. */
function getParserClass(pluginsFromOptions: $ReadOnlyArray<string>): Class<Parser> {
if (pluginsFromOptions.indexOf("decorators") >= 0 && pluginsFromOptions.indexOf("decorators2") >= 0) {
throw new Error("Cannot use decorators and decorators2 plugin together");
}
// Filter out just the plugins that have an actual mixin associated with them.
let pluginList = pluginsFromOptions.filter((p) => p === "estree" || p === "flow" || p === "jsx");

View File

@@ -857,8 +857,16 @@ export default class ExpressionParser extends LValParser {
if (this.eat(tt.braceR)) break;
}
while (this.match(tt.at)) {
decorators.push(this.parseDecorator());
if (this.match(tt.at)) {
if (this.hasPlugin("decorators2")) {
this.raise(this.state.start, "Stage 2 decorators disallow object literal property decorators");
} else {
// we needn't check if decorators (stage 0) plugin is enabled since it's checked by
// the call to this.parseDecorator
while (this.match(tt.at)) {
decorators.push(this.parseDecorator());
}
}
}
let prop = this.startNode(), isGenerator = false, isAsync = false, startPos, startLoc;

View File

@@ -187,6 +187,9 @@ export default class LValParser extends NodeUtils {
break;
} else {
const decorators = [];
if (this.match(tt.at) && this.hasPlugin("decorators2")) {
this.raise(this.state.start, "Stage 2 decorators cannot be used to decorate parameters");
}
while (this.match(tt.at)) {
decorators.push(this.parseDecorator());
}

View File

@@ -152,11 +152,18 @@ export default class StatementParser extends ExpressionParser {
takeDecorators(node: N.HasDecorators): void {
if (this.state.decorators.length) {
node.decorators = this.state.decorators;
if (this.hasPlugin("decorators2")) {
this.resetStartLocationFromNode(node, this.state.decorators[0]);
}
this.state.decorators = [];
}
}
parseDecorators(allowExport?: boolean): void {
if (this.hasPlugin("decorators2")) {
allowExport = false;
}
while (this.match(tt.at)) {
const decorator = this.parseDecorator();
this.state.decorators.push(decorator);
@@ -172,12 +179,38 @@ export default class StatementParser extends ExpressionParser {
}
parseDecorator(): N.Decorator {
if (!this.hasPlugin("decorators")) {
if (!(this.hasPlugin("decorators") || this.hasPlugin("decorators2"))) {
this.unexpected();
}
const node = this.startNode();
this.next();
node.expression = this.parseMaybeAssign();
if (this.hasPlugin("decorators2")) {
const startPos = this.state.start;
const startLoc = this.state.startLoc;
let expr = this.parseIdentifier(false);
while (this.eat(tt.dot)) {
const node = this.startNodeAt(startPos, startLoc);
node.object = expr;
node.property = this.parseIdentifier(true);
node.computed = false;
expr = this.finishNode(node, "MemberExpression");
}
if (this.eat(tt.parenL)) {
const node = this.startNodeAt(startPos, startLoc);
node.callee = expr;
node.arguments = this.parseCallExpressionArguments(tt.parenR, false);
expr = this.finishNode(node, "CallExpression");
this.toReferencedList(expr.arguments);
}
node.expression = expr;
} else {
node.expression = this.parseMaybeAssign();
}
return this.finishNode(node, "Decorator");
}
@@ -679,10 +712,17 @@ export default class StatementParser extends ExpressionParser {
// steal the decorators if there are any
if (decorators.length) {
member.decorators = decorators;
if (this.hasPlugin("decorators2")) {
this.resetStartLocationFromNode(member, decorators[0]);
}
decorators = [];
}
this.parseClassMember(classBody, member, state);
if (this.hasPlugin("decorators2") && member.kind != "method" && member.decorators && member.decorators.length > 0) {
this.raise(member.start, "Stage 2 decorators may only be used with a class or a class method");
}
}
if (decorators.length) {
@@ -750,6 +790,7 @@ export default class StatementParser extends ExpressionParser {
if (!methodOrProp.computed && methodOrProp.static && (methodOrProp.key.name === "prototype" || methodOrProp.key.value === "prototype")) {
this.raise(methodOrProp.key.start, "Classes may not have static property named prototype");
}
if (this.isClassMethod()) {
// a normal method
if (this.isNonstaticConstructor(method)) {