diff --git a/src/acorn/src/expression.js b/src/acorn/src/expression.js index cc5667f815..dccf9962a8 100755 --- a/src/acorn/src/expression.js +++ b/src/acorn/src/expression.js @@ -29,14 +29,23 @@ const pp = Parser.prototype // strict mode, init properties are also not allowed to be repeated. pp.checkPropClash = function(prop, propHash) { - if (this.options.ecmaVersion >= 6) return + if (this.options.ecmaVersion >= 6 && (prop.computed || prop.method || prop.shorthand)) + return let key = prop.key, name switch (key.type) { case "Identifier": name = key.name; break case "Literal": name = String(key.value); break default: return } - let kind = prop.kind || "init", other + let kind = prop.kind + if (this.options.ecmaVersion >= 6) { + if (name === "__proto__" && kind === "init") { + if (propHash.proto) this.raise(key.start, "Redefinition of __proto__ property"); + propHash.proto = true + } + return + } + let other if (has(propHash, name)) { other = propHash[name] let isGetSet = kind !== "init" @@ -260,8 +269,10 @@ pp.parseNoCallExpr = function() { pp.parseExprAtom = function(refShorthandDefaultPos) { let node, canBeArrow = this.potentialArrowAt == this.start switch (this.type) { - case tt._this: case tt._super: + if (!this.inFunction) + this.raise(this.start, "'super' outside of function or class") + case tt._this: let type = this.type === tt._this ? "ThisExpression" : "Super" node = this.startNode() this.next() @@ -609,6 +620,14 @@ pp.parseObjPropValue = function (prop, start, isGenerator, isAsync, isPattern, r prop.kind = prop.key.name this.parsePropertyName(prop) prop.value = this.parseMethod(false) + let paramCount = prop.kind === "get" ? 0 : 1 + if (prop.value.params.length !== paramCount) { + let start = prop.value.start + if (prop.kind === "get") + this.raise(start, "getter should have no params"); + else + this.raise(start, "setter should have exactly one param") + } } else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") { prop.kind = "init" if (isPattern) { @@ -634,12 +653,12 @@ pp.parsePropertyName = function(prop) { prop.computed = true prop.key = this.parseMaybeAssign() this.expect(tt.bracketR) - return + return prop.key } else { prop.computed = false } } - prop.key = (this.type === tt.num || this.type === tt.string) ? this.parseExprAtom() : this.parseIdent(true) + return prop.key = (this.type === tt.num || this.type === tt.string) ? this.parseExprAtom() : this.parseIdent(true) } // Initialize empty function node. diff --git a/src/acorn/src/lval.js b/src/acorn/src/lval.js index 1cf1371a85..a7718736c6 100755 --- a/src/acorn/src/lval.js +++ b/src/acorn/src/lval.js @@ -35,6 +35,7 @@ pp.toAssignable = function(node, isBinding) { 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.") } @@ -171,7 +172,7 @@ pp.checkLVal = function(expr, isBinding, checkClashes) { break case "ObjectPattern": - for (let i = 0; i < expr.properties.length; i++) { + for (let i = 0; i < expr.properties.length; i++) { var prop = expr.properties[i]; if (prop.type === "Property") prop = prop.value; this.checkLVal(prop, isBinding, checkClashes) @@ -194,6 +195,10 @@ pp.checkLVal = function(expr, isBinding, checkClashes) { this.checkLVal(expr.argument, isBinding, checkClashes) break + case "ParenthesizedExpression": + this.checkLVal(expr.expression, isBinding, checkClashes) + break + default: this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " rvalue") } diff --git a/src/acorn/src/state.js b/src/acorn/src/state.js index bebb6fdf31..c4f6d9292b 100755 --- a/src/acorn/src/state.js +++ b/src/acorn/src/state.js @@ -1,5 +1,6 @@ import {reservedWords, keywords} from "./identifier" -import {types as tt, lineBreak} from "./tokentype" +import {types as tt} from "./tokentype" +import {lineBreak} from "./whitespace" export function Parser(options, input, startPos) { this.options = options diff --git a/src/acorn/src/statement.js b/src/acorn/src/statement.js index 140f349a16..30940130fa 100755 --- a/src/acorn/src/statement.js +++ b/src/acorn/src/statement.js @@ -347,7 +347,14 @@ pp.parseLabeledStatement = function(node, maybeName, expr) { for (let i = 0; i < this.labels.length; ++i) if (this.labels[i].name === maybeName) this.raise(expr.start, "Label '" + maybeName + "' is already declared") let kind = this.type.isLoop ? "loop" : this.type === tt._switch ? "switch" : null - this.labels.push({name: maybeName, kind: kind}) + for (let i = this.labels.length - 1; i >= 0; i--) { + let label = this.labels[i] + if (label.statementStart == node.start) { + label.statementStart = this.start; + label.kind = kind; + } else break; + } + this.labels.push({name: maybeName, kind: kind, statementStart: this.start}) node.body = this.parseStatement(true) this.labels.pop() node.label = expr @@ -466,6 +473,7 @@ pp.parseClass = function(node, isStatement) { this.parseClassId(node, isStatement) this.parseClassSuper(node) var classBody = this.startNode() + let hadConstructor = false classBody.body = [] this.expect(tt.braceL) let decorators = [] @@ -480,16 +488,14 @@ pp.parseClass = function(node, isStatement) { method.decorators = decorators decorators = [] } + let isMaybeStatic = this.type === tt.name && this.value === "static" var isGenerator = this.eat(tt.star), isAsync = false this.parsePropertyName(method) - if (this.type !== tt.parenL && !method.computed && method.key.type === "Identifier" && - method.key.name === "static") { + method.static = isMaybeStatic && this.type !== tt.parenL + if (method.static) { if (isGenerator) this.unexpected() - method['static'] = true isGenerator = this.eat(tt.star) this.parsePropertyName(method) - } else { - method['static'] = false } if (!isGenerator && method.key.type === "Identifier" && !method.computed && this.isClassProperty()) { classBody.body.push(this.parseClassProperty(method)) @@ -500,23 +506,39 @@ pp.parseClass = function(node, isStatement) { isAsync = true this.parsePropertyName(method) } + let isGetSet = false method.kind = "method" - if (!method.computed && !isGenerator && !isAsync) { - if (method.key.type === "Identifier") { - if (this.type !== tt.parenL && (method.key.name === "get" || method.key.name === "set")) { - method.kind = method.key.name - this.parsePropertyName(method) - } else if (!method['static'] && method.key.name === "constructor") { - method.kind = "constructor" - } - } else if (!method['static'] && method.key.type === "Literal" && method.key.value === "constructor") { + if (!method.computed) { + let {key} = method + if (!isAsync && !isGenerator && key.type === "Identifier" && this.type !== tt.parenL && (key.name === "get" || key.name === "set")) { + isGetSet = true + method.kind = key.name + key = this.parsePropertyName(method) + } + if (!method.static && (key.type === "Identifier" && key.name === "constructor" || + key.type === "Literal" && key.value === "constructor")) { + if (hadConstructor) this.raise(key.start, "Duplicate constructor in the same class") + if (isGetSet) this.raise(key.start, "Constructor can't have get/set modifier") + if (isGenerator) this.raise(key.start, "Constructor can't be a generator") + if (isAsync) this.raise(key.start, "Constructor can't be an async function") method.kind = "constructor" + hadConstructor = true } } if (method.kind === "constructor" && method.decorators) { this.raise(method.start, "You can't attach decorators to a class constructor") } this.parseClassMethod(classBody, method, isGenerator, isAsync) + if (isGetSet) { + let paramCount = method.kind === "get" ? 0 : 1 + if (method.value.params.length !== paramCount) { + let start = method.value.start + if (method.kind === "get") + this.raise(start, "getter should have no params"); + else + this.raise(start, "setter should have exactly one param") + } + } } if (decorators.length) { this.raise(this.start, "You have trailing decorators with no method"); diff --git a/src/acorn/src/tokenize.js b/src/acorn/src/tokenize.js index 8d34c43a24..d9c22d5d04 100755 --- a/src/acorn/src/tokenize.js +++ b/src/acorn/src/tokenize.js @@ -25,6 +25,9 @@ export class Token { const pp = Parser.prototype +// Are we running under Rhino? +const isRhino = typeof Packages == "object" && Object.prototype.toString.call(Packages) == "[object JavaPackage]" + // Move to the next token pp.next = function() { @@ -430,23 +433,30 @@ pp.readRegexp = function() { // negatives in unlikely scenarios. For example, `[\u{61}-b]` is a // perfectly valid pattern that is equivalent to `[a-b]`, but it would // be replaced by `[x-b]` which throws an error. - tmp = tmp.replace(/\\u([a-fA-F0-9]{4})|\\u\{([0-9a-fA-F]+)\}|[\uD800-\uDBFF][\uDC00-\uDFFF]/g, "x") + tmp = tmp.replace(/\\u\{([0-9a-fA-F]+)\}/g, (match, code, offset) => { + code = Number("0x" + code) + if (code > 0x10FFFF) this.raise(start + offset + 3, "Code point out of bounds") + return "x" + }); + tmp = tmp.replace(/\\u([a-fA-F0-9]{4})|[\uD800-\uDBFF][\uDC00-\uDFFF]/g, "x") } } // Detect invalid regular expressions. - try { - new RegExp(tmp) - } catch (e) { - if (e instanceof SyntaxError) this.raise(start, "Error parsing regular expression: " + e.message) - this.raise(e) - } - // Get a regular expression object for this pattern-flag pair, or `null` in - // case the current environment doesn't support the flags it uses. - let value - try { - value = new RegExp(content, mods) - } catch (err) { - value = null + let value = null + // Rhino's regular expression parser is flaky and throws uncatchable exceptions, + // so don't do detection if we are running under Rhino + if (!isRhino) { + try { + new RegExp(tmp) + } catch (e) { + if (e instanceof SyntaxError) this.raise(start, "Error parsing regular expression: " + e.message) + this.raise(e) + } + // Get a regular expression object for this pattern-flag pair, or `null` in + // case the current environment doesn't support the flags it uses. + try { + value = new RegExp(content, mods) + } catch (err) {} } return this.finishToken(tt.regexp, {pattern: content, flags: mods, value: value}) } @@ -514,10 +524,10 @@ pp.readCodePoint = function() { if (ch === 123) { if (this.options.ecmaVersion < 6) this.unexpected() - ++this.pos + let codePos = ++this.pos code = this.readHexChar(this.input.indexOf('}', this.pos) - this.pos) ++this.pos - if (code > 0x10FFFF) this.unexpected() + if (code > 0x10FFFF) this.raise(codePos, "Code point out of bounds") } else { code = this.readHexChar(4) } @@ -539,7 +549,7 @@ pp.readString = function(quote) { if (ch === quote) break if (ch === 92) { // '\' out += this.input.slice(chunkStart, this.pos) - out += this.readEscapedChar() + out += this.readEscapedChar(false) chunkStart = this.pos } else { if (isNewLine(ch)) this.raise(this.start, "Unterminated string constant") @@ -572,7 +582,7 @@ pp.readTmplToken = function() { } if (ch === 92) { // '\' out += this.input.slice(chunkStart, this.pos) - out += this.readEscapedChar() + out += this.readEscapedChar(true) chunkStart = this.pos } else if (isNewLine(ch)) { out += this.input.slice(chunkStart, this.pos) @@ -600,42 +610,46 @@ pp.readTmplToken = function() { // Used to read escaped characters -pp.readEscapedChar = function() { +pp.readEscapedChar = function(inTemplate) { let ch = this.input.charCodeAt(++this.pos) - let octal = /^[0-7]+/.exec(this.input.slice(this.pos, this.pos + 3)) - if (octal) octal = octal[0] - while (octal && parseInt(octal, 8) > 255) octal = octal.slice(0, -1) - if (octal === "0") octal = null ++this.pos - if (octal) { - if (this.strict) this.raise(this.pos - 2, "Octal literal in strict mode") - this.pos += octal.length - 1 - return String.fromCharCode(parseInt(octal, 8)) - } else { - switch (ch) { - case 110: return "\n"; // 'n' -> '\n' - case 114: return "\r"; // 'r' -> '\r' - case 120: return String.fromCharCode(this.readHexChar(2)); // 'x' - case 117: return codePointToString(this.readCodePoint()); // 'u' - case 116: return "\t"; // 't' -> '\t' - case 98: return "\b"; // 'b' -> '\b' - case 118: return "\u000b"; // 'v' -> '\u000b' - case 102: return "\f"; // 'f' -> '\f' - case 48: return "\0"; // 0 -> '\0' - case 13: if (this.input.charCodeAt(this.pos) === 10) ++this.pos; // '\r\n' - case 10: // ' \n' - if (this.options.locations) { this.lineStart = this.pos; ++this.curLine } - return "" - default: return String.fromCharCode(ch) + switch (ch) { + case 110: return "\n"; // 'n' -> '\n' + case 114: return "\r"; // 'r' -> '\r' + case 120: return String.fromCharCode(this.readHexChar(2)); // 'x' + case 117: return codePointToString(this.readCodePoint()); // 'u' + case 116: return "\t"; // 't' -> '\t' + case 98: return "\b"; // 'b' -> '\b' + case 118: return "\u000b"; // 'v' -> '\u000b' + case 102: return "\f"; // 'f' -> '\f' + case 13: if (this.input.charCodeAt(this.pos) === 10) ++this.pos; // '\r\n' + case 10: // ' \n' + if (this.options.locations) { this.lineStart = this.pos; ++this.curLine } + return "" + default: + if (ch >= 48 && ch <= 55) { + let octalStr = this.input.substr(this.pos - 1, 3).match(/^[0-7]+/)[0] + let octal = parseInt(octalStr, 8) + if (octal > 255) { + octalStr = octalStr.slice(0, -1) + octal = parseInt(octalStr, 8) + } + if (octal > 0 && (this.strict || inTemplate)) { + this.raise(this.pos - 2, "Octal literal in strict mode") + } + this.pos += octalStr.length - 1 + return String.fromCharCode(octal) } + return String.fromCharCode(ch) } } // Used to read character escape sequences ('\x', '\u', '\U'). pp.readHexChar = function(len) { + let codePos = this.pos let n = this.readInt(16, len) - if (n === null) this.raise(this.start, "Bad character escape sequence") + if (n === null) this.raise(codePos, "Bad character escape sequence") return n } diff --git a/src/babel/messages.js b/src/babel/messages.js index 45b0ba25e8..f06f248083 100644 --- a/src/babel/messages.js +++ b/src/babel/messages.js @@ -5,9 +5,7 @@ export const MESSAGES = { JSXNamespacedTags: "Namespace tags are not supported. ReactJSX is not XML.", classesIllegalBareSuper: "Illegal use of bare super", classesIllegalSuperCall: "Direct super call is illegal in non-constructor, use super.$1() instead", - classesIllegalConstructorKind: "Illegal kind for constructor method", scopeDuplicateDeclaration: "Duplicate declaration $1", - settersInvalidParamLength: "Setters must have exactly one parameter", settersNoRest: "Setters aren't allowed to have a rest", noAssignmentsInForHead: "No assignments allowed in for-in/of head", expectedMemberExpressionOrIdentifier: "Expected type MemberExpression or Identifier", diff --git a/src/babel/transformation/transformers/internal/validation.js b/src/babel/transformation/transformers/internal/validation.js index e09092209a..7e858c0c5e 100644 --- a/src/babel/transformation/transformers/internal/validation.js +++ b/src/babel/transformation/transformers/internal/validation.js @@ -14,28 +14,8 @@ export var visitor = { } }, - MethodDefinition(node) { - if (node.kind !== "constructor") { - // get constructor() {} - var isConstructor = !node.computed && t.isIdentifier(node.key, { name: "constructor" }); - - // get ["constructor"]() {} - isConstructor = isConstructor || t.isLiteral(node.key, { value: "constructor" }); - - if (isConstructor) { - throw this.errorWithNode(messages.get("classesIllegalConstructorKind")); - } - } - - visitor.Property.apply(this, arguments); - }, - Property(node, parent, scope, file) { if (node.kind === "set") { - if (node.value.params.length !== 1) { - throw file.errorWithNode(node.value, messages.get("settersInvalidParamLength")); - } - var first = node.value.params[0]; if (t.isRestElement(first)) { throw file.errorWithNode(first, messages.get("settersNoRest")); diff --git a/test/acorn/tests-babel.js b/test/acorn/tests-babel.js index 089be147d9..03aeae4ec7 100644 --- a/test/acorn/tests-babel.js +++ b/test/acorn/tests-babel.js @@ -2299,7 +2299,7 @@ test("class Foo { @foo bar() {} }", { features: { "es7.decorators": true } }); -test("class Foo { @foo set bar() {} }", { +test("class Foo { @foo set bar(f) {} }", { "start": 0, "body": [ { @@ -2343,29 +2343,32 @@ test("class Foo { @foo set bar() {} }", { "id": null, "generator": false, "expression": false, - "params": [], + "params": [{ + "type": "Identifier", + "name": "f" + }], "body": { - "start": 27, + "start": 28, "body": [], "type": "BlockStatement", - "end": 29 + "end": 30 }, "type": "FunctionExpression", - "end": 29 + "end": 30 }, "type": "MethodDefinition", - "end": 29 + "end": 30 } ], "type": "ClassBody", - "end": 31 + "end": 32 }, "type": "ClassDeclaration", - "end": 31 + "end": 32 } ], "type": "Program", - "end": 31 + "end": 32 }, { ecmaVersion: 6, features: { "es7.decorators": true } diff --git a/test/acorn/tests-harmony.js b/test/acorn/tests-harmony.js index d253ac2099..8d93337f4d 100755 --- a/test/acorn/tests-harmony.js +++ b/test/acorn/tests-harmony.js @@ -1,4 +1,5 @@ /* + Copyright (C) 2015 Ingvar Stepanyan Copyright (C) 2012 Ariya Hidayat Copyright (C) 2012 Joost-Wim Boekesteijn Copyright (C) 2012 Yusuke Suzuki @@ -6,7 +7,6 @@ Copyright (C) 2011 Ariya Hidayat Copyright (C) 2011 Yusuke Suzuki Copyright (C) 2011 Arpad Borsos - Copyright (C) 2014 Ingvar Stepanyan Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -877,7 +877,7 @@ test("`\n\r\n\r`", { locations: true }); -test("`\\u{000042}\\u0042\\x42u0\\102\\A`", { +test("`\\u{000042}\\u0042\\x42u0\\A`", { type: "Program", body: [{ type: "ExpressionStatement", @@ -885,27 +885,27 @@ test("`\\u{000042}\\u0042\\x42u0\\102\\A`", { type: "TemplateLiteral", quasis: [{ type: "TemplateElement", - value: {raw: "\\u{000042}\\u0042\\x42u0\\102\\A", cooked: "BBBu0BA"}, + value: {raw: "\\u{000042}\\u0042\\x42u0\\A", cooked: "BBBu0A"}, tail: true, loc: { start: {line: 1, column: 1}, - end: {line: 1, column: 29} + end: {line: 1, column: 25} } }], expressions: [], loc: { start: {line: 1, column: 0}, - end: {line: 1, column: 30} + end: {line: 1, column: 26} } }, loc: { start: {line: 1, column: 0}, - end: {line: 1, column: 30} + end: {line: 1, column: 26} } }], loc: { start: {line: 1, column: 0}, - end: {line: 1, column: 30} + end: {line: 1, column: 26} } }, { ecmaVersion: 6, @@ -1714,7 +1714,7 @@ test("([a, , b]) => 42", { testFail("([a.a]) => 42", "Assigning to rvalue (1:2)", {ecmaVersion: 6}); -testFail("console.log(typeof () => {});", "Unexpected token (1:20)", {ecmaVersion: 6}); +testFail("console.log(typeof () => {});", "Unexpected token (1:20)", {ecmaVersion: 6}) test("(x=1) => x * x", { type: "Program", @@ -7223,6 +7223,12 @@ test("class A {'constructor'() {}}", { }] }, {ecmaVersion: 6}); +testFail("class A { constructor() {} 'constructor'() }", "Duplicate constructor in the same class (1:27)", {ecmaVersion: 6}); + +testFail("class A { get constructor() {} }", "Constructor can't have get/set modifier (1:14)", {ecmaVersion: 6}); + +testFail("class A { *constructor() {} }", "Constructor can't be a generator (1:11)", {ecmaVersion: 6}); + test("class A {static foo() {}}", { type: "Program", body: [{ @@ -13641,21 +13647,21 @@ testFail("0B18", "Unexpected token (1:3)", {ecmaVersion: 6}); testFail("0B12", "Unexpected token (1:3)", {ecmaVersion: 6}); -testFail("\"\\u{110000}\"", "Unexpected token (1:0)", {ecmaVersion: 6}); +testFail("\"\\u{110000}\"", "Code point out of bounds (1:4)", {ecmaVersion: 6}); -testFail("\"\\u{}\"", "Bad character escape sequence (1:0)", {ecmaVersion: 6}); +testFail("\"\\u{}\"", "Bad character escape sequence (1:4)", {ecmaVersion: 6}); -testFail("\"\\u{FFFF\"", "Bad character escape sequence (1:0)", {ecmaVersion: 6}); +testFail("\"\\u{FFFF\"", "Bad character escape sequence (1:4)", {ecmaVersion: 6}); -testFail("\"\\u{FFZ}\"", "Bad character escape sequence (1:0)", {ecmaVersion: 6}); +testFail("\"\\u{FFZ}\"", "Bad character escape sequence (1:4)", {ecmaVersion: 6}); testFail("[v] += ary", "Assigning to rvalue (1:0)", {ecmaVersion: 6}); testFail("[2] = 42", "Assigning to rvalue (1:1)", {ecmaVersion: 6}); -testFail("({ obj:20 } = 42)", "Assigning to rvalue (1:7)", {ecmaVersion: 6}); +testFail("({ obj:20 }) = 42", "Assigning to rvalue (1:7)", {ecmaVersion: 6}); -testFail("( { get x() {} } = 0 )", "Object pattern can't contain getter or setter (1:8)", {ecmaVersion: 6}); +testFail("( { get x() {} } ) = 0", "Object pattern can't contain getter or setter (1:8)", {ecmaVersion: 6}); testFail("x \n is y", "Unexpected token (2:4)", {ecmaVersion: 6}); @@ -13675,9 +13681,9 @@ testFail("let default", "Unexpected token (1:4)", {ecmaVersion: 6}); testFail("const default", "Unexpected token (1:6)", {ecmaVersion: 6}); -testFail("\"use strict\"; ({ v: eval } = obj)", "Assigning to eval in strict mode (1:20)", {ecmaVersion: 6}); +testFail("\"use strict\"; ({ v: eval }) = obj", "Assigning to eval in strict mode (1:20)", {ecmaVersion: 6}); -testFail("\"use strict\"; ({ v: arguments } = obj)", "Assigning to arguments in strict mode (1:20)", {ecmaVersion: 6}); +testFail("\"use strict\"; ({ v: arguments }) = obj", "Assigning to arguments in strict mode (1:20)", {ecmaVersion: 6}); testFail("for (let x = 42 in list) process(x);", "Unexpected token (1:16)", {ecmaVersion: 6}); @@ -14288,7 +14294,6 @@ test("var {propName: localVar = defaultValue} = obj", { value: { type: "AssignmentPattern", range: [15, 38], - operator: "=", left: { type: "Identifier", range: [15, 23], @@ -14344,7 +14349,6 @@ test("var {propName = defaultValue} = obj", { value: { type: "AssignmentPattern", range: [5, 28], - operator: "=", left: { type: "Identifier", range: [5, 13], @@ -14387,7 +14391,6 @@ test("var [localVar = defaultValue] = obj", { elements: [{ type: "AssignmentPattern", range: [5, 28], - operator: "=", left: { type: "Identifier", range: [5, 13], @@ -14442,7 +14445,6 @@ test("({x = 0} = obj)", { value: { type: "AssignmentPattern", range: [2, 7], - operator: "=", left: { type: "Identifier", range: [2, 3], @@ -14498,7 +14500,6 @@ test("({x = 0}) => x", { value: { type: "AssignmentPattern", range: [2, 7], - operator: "=", left: { type: "Identifier", range: [2, 3], @@ -14575,7 +14576,6 @@ test("[a, {b: {c = 1}}] = arr", { value: { type: "AssignmentPattern", range: [9, 14], - operator: "=", left: { type: "Identifier", range: [9, 10], @@ -14630,7 +14630,6 @@ test("for ({x = 0} in arr);", { value: { type: "AssignmentPattern", range: [6, 11], - operator: "=", left: { type: "Identifier", range: [6, 7], @@ -15262,3 +15261,70 @@ test("new.target", { }, {ecmaVersion: 6}); testFail("new.prop", "The only valid meta property for new is new.target (1:4)", {ecmaVersion: 6}); + +test("export default function foo() {} false", { + body: [ + { + declaration: { + id: { + name: "foo", + type: "Identifier" + }, + generator: false, + expression: false, + params: [], + body: { + body: [], + type: "BlockStatement" + }, + type: "FunctionDeclaration" + }, + type: "ExportDefaultDeclaration" + }, + { + expression: { + value: false, + raw: "false", + type: "Literal" + }, + type: "ExpressionStatement" + } + ], + sourceType: "module", + type: "Program" +}, {ecmaVersion: 6, sourceType: "module"}) + +// https://github.com/marijnh/acorn/issues/274 + +testFail("`\\07`", "Octal literal in strict mode (1:1)", {ecmaVersion: 6}); + +// https://github.com/marijnh/acorn/issues/277 + +testFail("x = { method() 42 }", "Unexpected token (1:15)", {ecmaVersion: 6}); + +testFail("x = { get method() 42 }", "Unexpected token (1:19)", {ecmaVersion: 6}); + +testFail("x = { set method(val) v = val }", "Unexpected token (1:22)", {ecmaVersion: 6}); + +// https://github.com/marijnh/acorn/issues/278 + +testFail("/\\u{110000}/u", "Code point out of bounds (1:4)", {ecmaVersion: 6}); + +// https://github.com/marijnh/acorn/issues/279 + +testFail("super", "'super' outside of function or class (1:0)", {ecmaVersion: 6}); + +// https://github.com/marijnh/acorn/issues/275 + +testFail("class A { get prop(x) {} }", "getter should have no params (1:18)", {ecmaVersion: 6}); +testFail("class A { set prop() {} }", "setter should have exactly one param (1:18)", {ecmaVersion: 6}); +testFail("class A { set prop(x, y) {} }", "setter should have exactly one param (1:18)", {ecmaVersion: 6}); + +// https://github.com/marijnh/acorn/issues/276 + +testFail("({ __proto__: 1, __proto__: 2 })", "Redefinition of __proto__ property (1:17)", {ecmaVersion: 6}); +testFail("({ '__proto__': 1, __proto__: 2 })", "Redefinition of __proto__ property (1:19)", {ecmaVersion: 6}); +test("({ ['__proto__']: 1, __proto__: 2 })", {}, {ecmaVersion: 6}); +test("({ __proto__() { return 1 }, __proto__: 2 })", {}, {ecmaVersion: 6}); +test("({ get __proto__() { return 1 }, __proto__: 2 })", {}, {ecmaVersion: 6}); +test("({ __proto__, __proto__: 2 })", {}, {ecmaVersion: 6}); diff --git a/test/acorn/tests.js b/test/acorn/tests.js index c9c2749bb4..6f15af97fe 100755 --- a/test/acorn/tests.js +++ b/test/acorn/tests.js @@ -403,7 +403,30 @@ test("(1 + 2 ) * 3", { preserveParens: true }); -testFail("(x) = 23", "Assigning to rvalue (1:0)", { preserveParens: true }); +test("(x) = 23", { + body: [ + { + expression: { + operator: "=", + left: { + expression: { + name: "x", + type: "Identifier", + }, + type: "ParenthesizedExpression", + }, + right: { + value: 23, + raw: "23", + type: "Literal", + }, + type: "AssignmentExpression", + }, + type: "ExpressionStatement", + } + ], + type: "Program", +}, {preserveParens: true}); test("x = []", { type: "Program", @@ -20637,6 +20660,9 @@ test("done: while (true) { break done; }", { } }); +test("target1: target2: while (true) { continue target1; }", {}); +test("target1: target2: target3: while (true) { continue target1; }", {}); + test("(function(){ return })", { type: "Program", body: [ @@ -26881,7 +26907,7 @@ testFail("/test", "Unterminated regular expression (1:1)"); testFail("var x = /[a-z]/\\ux", - "Bad character escape sequence (1:8)"); + "Bad character escape sequence (1:17)"); testFail("3 = 4", "Assigning to rvalue (1:0)"); @@ -27168,7 +27194,7 @@ testFail("\"\\", "Unterminated string constant (1:0)"); testFail("\"\\u", - "Bad character escape sequence (1:0)"); + "Bad character escape sequence (1:3)"); testFail("return", "'return' outside of function (1:0)"); @@ -28720,6 +28746,39 @@ testFail("for(x of a);", "Unexpected token (1:6)"); testFail("for(var x of a);", "Unexpected token (1:10)"); +// Assertion Tests +test(function TestComments() { + // Bear class + function Bear(x,y,z) { + this.position = [x||0,y||0,z||0] + } + + Bear.prototype.roar = function(message) { + return 'RAWWW: ' + message; // Whatever + }; + + function Cat() { + /* 1 + 2 + 3*/ + } + + Cat.prototype.roar = function(message) { + return 'MEOOWW: ' + /*stuff*/ message; + }; +}.toString().replace(/\r\n/g, '\n'), {}, { + onComment: [ + {type: "Line", value: " Bear class"}, + {type: "Line", value: " Whatever"}, + {type: "Block", value: [ + " 1", + " 2", + " 3" + ].join('\n')}, + {type: "Block", value: "stuff"} + ] +}); + test("