UNPKG

33.5 kBJavaScriptView Raw
1/* @flow */
2
3"use strict";
4
5var _Object$create = require("babel-runtime/core-js/object/create")["default"];
6
7var _getIterator = require("babel-runtime/core-js/get-iterator")["default"];
8
9var _interopRequireDefault = require("babel-runtime/helpers/interop-require-default")["default"];
10
11var _tokenizerTypes = require("../tokenizer/types");
12
13var _index = require("./index");
14
15var _index2 = _interopRequireDefault(_index);
16
17var _utilWhitespace = require("../util/whitespace");
18
19var pp = _index2["default"].prototype;
20
21// ### Statement parsing
22
23// Parse a program. Initializes the parser, reads any number of
24// statements, and wraps them in a Program node. Optionally takes a
25// `program` argument. If present, the statements will be appended
26// to its body instead of creating a new node.
27
28pp.parseTopLevel = function (file, program) {
29 program.sourceType = this.options.sourceType;
30
31 this.parseBlockBody(program, true, true, _tokenizerTypes.types.eof);
32
33 file.program = this.finishNode(program, "Program");
34 file.comments = this.state.comments;
35 file.tokens = this.state.tokens;
36
37 return this.finishNode(file, "File");
38};
39
40var loopLabel = { kind: "loop" },
41 switchLabel = { kind: "switch" };
42
43// TODO
44
45pp.parseDirective = function () {
46 var directiveLiteral = this.startNode();
47 var directive = this.startNode();
48
49 var raw = this.input.slice(this.state.start, this.state.end);
50 var val = directiveLiteral.value = raw.slice(1, -1); // remove quotes
51
52 this.addExtra(directiveLiteral, "raw", raw);
53 this.addExtra(directiveLiteral, "rawValue", val);
54
55 this.next();
56
57 directive.value = this.finishNode(directiveLiteral, "DirectiveLiteral");
58
59 this.semicolon();
60 return this.finishNode(directive, "Directive");
61};
62
63// Parse a single statement.
64//
65// If expecting a statement and finding a slash operator, parse a
66// regular expression literal. This is to handle cases like
67// `if (foo) /blah/.exec(foo)`, where looking at the previous token
68// does not help.
69
70pp.parseStatement = function (declaration, topLevel) {
71 if (this.match(_tokenizerTypes.types.at)) {
72 this.parseDecorators(true);
73 }
74
75 var starttype = this.state.type,
76 node = this.startNode();
77
78 // Most types of statements are recognized by the keyword they
79 // start with. Many are trivial to parse, some require a bit of
80 // complexity.
81
82 switch (starttype) {
83 case _tokenizerTypes.types._break:case _tokenizerTypes.types._continue:
84 return this.parseBreakContinueStatement(node, starttype.keyword);
85 case _tokenizerTypes.types._debugger:
86 return this.parseDebuggerStatement(node);
87 case _tokenizerTypes.types._do:
88 return this.parseDoStatement(node);
89 case _tokenizerTypes.types._for:
90 return this.parseForStatement(node);
91 case _tokenizerTypes.types._function:
92 if (!declaration) this.unexpected();
93 return this.parseFunctionStatement(node);
94
95 case _tokenizerTypes.types._class:
96 if (!declaration) this.unexpected();
97 this.takeDecorators(node);
98 return this.parseClass(node, true);
99
100 case _tokenizerTypes.types._if:
101 return this.parseIfStatement(node);
102 case _tokenizerTypes.types._return:
103 return this.parseReturnStatement(node);
104 case _tokenizerTypes.types._switch:
105 return this.parseSwitchStatement(node);
106 case _tokenizerTypes.types._throw:
107 return this.parseThrowStatement(node);
108 case _tokenizerTypes.types._try:
109 return this.parseTryStatement(node);
110
111 case _tokenizerTypes.types._let:
112 case _tokenizerTypes.types._const:
113 if (!declaration) this.unexpected(); // NOTE: falls through to _var
114
115 case _tokenizerTypes.types._var:
116 return this.parseVarStatement(node, starttype);
117
118 case _tokenizerTypes.types._while:
119 return this.parseWhileStatement(node);
120 case _tokenizerTypes.types._with:
121 return this.parseWithStatement(node);
122 case _tokenizerTypes.types.braceL:
123 return this.parseBlock();
124 case _tokenizerTypes.types.semi:
125 return this.parseEmptyStatement(node);
126 case _tokenizerTypes.types._export:
127 case _tokenizerTypes.types._import:
128 if (!this.options.allowImportExportEverywhere) {
129 if (!topLevel) {
130 this.raise(this.state.start, "'import' and 'export' may only appear at the top level");
131 }
132
133 if (!this.inModule) {
134 this.raise(this.state.start, "'import' and 'export' may appear only with 'sourceType: module'");
135 }
136 }
137 return starttype === _tokenizerTypes.types._import ? this.parseImport(node) : this.parseExport(node);
138
139 case _tokenizerTypes.types.name:
140 if (this.hasPlugin("asyncFunctions") && this.state.value === "async") {
141 // peek ahead and see if next token is a function
142 var state = this.state.clone();
143 this.next();
144 if (this.match(_tokenizerTypes.types._function) && !this.canInsertSemicolon()) {
145 this.expect(_tokenizerTypes.types._function);
146 return this.parseFunction(node, true, false, true);
147 } else {
148 this.state = state;
149 }
150 }
151 }
152
153 // If the statement does not start with a statement keyword or a
154 // brace, it's an ExpressionStatement or LabeledStatement. We
155 // simply start parsing an expression, and afterwards, if the
156 // next token is a colon and the expression was a simple
157 // Identifier node, we switch to interpreting it as a label.
158 var maybeName = this.state.value;
159 var expr = this.parseExpression();
160
161 if (starttype === _tokenizerTypes.types.name && expr.type === "Identifier" && this.eat(_tokenizerTypes.types.colon)) {
162 return this.parseLabeledStatement(node, maybeName, expr);
163 } else {
164 return this.parseExpressionStatement(node, expr);
165 }
166};
167
168pp.takeDecorators = function (node) {
169 if (this.state.decorators.length) {
170 node.decorators = this.state.decorators;
171 this.state.decorators = [];
172 }
173};
174
175pp.parseDecorators = function (allowExport) {
176 while (this.match(_tokenizerTypes.types.at)) {
177 this.state.decorators.push(this.parseDecorator());
178 }
179
180 if (allowExport && this.match(_tokenizerTypes.types._export)) {
181 return;
182 }
183
184 if (!this.match(_tokenizerTypes.types._class)) {
185 this.raise(this.state.start, "Leading decorators must be attached to a class declaration");
186 }
187};
188
189pp.parseDecorator = function () {
190 if (!this.hasPlugin("decorators")) {
191 this.unexpected();
192 }
193 var node = this.startNode();
194 this.next();
195 node.expression = this.parseMaybeAssign();
196 return this.finishNode(node, "Decorator");
197};
198
199pp.parseBreakContinueStatement = function (node, keyword) {
200 var isBreak = keyword === "break";
201 this.next();
202
203 if (this.isLineTerminator()) {
204 node.label = null;
205 } else if (!this.match(_tokenizerTypes.types.name)) {
206 this.unexpected();
207 } else {
208 node.label = this.parseIdentifier();
209 this.semicolon();
210 }
211
212 // Verify that there is an actual destination to break or
213 // continue to.
214 var i = undefined;
215 for (i = 0; i < this.state.labels.length; ++i) {
216 var lab = this.state.labels[i];
217 if (node.label == null || lab.name === node.label.name) {
218 if (lab.kind != null && (isBreak || lab.kind === "loop")) break;
219 if (node.label && isBreak) break;
220 }
221 }
222 if (i === this.state.labels.length) this.raise(node.start, "Unsyntactic " + keyword);
223 return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement");
224};
225
226pp.parseDebuggerStatement = function (node) {
227 this.next();
228 this.semicolon();
229 return this.finishNode(node, "DebuggerStatement");
230};
231
232pp.parseDoStatement = function (node) {
233 this.next();
234 this.state.labels.push(loopLabel);
235 node.body = this.parseStatement(false);
236 this.state.labels.pop();
237 this.expect(_tokenizerTypes.types._while);
238 node.test = this.parseParenExpression();
239 this.eat(_tokenizerTypes.types.semi);
240 return this.finishNode(node, "DoWhileStatement");
241};
242
243// Disambiguating between a `for` and a `for`/`in` or `for`/`of`
244// loop is non-trivial. Basically, we have to parse the init `var`
245// statement or expression, disallowing the `in` operator (see
246// the second parameter to `parseExpression`), and then check
247// whether the next token is `in` or `of`. When there is no init
248// part (semicolon immediately after the opening parenthesis), it
249// is a regular `for` loop.
250
251pp.parseForStatement = function (node) {
252 this.next();
253 this.state.labels.push(loopLabel);
254 this.expect(_tokenizerTypes.types.parenL);
255
256 if (this.match(_tokenizerTypes.types.semi)) {
257 return this.parseFor(node, null);
258 }
259
260 if (this.match(_tokenizerTypes.types._var) || this.match(_tokenizerTypes.types._let) || this.match(_tokenizerTypes.types._const)) {
261 var _init = this.startNode(),
262 varKind = this.state.type;
263 this.next();
264 this.parseVar(_init, true, varKind);
265 this.finishNode(_init, "VariableDeclaration");
266
267 if (this.match(_tokenizerTypes.types._in) || this.isContextual("of")) {
268 if (_init.declarations.length === 1 && !_init.declarations[0].init) {
269 return this.parseForIn(node, _init);
270 }
271 }
272
273 return this.parseFor(node, _init);
274 }
275
276 var refShorthandDefaultPos = { start: 0 };
277 var init = this.parseExpression(true, refShorthandDefaultPos);
278 if (this.match(_tokenizerTypes.types._in) || this.isContextual("of")) {
279 this.toAssignable(init);
280 this.checkLVal(init);
281 return this.parseForIn(node, init);
282 } else if (refShorthandDefaultPos.start) {
283 this.unexpected(refShorthandDefaultPos.start);
284 }
285 return this.parseFor(node, init);
286};
287
288pp.parseFunctionStatement = function (node) {
289 this.next();
290 return this.parseFunction(node, true);
291};
292
293pp.parseIfStatement = function (node) {
294 this.next();
295 node.test = this.parseParenExpression();
296 node.consequent = this.parseStatement(false);
297 node.alternate = this.eat(_tokenizerTypes.types._else) ? this.parseStatement(false) : null;
298 return this.finishNode(node, "IfStatement");
299};
300
301pp.parseReturnStatement = function (node) {
302 if (!this.state.inFunction && !this.options.allowReturnOutsideFunction) {
303 this.raise(this.state.start, "'return' outside of function");
304 }
305
306 this.next();
307
308 // In `return` (and `break`/`continue`), the keywords with
309 // optional arguments, we eagerly look for a semicolon or the
310 // possibility to insert one.
311
312 if (this.isLineTerminator()) {
313 node.argument = null;
314 } else {
315 node.argument = this.parseExpression();
316 this.semicolon();
317 }
318
319 return this.finishNode(node, "ReturnStatement");
320};
321
322pp.parseSwitchStatement = function (node) {
323 this.next();
324 node.discriminant = this.parseParenExpression();
325 node.cases = [];
326 this.expect(_tokenizerTypes.types.braceL);
327 this.state.labels.push(switchLabel);
328
329 // Statements under must be grouped (by label) in SwitchCase
330 // nodes. `cur` is used to keep the node that we are currently
331 // adding statements to.
332
333 var cur = undefined;
334 for (var sawDefault = undefined; !this.match(_tokenizerTypes.types.braceR);) {
335 if (this.match(_tokenizerTypes.types._case) || this.match(_tokenizerTypes.types._default)) {
336 var isCase = this.match(_tokenizerTypes.types._case);
337 if (cur) this.finishNode(cur, "SwitchCase");
338 node.cases.push(cur = this.startNode());
339 cur.consequent = [];
340 this.next();
341 if (isCase) {
342 cur.test = this.parseExpression();
343 } else {
344 if (sawDefault) this.raise(this.state.lastTokStart, "Multiple default clauses");
345 sawDefault = true;
346 cur.test = null;
347 }
348 this.expect(_tokenizerTypes.types.colon);
349 } else {
350 if (cur) {
351 cur.consequent.push(this.parseStatement(true));
352 } else {
353 this.unexpected();
354 }
355 }
356 }
357 if (cur) this.finishNode(cur, "SwitchCase");
358 this.next(); // Closing brace
359 this.state.labels.pop();
360 return this.finishNode(node, "SwitchStatement");
361};
362
363pp.parseThrowStatement = function (node) {
364 this.next();
365 if (_utilWhitespace.lineBreak.test(this.input.slice(this.state.lastTokEnd, this.state.start))) this.raise(this.state.lastTokEnd, "Illegal newline after throw");
366 node.argument = this.parseExpression();
367 this.semicolon();
368 return this.finishNode(node, "ThrowStatement");
369};
370
371// Reused empty array added for node fields that are always empty.
372
373var empty = [];
374
375pp.parseTryStatement = function (node) {
376 this.next();
377
378 node.block = this.parseBlock();
379 node.handler = null;
380
381 if (this.match(_tokenizerTypes.types._catch)) {
382 var clause = this.startNode();
383 this.next();
384
385 this.expect(_tokenizerTypes.types.parenL);
386 clause.param = this.parseBindingAtom();
387 this.checkLVal(clause.param, true, _Object$create(null));
388 this.expect(_tokenizerTypes.types.parenR);
389
390 clause.body = this.parseBlock();
391 node.handler = this.finishNode(clause, "CatchClause");
392 }
393
394 node.guardedHandlers = empty;
395 node.finalizer = this.eat(_tokenizerTypes.types._finally) ? this.parseBlock() : null;
396
397 if (!node.handler && !node.finalizer) {
398 this.raise(node.start, "Missing catch or finally clause");
399 }
400
401 return this.finishNode(node, "TryStatement");
402};
403
404pp.parseVarStatement = function (node, kind) {
405 this.next();
406 this.parseVar(node, false, kind);
407 this.semicolon();
408 return this.finishNode(node, "VariableDeclaration");
409};
410
411pp.parseWhileStatement = function (node) {
412 this.next();
413 node.test = this.parseParenExpression();
414 this.state.labels.push(loopLabel);
415 node.body = this.parseStatement(false);
416 this.state.labels.pop();
417 return this.finishNode(node, "WhileStatement");
418};
419
420pp.parseWithStatement = function (node) {
421 if (this.state.strict) this.raise(this.state.start, "'with' in strict mode");
422 this.next();
423 node.object = this.parseParenExpression();
424 node.body = this.parseStatement(false);
425 return this.finishNode(node, "WithStatement");
426};
427
428pp.parseEmptyStatement = function (node) {
429 this.next();
430 return this.finishNode(node, "EmptyStatement");
431};
432
433pp.parseLabeledStatement = function (node, maybeName, expr) {
434 for (var _iterator = (this.state.labels /*: Array<Object>*/), _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _getIterator(_iterator);;) {
435 var _ref;
436
437 if (_isArray) {
438 if (_i >= _iterator.length) break;
439 _ref = _iterator[_i++];
440 } else {
441 _i = _iterator.next();
442 if (_i.done) break;
443 _ref = _i.value;
444 }
445
446 var label = _ref;
447
448 if (label.name === maybeName) {
449 this.raise(expr.start, "Label '" + maybeName + "' is already declared");
450 }
451 }
452
453 var kind = this.state.type.isLoop ? "loop" : this.match(_tokenizerTypes.types._switch) ? "switch" : null;
454 for (var i = this.state.labels.length - 1; i >= 0; i--) {
455 var label = this.state.labels[i];
456 if (label.statementStart === node.start) {
457 label.statementStart = this.state.start;
458 label.kind = kind;
459 } else {
460 break;
461 }
462 }
463
464 this.state.labels.push({ name: maybeName, kind: kind, statementStart: this.state.start });
465 node.body = this.parseStatement(true);
466 this.state.labels.pop();
467 node.label = expr;
468 return this.finishNode(node, "LabeledStatement");
469};
470
471pp.parseExpressionStatement = function (node, expr) {
472 node.expression = expr;
473 this.semicolon();
474 return this.finishNode(node, "ExpressionStatement");
475};
476
477// Parse a semicolon-enclosed block of statements, handling `"use
478// strict"` declarations when `allowStrict` is true (used for
479// function bodies).
480
481pp.parseBlock = function (allowDirectives /*:: ?*/) {
482 var node = this.startNode();
483 this.expect(_tokenizerTypes.types.braceL);
484 this.parseBlockBody(node, allowDirectives, false, _tokenizerTypes.types.braceR);
485 return this.finishNode(node, "BlockStatement");
486};
487
488// TODO
489
490pp.parseBlockBody = function (node, allowDirectives, topLevel, end) {
491 node.body = [];
492 node.directives = [];
493
494 var parsedNonDirective = false;
495 var oldStrict = undefined;
496 var octalPosition = undefined;
497
498 while (!this.eat(end)) {
499 if (allowDirectives && !parsedNonDirective && this.match(_tokenizerTypes.types.string)) {
500 var oldState = this.state;
501 var lookahead = this.lookahead();
502 this.state = lookahead;
503 var isDirective = this.isLineTerminator();
504 this.state = oldState;
505
506 if (isDirective) {
507 if (this.state.containsOctal && !octalPosition) {
508 octalPosition = this.state.octalPosition;
509 }
510
511 var stmt = this.parseDirective();
512 node.directives.push(stmt);
513
514 if (allowDirectives && stmt.value.value === "use strict") {
515 oldStrict = this.state.strict;
516 this.state.strict = true;
517 this.setStrict(true);
518
519 if (octalPosition) {
520 this.raise(octalPosition, "Octal literal in strict mode");
521 }
522 }
523
524 continue;
525 }
526 }
527
528 parsedNonDirective = true;
529 node.body.push(this.parseStatement(true, topLevel));
530 }
531
532 if (oldStrict === false) {
533 this.setStrict(false);
534 }
535};
536
537// Parse a regular `for` loop. The disambiguation code in
538// `parseStatement` will already have parsed the init statement or
539// expression.
540
541pp.parseFor = function (node, init) {
542 node.init = init;
543 this.expect(_tokenizerTypes.types.semi);
544 node.test = this.match(_tokenizerTypes.types.semi) ? null : this.parseExpression();
545 this.expect(_tokenizerTypes.types.semi);
546 node.update = this.match(_tokenizerTypes.types.parenR) ? null : this.parseExpression();
547 this.expect(_tokenizerTypes.types.parenR);
548 node.body = this.parseStatement(false);
549 this.state.labels.pop();
550 return this.finishNode(node, "ForStatement");
551};
552
553// Parse a `for`/`in` and `for`/`of` loop, which are almost
554// same from parser's perspective.
555
556pp.parseForIn = function (node, init) {
557 var type = this.match(_tokenizerTypes.types._in) ? "ForInStatement" : "ForOfStatement";
558 this.next();
559 node.left = init;
560 node.right = this.parseExpression();
561 this.expect(_tokenizerTypes.types.parenR);
562 node.body = this.parseStatement(false);
563 this.state.labels.pop();
564 return this.finishNode(node, type);
565};
566
567// Parse a list of variable declarations.
568
569pp.parseVar = function (node, isFor, kind) {
570 node.declarations = [];
571 node.kind = kind.keyword;
572 for (;;) {
573 var decl = this.startNode();
574 this.parseVarHead(decl);
575 if (this.eat(_tokenizerTypes.types.eq)) {
576 decl.init = this.parseMaybeAssign(isFor);
577 } else if (kind === _tokenizerTypes.types._const && !(this.match(_tokenizerTypes.types._in) || this.isContextual("of"))) {
578 this.unexpected();
579 } else if (decl.id.type !== "Identifier" && !(isFor && (this.match(_tokenizerTypes.types._in) || this.isContextual("of")))) {
580 this.raise(this.state.lastTokEnd, "Complex binding patterns require an initialization value");
581 } else {
582 decl.init = null;
583 }
584 node.declarations.push(this.finishNode(decl, "VariableDeclarator"));
585 if (!this.eat(_tokenizerTypes.types.comma)) break;
586 }
587 return node;
588};
589
590pp.parseVarHead = function (decl) {
591 decl.id = this.parseBindingAtom();
592 this.checkLVal(decl.id, true);
593};
594
595// Parse a function declaration or literal (depending on the
596// `isStatement` parameter).
597
598pp.parseFunction = function (node, isStatement, allowExpressionBody, isAsync, optionalId) {
599 var oldInMethod = this.state.inMethod;
600 this.state.inMethod = false;
601
602 this.initFunction(node, isAsync);
603
604 if (this.match(_tokenizerTypes.types.star)) {
605 if (node.async && !this.hasPlugin("asyncGenerators")) {
606 this.unexpected();
607 } else {
608 node.generator = true;
609 this.next();
610 }
611 }
612
613 if (isStatement && !optionalId && !this.match(_tokenizerTypes.types.name) && !this.match(_tokenizerTypes.types._yield)) {
614 this.unexpected();
615 }
616
617 if (this.match(_tokenizerTypes.types.name) || this.match(_tokenizerTypes.types._yield)) {
618 node.id = this.parseBindingIdentifier();
619 }
620
621 this.parseFunctionParams(node);
622 this.parseFunctionBody(node, allowExpressionBody);
623
624 this.state.inMethod = oldInMethod;
625
626 return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression");
627};
628
629pp.parseFunctionParams = function (node) {
630 this.expect(_tokenizerTypes.types.parenL);
631 node.params = this.parseBindingList(_tokenizerTypes.types.parenR, false, this.hasPlugin("trailingFunctionCommas"));
632};
633
634// Parse a class declaration or literal (depending on the
635// `isStatement` parameter).
636
637pp.parseClass = function (node, isStatement, optionalId) {
638 this.next();
639 this.parseClassId(node, isStatement, optionalId);
640 this.parseClassSuper(node);
641 this.parseClassBody(node);
642 return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression");
643};
644
645pp.isClassProperty = function () {
646 return this.match(_tokenizerTypes.types.eq) || this.isLineTerminator();
647};
648
649pp.parseClassBody = function (node) {
650 // class bodies are implicitly strict
651 var oldStrict = this.state.strict;
652 this.state.strict = true;
653
654 var hadConstructorCall = false;
655 var hadConstructor = false;
656 var decorators = [];
657 var classBody = this.startNode();
658
659 classBody.body = [];
660
661 this.expect(_tokenizerTypes.types.braceL);
662
663 while (!this.eat(_tokenizerTypes.types.braceR)) {
664 if (this.eat(_tokenizerTypes.types.semi)) {
665 continue;
666 }
667
668 if (this.match(_tokenizerTypes.types.at)) {
669 decorators.push(this.parseDecorator());
670 continue;
671 }
672
673 var method = this.startNode();
674
675 // steal the decorators if there are any
676 if (decorators.length) {
677 method.decorators = decorators;
678 decorators = [];
679 }
680
681 var isConstructorCall = false;
682 var isMaybeStatic = this.match(_tokenizerTypes.types.name) && this.state.value === "static";
683 var isGenerator = this.eat(_tokenizerTypes.types.star);
684 var isGetSet = false;
685 var isAsync = false;
686
687 this.parsePropertyName(method);
688
689 method["static"] = isMaybeStatic && !this.match(_tokenizerTypes.types.parenL);
690 if (method["static"]) {
691 if (isGenerator) this.unexpected();
692 isGenerator = this.eat(_tokenizerTypes.types.star);
693 this.parsePropertyName(method);
694 }
695
696 if (!isGenerator && method.key.type === "Identifier" && !method.computed) {
697 if (this.isClassProperty()) {
698 classBody.body.push(this.parseClassProperty(method));
699 continue;
700 }
701
702 if (this.hasPlugin("classConstructorCall") && method.key.name === "call" && this.match(_tokenizerTypes.types.name) && this.state.value === "constructor") {
703 isConstructorCall = true;
704 this.parsePropertyName(method);
705 }
706 }
707
708 var isAsyncMethod = this.hasPlugin("asyncFunctions") && !this.match(_tokenizerTypes.types.parenL) && !method.computed && method.key.type === "Identifier" && method.key.name === "async";
709 if (isAsyncMethod) {
710 if (this.hasPlugin("asyncGenerators") && this.eat(_tokenizerTypes.types.star)) isGenerator = true;
711 isAsync = true;
712 this.parsePropertyName(method);
713 }
714
715 method.kind = "method";
716
717 if (!method.computed) {
718 var key = method.key;
719
720 // handle get/set methods
721 // eg. class Foo { get bar() {} set bar() {} }
722 if (!isAsync && !isGenerator && key.type === "Identifier" && !this.match(_tokenizerTypes.types.parenL) && (key.name === "get" || key.name === "set")) {
723 isGetSet = true;
724 method.kind = key.name;
725 key = this.parsePropertyName(method);
726 }
727
728 // disallow invalid constructors
729 var isConstructor = !isConstructorCall && !method["static"] && (key.type === "Identifier" && key.name === "constructor" || key.type === "StringLiteral" && key.value === "constructor");
730 if (isConstructor) {
731 if (hadConstructor) this.raise(key.start, "Duplicate constructor in the same class");
732 if (isGetSet) this.raise(key.start, "Constructor can't have get/set modifier");
733 if (isGenerator) this.raise(key.start, "Constructor can't be a generator");
734 if (isAsync) this.raise(key.start, "Constructor can't be an async function");
735 method.kind = "constructor";
736 hadConstructor = true;
737 }
738
739 // disallow static prototype method
740 var isStaticPrototype = method["static"] && (key.type === "Identifier" && key.name === "prototype" || key.type === "StringLiteral" && key.value === "prototype");
741 if (isStaticPrototype) {
742 this.raise(key.start, "Classes may not have static property named prototype");
743 }
744 }
745
746 // convert constructor to a constructor call
747 if (isConstructorCall) {
748 if (hadConstructorCall) this.raise(method.start, "Duplicate constructor call in the same class");
749 method.kind = "constructorCall";
750 hadConstructorCall = true;
751 }
752
753 // disallow decorators on class constructors
754 if ((method.kind === "constructor" || method.kind === "constructorCall") && method.decorators) {
755 this.raise(method.start, "You can't attach decorators to a class constructor");
756 }
757
758 this.parseClassMethod(classBody, method, isGenerator, isAsync);
759
760 // get methods aren't allowed to have any parameters
761 // set methods must have exactly 1 parameter
762 if (isGetSet) {
763 var paramCount = method.kind === "get" ? 0 : 1;
764 if (method.params.length !== paramCount) {
765 var start = method.start;
766 if (method.kind === "get") {
767 this.raise(start, "getter should have no params");
768 } else {
769 this.raise(start, "setter should have exactly one param");
770 }
771 }
772 }
773 }
774
775 if (decorators.length) {
776 this.raise(this.state.start, "You have trailing decorators with no method");
777 }
778
779 node.body = this.finishNode(classBody, "ClassBody");
780
781 this.state.strict = oldStrict;
782};
783
784pp.parseClassProperty = function (node) {
785 if (this.match(_tokenizerTypes.types.eq)) {
786 if (!this.hasPlugin("classProperties")) this.unexpected();
787 this.next();
788 node.value = this.parseMaybeAssign();
789 } else {
790 node.value = null;
791 }
792 this.semicolon();
793 return this.finishNode(node, "ClassProperty");
794};
795
796pp.parseClassMethod = function (classBody, method, isGenerator, isAsync) {
797 this.parseMethod(method, isGenerator, isAsync);
798 classBody.body.push(this.finishNode(method, "ClassMethod"));
799};
800
801pp.parseClassId = function (node, isStatement, optionalId) {
802 if (this.match(_tokenizerTypes.types.name)) {
803 node.id = this.parseIdentifier();
804 } else {
805 if (optionalId || !isStatement) {
806 node.id = null;
807 } else {
808 this.unexpected();
809 }
810 }
811};
812
813pp.parseClassSuper = function (node) {
814 node.superClass = this.eat(_tokenizerTypes.types._extends) ? this.parseExprSubscripts() : null;
815};
816
817// Parses module export declaration.
818
819pp.parseExport = function (node) {
820 this.next();
821 // export * from '...'
822 if (this.match(_tokenizerTypes.types.star)) {
823 var specifier = this.startNode();
824 this.next();
825 if (this.hasPlugin("exportExtensions") && this.eatContextual("as")) {
826 specifier.exported = this.parseIdentifier();
827 node.specifiers = [this.finishNode(specifier, "ExportNamespaceSpecifier")];
828 this.parseExportSpecifiersMaybe(node);
829 this.parseExportFrom(node, true);
830 } else {
831 this.parseExportFrom(node, true);
832 return this.finishNode(node, "ExportAllDeclaration");
833 }
834 } else if (this.hasPlugin("exportExtensions") && this.isExportDefaultSpecifier()) {
835 var specifier = this.startNode();
836 specifier.exported = this.parseIdentifier(true);
837 node.specifiers = [this.finishNode(specifier, "ExportDefaultSpecifier")];
838 if (this.match(_tokenizerTypes.types.comma) && this.lookahead().type === _tokenizerTypes.types.star) {
839 this.expect(_tokenizerTypes.types.comma);
840 var _specifier = this.startNode();
841 this.expect(_tokenizerTypes.types.star);
842 this.expectContextual("as");
843 _specifier.exported = this.parseIdentifier();
844 node.specifiers.push(this.finishNode(_specifier, "ExportNamespaceSpecifier"));
845 } else {
846 this.parseExportSpecifiersMaybe(node);
847 }
848 this.parseExportFrom(node, true);
849 } else if (this.eat(_tokenizerTypes.types._default)) {
850 // export default ...
851 var expr = this.startNode();
852 var needsSemi = false;
853 if (this.eat(_tokenizerTypes.types._function)) {
854 expr = this.parseFunction(expr, true, false, false, true);
855 } else if (this.match(_tokenizerTypes.types._class)) {
856 expr = this.parseClass(expr, true, true);
857 } else {
858 needsSemi = true;
859 expr = this.parseMaybeAssign();
860 }
861 node.declaration = expr;
862 if (needsSemi) this.semicolon();
863 this.checkExport(node);
864 return this.finishNode(node, "ExportDefaultDeclaration");
865 } else if (this.state.type.keyword || this.shouldParseExportDeclaration()) {
866 node.specifiers = [];
867 node.source = null;
868 node.declaration = this.parseExportDeclaration(node);
869 } else {
870 // export { x, y as z } [from '...']
871 node.declaration = null;
872 node.specifiers = this.parseExportSpecifiers();
873 this.parseExportFrom(node);
874 }
875 this.checkExport(node);
876 return this.finishNode(node, "ExportNamedDeclaration");
877};
878
879pp.parseExportDeclaration = function () {
880 return this.parseStatement(true);
881};
882
883pp.isExportDefaultSpecifier = function () {
884 if (this.match(_tokenizerTypes.types.name)) {
885 return this.state.value !== "type" && this.state.value !== "async";
886 }
887
888 if (!this.match(_tokenizerTypes.types._default)) {
889 return false;
890 }
891
892 var lookahead = this.lookahead();
893 return lookahead.type === _tokenizerTypes.types.comma || lookahead.type === _tokenizerTypes.types.name && lookahead.value === "from";
894};
895
896pp.parseExportSpecifiersMaybe = function (node) {
897 if (this.eat(_tokenizerTypes.types.comma)) {
898 node.specifiers = node.specifiers.concat(this.parseExportSpecifiers());
899 }
900};
901
902pp.parseExportFrom = function (node, expect /*:: ?*/) {
903 if (this.eatContextual("from")) {
904 node.source = this.match(_tokenizerTypes.types.string) ? this.parseExprAtom() : this.unexpected();
905 this.checkExport(node);
906 } else {
907 if (expect) {
908 this.unexpected();
909 } else {
910 node.source = null;
911 }
912 }
913
914 this.semicolon();
915};
916
917pp.shouldParseExportDeclaration = function () {
918 return this.hasPlugin("asyncFunctions") && this.isContextual("async");
919};
920
921pp.checkExport = function (node) {
922 if (this.state.decorators.length) {
923 var isClass = node.declaration && (node.declaration.type === "ClassDeclaration" || node.declaration.type === "ClassExpression");
924 if (!node.declaration || !isClass) {
925 this.raise(node.start, "You can only use decorators on an export when exporting a class");
926 }
927 this.takeDecorators(node.declaration);
928 }
929};
930
931// Parses a comma-separated list of module exports.
932
933pp.parseExportSpecifiers = function () {
934 var nodes = [];
935 var first = true;
936 var needsFrom = undefined;
937
938 // export { x, y as z } [from '...']
939 this.expect(_tokenizerTypes.types.braceL);
940
941 while (!this.eat(_tokenizerTypes.types.braceR)) {
942 if (first) {
943 first = false;
944 } else {
945 this.expect(_tokenizerTypes.types.comma);
946 if (this.eat(_tokenizerTypes.types.braceR)) break;
947 }
948
949 var isDefault = this.match(_tokenizerTypes.types._default);
950 if (isDefault && !needsFrom) needsFrom = true;
951
952 var node = this.startNode();
953 node.local = this.parseIdentifier(isDefault);
954 node.exported = this.eatContextual("as") ? this.parseIdentifier(true) : node.local.__clone();
955 nodes.push(this.finishNode(node, "ExportSpecifier"));
956 }
957
958 // https://github.com/ember-cli/ember-cli/pull/3739
959 if (needsFrom && !this.isContextual("from")) {
960 this.unexpected();
961 }
962
963 return nodes;
964};
965
966// Parses import declaration.
967
968pp.parseImport = function (node) {
969 this.next();
970
971 // import '...'
972 if (this.match(_tokenizerTypes.types.string)) {
973 node.specifiers = [];
974 node.source = this.parseExprAtom();
975 } else {
976 node.specifiers = [];
977 this.parseImportSpecifiers(node);
978 this.expectContextual("from");
979 node.source = this.match(_tokenizerTypes.types.string) ? this.parseExprAtom() : this.unexpected();
980 }
981 this.semicolon();
982 return this.finishNode(node, "ImportDeclaration");
983};
984
985// Parses a comma-separated list of module imports.
986
987pp.parseImportSpecifiers = function (node) {
988 var first = true;
989 if (this.match(_tokenizerTypes.types.name)) {
990 // import defaultObj, { x, y as z } from '...'
991 var startPos = this.state.start,
992 startLoc = this.state.startLoc;
993 node.specifiers.push(this.parseImportSpecifierDefault(this.parseIdentifier(), startPos, startLoc));
994 if (!this.eat(_tokenizerTypes.types.comma)) return;
995 }
996
997 if (this.match(_tokenizerTypes.types.star)) {
998 var specifier = this.startNode();
999 this.next();
1000 this.expectContextual("as");
1001 specifier.local = this.parseIdentifier();
1002 this.checkLVal(specifier.local, true);
1003 node.specifiers.push(this.finishNode(specifier, "ImportNamespaceSpecifier"));
1004 return;
1005 }
1006
1007 this.expect(_tokenizerTypes.types.braceL);
1008 while (!this.eat(_tokenizerTypes.types.braceR)) {
1009 if (first) {
1010 first = false;
1011 } else {
1012 this.expect(_tokenizerTypes.types.comma);
1013 if (this.eat(_tokenizerTypes.types.braceR)) break;
1014 }
1015
1016 var specifier = this.startNode();
1017 specifier.imported = this.parseIdentifier(true);
1018 specifier.local = this.eatContextual("as") ? this.parseIdentifier() : specifier.imported.__clone();
1019 this.checkLVal(specifier.local, true);
1020 node.specifiers.push(this.finishNode(specifier, "ImportSpecifier"));
1021 }
1022};
1023
1024pp.parseImportSpecifierDefault = function (id, startPos, startLoc) {
1025 var node = this.startNodeAt(startPos, startLoc);
1026 node.local = id;
1027 this.checkLVal(node.local, true);
1028 return this.finishNode(node, "ImportDefaultSpecifier");
1029};
\No newline at end of file