315 lines
9.1 KiB
JavaScript
315 lines
9.1 KiB
JavaScript
|
"use strict";
|
||
|
|
||
|
var _types = require("../tokenizer/types");
|
||
|
|
||
|
var _index = require("./index");
|
||
|
|
||
|
var _index2 = _interopRequireDefault(_index);
|
||
|
|
||
|
var _identifier = require("../util/identifier");
|
||
|
|
||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||
|
|
||
|
var pp = _index2.default.prototype;
|
||
|
|
||
|
// Convert existing expression atom to assignable pattern
|
||
|
// if possible.
|
||
|
|
||
|
/* eslint indent: 0 */
|
||
|
|
||
|
pp.toAssignable = function (node, isBinding, contextDescription) {
|
||
|
if (node) {
|
||
|
switch (node.type) {
|
||
|
case "Identifier":
|
||
|
case "ObjectPattern":
|
||
|
case "ArrayPattern":
|
||
|
case "AssignmentPattern":
|
||
|
break;
|
||
|
|
||
|
case "ObjectExpression":
|
||
|
node.type = "ObjectPattern";
|
||
|
for (var _iterator = node.properties, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
|
||
|
var _ref;
|
||
|
|
||
|
if (_isArray) {
|
||
|
if (_i >= _iterator.length) break;
|
||
|
_ref = _iterator[_i++];
|
||
|
} else {
|
||
|
_i = _iterator.next();
|
||
|
if (_i.done) break;
|
||
|
_ref = _i.value;
|
||
|
}
|
||
|
|
||
|
var prop = _ref;
|
||
|
|
||
|
if (prop.type === "ObjectMethod") {
|
||
|
if (prop.kind === "get" || prop.kind === "set") {
|
||
|
this.raise(prop.key.start, "Object pattern can't contain getter or setter");
|
||
|
} else {
|
||
|
this.raise(prop.key.start, "Object pattern can't contain methods");
|
||
|
}
|
||
|
} else {
|
||
|
this.toAssignable(prop, isBinding, "object destructuring pattern");
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case "ObjectProperty":
|
||
|
this.toAssignable(node.value, isBinding, contextDescription);
|
||
|
break;
|
||
|
|
||
|
case "SpreadProperty":
|
||
|
node.type = "RestProperty";
|
||
|
break;
|
||
|
|
||
|
case "ArrayExpression":
|
||
|
node.type = "ArrayPattern";
|
||
|
this.toAssignableList(node.elements, isBinding, contextDescription);
|
||
|
break;
|
||
|
|
||
|
case "AssignmentExpression":
|
||
|
if (node.operator === "=") {
|
||
|
node.type = "AssignmentPattern";
|
||
|
delete node.operator;
|
||
|
} else {
|
||
|
this.raise(node.left.end, "Only '=' operator can be used for specifying default value.");
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case "MemberExpression":
|
||
|
if (!isBinding) break;
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
var message = "Invalid left-hand side" + (contextDescription ? " in " + contextDescription : /* istanbul ignore next */"expression");
|
||
|
this.raise(node.start, message);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return node;
|
||
|
};
|
||
|
|
||
|
// Convert list of expression atoms to binding list.
|
||
|
|
||
|
pp.toAssignableList = function (exprList, isBinding, contextDescription) {
|
||
|
var end = exprList.length;
|
||
|
if (end) {
|
||
|
var last = exprList[end - 1];
|
||
|
if (last && last.type === "RestElement") {
|
||
|
--end;
|
||
|
} else if (last && last.type === "SpreadElement") {
|
||
|
last.type = "RestElement";
|
||
|
var arg = last.argument;
|
||
|
this.toAssignable(arg, isBinding, contextDescription);
|
||
|
if (arg.type !== "Identifier" && arg.type !== "MemberExpression" && arg.type !== "ArrayPattern") {
|
||
|
this.unexpected(arg.start);
|
||
|
}
|
||
|
--end;
|
||
|
}
|
||
|
}
|
||
|
for (var i = 0; i < end; i++) {
|
||
|
var elt = exprList[i];
|
||
|
if (elt) this.toAssignable(elt, isBinding, contextDescription);
|
||
|
}
|
||
|
return exprList;
|
||
|
};
|
||
|
|
||
|
// Convert list of expression atoms to a list of
|
||
|
|
||
|
pp.toReferencedList = function (exprList) {
|
||
|
return exprList;
|
||
|
};
|
||
|
|
||
|
// Parses spread element.
|
||
|
|
||
|
pp.parseSpread = function (refShorthandDefaultPos) {
|
||
|
var node = this.startNode();
|
||
|
this.next();
|
||
|
node.argument = this.parseMaybeAssign(false, refShorthandDefaultPos);
|
||
|
return this.finishNode(node, "SpreadElement");
|
||
|
};
|
||
|
|
||
|
pp.parseRest = function () {
|
||
|
var node = this.startNode();
|
||
|
this.next();
|
||
|
node.argument = this.parseBindingIdentifier();
|
||
|
return this.finishNode(node, "RestElement");
|
||
|
};
|
||
|
|
||
|
pp.shouldAllowYieldIdentifier = function () {
|
||
|
return this.match(_types.types._yield) && !this.state.strict && !this.state.inGenerator;
|
||
|
};
|
||
|
|
||
|
pp.parseBindingIdentifier = function () {
|
||
|
return this.parseIdentifier(this.shouldAllowYieldIdentifier());
|
||
|
};
|
||
|
|
||
|
// Parses lvalue (assignable) atom.
|
||
|
|
||
|
pp.parseBindingAtom = function () {
|
||
|
switch (this.state.type) {
|
||
|
case _types.types._yield:
|
||
|
if (this.state.strict || this.state.inGenerator) this.unexpected();
|
||
|
// fall-through
|
||
|
case _types.types.name:
|
||
|
return this.parseIdentifier(true);
|
||
|
|
||
|
case _types.types.bracketL:
|
||
|
var node = this.startNode();
|
||
|
this.next();
|
||
|
node.elements = this.parseBindingList(_types.types.bracketR, true);
|
||
|
return this.finishNode(node, "ArrayPattern");
|
||
|
|
||
|
case _types.types.braceL:
|
||
|
return this.parseObj(true);
|
||
|
|
||
|
default:
|
||
|
this.unexpected();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
pp.parseBindingList = function (close, allowEmpty) {
|
||
|
var elts = [];
|
||
|
var first = true;
|
||
|
while (!this.eat(close)) {
|
||
|
if (first) {
|
||
|
first = false;
|
||
|
} else {
|
||
|
this.expect(_types.types.comma);
|
||
|
}
|
||
|
if (allowEmpty && this.match(_types.types.comma)) {
|
||
|
elts.push(null);
|
||
|
} else if (this.eat(close)) {
|
||
|
break;
|
||
|
} else if (this.match(_types.types.ellipsis)) {
|
||
|
elts.push(this.parseAssignableListItemTypes(this.parseRest()));
|
||
|
this.expect(close);
|
||
|
break;
|
||
|
} else {
|
||
|
var decorators = [];
|
||
|
while (this.match(_types.types.at)) {
|
||
|
decorators.push(this.parseDecorator());
|
||
|
}
|
||
|
var left = this.parseMaybeDefault();
|
||
|
if (decorators.length) {
|
||
|
left.decorators = decorators;
|
||
|
}
|
||
|
this.parseAssignableListItemTypes(left);
|
||
|
elts.push(this.parseMaybeDefault(left.start, left.loc.start, left));
|
||
|
}
|
||
|
}
|
||
|
return elts;
|
||
|
};
|
||
|
|
||
|
pp.parseAssignableListItemTypes = function (param) {
|
||
|
return param;
|
||
|
};
|
||
|
|
||
|
// Parses assignment pattern around given atom if possible.
|
||
|
|
||
|
pp.parseMaybeDefault = function (startPos, startLoc, left) {
|
||
|
startLoc = startLoc || this.state.startLoc;
|
||
|
startPos = startPos || this.state.start;
|
||
|
left = left || this.parseBindingAtom();
|
||
|
if (!this.eat(_types.types.eq)) return left;
|
||
|
|
||
|
var node = this.startNodeAt(startPos, startLoc);
|
||
|
node.left = left;
|
||
|
node.right = this.parseMaybeAssign();
|
||
|
return this.finishNode(node, "AssignmentPattern");
|
||
|
};
|
||
|
|
||
|
// Verify that a node is an lval — something that can be assigned
|
||
|
// to.
|
||
|
|
||
|
pp.checkLVal = function (expr, isBinding, checkClashes, contextDescription) {
|
||
|
switch (expr.type) {
|
||
|
case "Identifier":
|
||
|
if (this.state.strict && (_identifier.reservedWords.strictBind(expr.name) || _identifier.reservedWords.strict(expr.name))) {
|
||
|
this.raise(expr.start, (isBinding ? "Binding " : "Assigning to ") + expr.name + " in strict mode");
|
||
|
}
|
||
|
|
||
|
if (checkClashes) {
|
||
|
// we need to prefix this with an underscore for the cases where we have a key of
|
||
|
// `__proto__`. there's a bug in old V8 where the following wouldn't work:
|
||
|
//
|
||
|
// > var obj = Object.create(null);
|
||
|
// undefined
|
||
|
// > obj.__proto__
|
||
|
// null
|
||
|
// > obj.__proto__ = true;
|
||
|
// true
|
||
|
// > obj.__proto__
|
||
|
// null
|
||
|
var key = "_" + expr.name;
|
||
|
|
||
|
if (checkClashes[key]) {
|
||
|
this.raise(expr.start, "Argument name clash in strict mode");
|
||
|
} else {
|
||
|
checkClashes[key] = true;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case "MemberExpression":
|
||
|
if (isBinding) this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " member expression");
|
||
|
break;
|
||
|
|
||
|
case "ObjectPattern":
|
||
|
for (var _iterator2 = expr.properties, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) {
|
||
|
var _ref2;
|
||
|
|
||
|
if (_isArray2) {
|
||
|
if (_i2 >= _iterator2.length) break;
|
||
|
_ref2 = _iterator2[_i2++];
|
||
|
} else {
|
||
|
_i2 = _iterator2.next();
|
||
|
if (_i2.done) break;
|
||
|
_ref2 = _i2.value;
|
||
|
}
|
||
|
|
||
|
var prop = _ref2;
|
||
|
|
||
|
if (prop.type === "ObjectProperty") prop = prop.value;
|
||
|
this.checkLVal(prop, isBinding, checkClashes, "object destructuring pattern");
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case "ArrayPattern":
|
||
|
for (var _iterator3 = expr.elements, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) {
|
||
|
var _ref3;
|
||
|
|
||
|
if (_isArray3) {
|
||
|
if (_i3 >= _iterator3.length) break;
|
||
|
_ref3 = _iterator3[_i3++];
|
||
|
} else {
|
||
|
_i3 = _iterator3.next();
|
||
|
if (_i3.done) break;
|
||
|
_ref3 = _i3.value;
|
||
|
}
|
||
|
|
||
|
var elem = _ref3;
|
||
|
|
||
|
if (elem) this.checkLVal(elem, isBinding, checkClashes, "array destructuring pattern");
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case "AssignmentPattern":
|
||
|
this.checkLVal(expr.left, isBinding, checkClashes, "assignment pattern");
|
||
|
break;
|
||
|
|
||
|
case "RestProperty":
|
||
|
this.checkLVal(expr.argument, isBinding, checkClashes, "rest property");
|
||
|
break;
|
||
|
|
||
|
case "RestElement":
|
||
|
this.checkLVal(expr.argument, isBinding, checkClashes, "rest element");
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
var message = (isBinding ? /* istanbul ignore next */"Binding invalid" : "Invalid") + " left-hand side" + (contextDescription ? " in " + contextDescription : /* istanbul ignore next */"expression");
|
||
|
this.raise(expr.start, message);
|
||
|
}
|
||
|
}
|
||
|
};
|