function assertClassExpr(str, methods, heritage = null, name = null) {
let template = classExpr(name, heritage, methods);
assertExpr("(" + str + ")", template);
}
// FunctionExpression of constructor has class name as its id. // FIXME: Implement ES6 function "name" property semantics (bug 883377).
let ctorPlaceholder = {}; function assertClass(str, methods, heritage = null, constructorBody = []) {
let namelessStr = str.replace("NAME", "");
let namedStr = str.replace("NAME", "Foo");
let namedCtor = ctorWithName("Foo", constructorBody);
let namelessCtor = ctorWithName(null, constructorBody);
let namelessMethods = methods.map(x => x == ctorPlaceholder ? namelessCtor : x);
let namedMethods = methods.map(x => x == ctorPlaceholder ? namedCtor : x);
assertClassExpr(namelessStr, namelessMethods, heritage);
assertClassExpr(namedStr, namedMethods, heritage, ident("Foo"));
/* Trivial classes */ // Unnamed class statements are forbidden, but unnamed class expressions are // just fine.
assertError("class { constructor() { } }", SyntaxError);
assertClass("class NAME { constructor() { } }", [ctorPlaceholder]);
// A class name must actually be a name
assertNamedClassError("class x.y { constructor() {} }", SyntaxError);
assertNamedClassError("class [] { constructor() {} }", SyntaxError);
assertNamedClassError("class {x} { constructor() {} }", SyntaxError);
assertNamedClassError("class for { constructor() {} }", SyntaxError);
// Allow methods and accessors
assertClass("class NAME { constructor() { } method() { } }",
[ctorPlaceholder, simpleMethod("method", "method", false)]);
assertClass("class NAME { constructor() { } get method() { } }",
[ctorPlaceholder, simpleMethod("method", "get", false)]);
assertClass("class NAME { constructor() { } set method(x) { } }",
[ctorPlaceholder, simpleMethod("method", "set", false, ["x"])]);
// It's not an error to have a method named static, static, or not.
assertClass("class NAME { constructor() { } static() { } }",
[ctorPlaceholder, simpleMethod("static", "method", false)]);
assertClass("class NAME { static static() { }; constructor() { } }",
[simpleMethod("static", "method", false, [], true), ctorPlaceholder]);
assertClass("class NAME { static get static() { }; constructor() { } }",
[simpleMethod("static", "get", false, [], true), ctorPlaceholder]);
assertClass("class NAME { constructor() { }; static set static(x) { } }",
[ctorPlaceholder, simpleMethod("static", "set", false, ["x"], true)]);
// You do, however, have to put static in the right spot
assertClassError("class NAME { constructor() { }; get static foo() { } }", SyntaxError);
// Spec disallows "prototype" as a static member in a class, since that // one's important to make the desugaring work
assertClassError("class NAME { constructor() { } static prototype() { } }", SyntaxError);
assertClassError("class NAME { constructor() { } static *prototype() { } }", SyntaxError);
assertClassError("class NAME { static get prototype() { }; constructor() { } }", SyntaxError);
assertClassError("class NAME { static set prototype(x) { }; constructor() { } }", SyntaxError);
// You are, however, allowed to have a CPN called prototype as a static
assertClass("class NAME { constructor() { }; static [\"prototype\"]() { } }",
[ctorPlaceholder, emptyCPNMethod("prototype", true)]);
/* Constructor */ // Allow default constructors
assertClass("class NAME { }", []);
assertClass("class NAME extends null { }", [], lit(null));
// Derived class constructor must have curly brackets
assertClassError("class NAME extends null { constructor() 1 }", SyntaxError);
// It is an error to have two methods named constructor, but not other // names, regardless if one is an accessor or a generator or static.
assertClassError("class NAME { constructor() { } constructor(a) { } }", SyntaxError);
let methods = [["method() { }", simpleMethod("method", "method", false)],
["*method() { }", simpleMethod("method", "method", true)],
["get method() { }", simpleMethod("method", "get", false)],
["set method(x) { }", simpleMethod("method", "set", false, ["x"])],
["static method() { }", simpleMethod("method", "method", false, [], true)],
["static *method() { }", simpleMethod("method", "method", true, [], true)],
["static get method() { }", simpleMethod("method", "get", false, [], true)],
["static set method(x) { }", simpleMethod("method", "set", false, ["x"], true)]];
let i, j; for (i = 0; i < methods.length; i++) { for (j = 0; j < methods.length; j++) {
let str = "class NAME { constructor() { } " +
methods[i][0] + " " + methods[j][0] + " }";
assertClass(str, [ctorPlaceholder, methods[i][1], methods[j][1]]);
}
}
// It is, however, not an error to have a constructor, and a method with a // computed property name 'constructor'
assertClass("class NAME { constructor () { } [\"constructor\"] () { } }",
[ctorPlaceholder, emptyCPNMethod("constructor", false)]);
// It is an error to have a generator or accessor named constructor
assertClassError("class NAME { *constructor() { } }", SyntaxError);
assertClassError("class NAME { get constructor() { } }", SyntaxError);
assertClassError("class NAME { set constructor() { } }", SyntaxError);
/* Semicolons */ // Allow Semicolons in Class Definitions
assertClass("class NAME { constructor() { }; }", [ctorPlaceholder]);
// Allow more than one semicolon, even in otherwise trivial classses
assertClass("class NAME { ;;; constructor() { } }", [ctorPlaceholder]);
// Semicolons are optional, even if the methods share a line
assertClass("class NAME { method() { } constructor() { } }",
[simpleMethod("method", "method", false), ctorPlaceholder]);
/* Generators */ // No yield as a class name inside a generator
assertError(`function *foo() { class yield {
constructor() { }
}
}`, SyntaxError);
assertError(`function *foo() {
(class yield {
constructor() { }
})
}`, SyntaxError);
// No legacy generators for methods.
assertClassError(`class NAME {
constructor() { yield 2; }
}`, SyntaxError);
assertClassError(`class NAME {
method() { yield 2; }
}`, SyntaxError);
assertClassError(`class NAME {
get method() { yield 2; }
}`, SyntaxError);
assertClassError(`class NAME {
set method() { yield 2; }
}`, SyntaxError);
// Methods may be generators, but not accessors
assertClassError("class NAME { constructor() { } *get foo() { } }", SyntaxError);
assertClassError("class NAME { constructor() { } *set foo() { } }", SyntaxError);
/* Strictness */ // yield is a strict-mode keyword, and class definitions are always strict.
assertClassError("class NAME { constructor() { var yield; } }", SyntaxError);
// Beware of the strictness of computed property names. Here use bareword // deletion (a deprecated action) to check.
assertClassError("class NAME { constructor() { } [delete bar]() { }}", SyntaxError);
/* Bindings */ // Class statements bind lexically, so they should collide with other // in-block lexical bindings, but class expressions don't.
let FooCtor = ctorWithName("Foo");
assertError("{ let Foo; class Foo { constructor() { } } }", SyntaxError);
assertStmt("{ let Foo; (class Foo { constructor() { } }) }",
blockStmt([letDecl([{ id: ident("Foo"), init: null }]),
exprStmt(classExpr(ident("Foo"), null, [FooCtor]))]));
assertError("{ const Foo = 0; class Foo { constructor() { } } }", SyntaxError);
assertStmt("{ const Foo = 0; (class Foo { constructor() { } }) }",
blockStmt([constDecl([{ id: ident("Foo"), init: lit(0) }]),
exprStmt(classExpr(ident("Foo"), null, [FooCtor]))]));
assertError("{ class Foo { constructor() { } } class Foo { constructor() { } } }", SyntaxError);
assertStmt(`{
(class Foo {
constructor() { }
}, class Foo {
constructor() { }
});
}`,
blockStmt([exprStmt(seqExpr([classExpr(ident("Foo"), null, [FooCtor]),
classExpr(ident("Foo"), null, [FooCtor])]))]));
assertStmt(`{ var x = class Foo { constructor() { } }; class Foo { constructor() { } }
}`,
blockStmt([varDecl([{
id: ident("x"),
init: classExpr(ident("Foo"), null, [FooCtor])
}]),
classStmt(ident("Foo"), null, [FooCtor])]));
// Can't make a lexical binding without a block.
assertError("if (1) class Foo { constructor() { } }", SyntaxError);
/* Heritage Expressions */ // It's illegal to have things that look like "multiple inheritance": // non-parenthesized comma expressions.
assertClassError("class NAME extends null, undefined { constructor() { } }", SyntaxError);
// Again check for strict-mode in heritage expressions
assertClassError("class NAME extends (delete x) { constructor() { } }", SyntaxError);
// You must specify an inheritance if you say "extends"
assertClassError("class NAME extends { constructor() { } }", SyntaxError);
// "extends" is still a valid name for a method
assertClass("class NAME { constructor() { }; extends() { } }",
[ctorPlaceholder, simpleMethod("extends", "method", false)]);
// Private getters and setters of the same name are allowed.
assertClass(`class NAME { constructor() { } ${getter[0]} ${setter[0]} }`,
[ctorPlaceholder, getter[1], setter[1]]);
assertClass(`class NAME { constructor() { } ${setter[0]} ${getter[0]} }`,
[ctorPlaceholder, setter[1], getter[1]]);
// Private method names can't be used multiple times, other than for a getter/setter pair. for (const [source1, _] of [method, generator, getter, setter]) { for (const [source2, _] of [method, generator]) {
assertClassError(`class NAME { constructor() { } ${source1} ${source2} }`, SyntaxError);
}
}
assertClassError(`class NAME { constructor() { } ${setter[0]} ${setter[0]} }`, SyntaxError);
assertClassError(`class NAME { constructor() { } ${getter[0]} ${getter[0]} }`, SyntaxError);
/* SuperProperty */ // NOTE: Some of these tests involve object literals, as SuperProperty is a // valid production in any method definition, including in objectl // litterals. These tests still fall here, as |super| is not implemented in // any form without classes.
function assertValidSuperProps(assertion, makeStr, makeExpr, type, generator, args, static,
extending) {
let superProperty = superProp(ident("prop"));
let superMember = superElem(lit("prop"));
// We can also use super from inside arrow functions in method // definitions
["()=>super.prop", arrowExpr([], superProperty)],
["()=>super['prop']", arrowExpr([], superMember)]];
for (let situation of situations) {
let sitStr = situation[0];
let sitExpr = situation[1];
let fun = methodFun("method", type, generator, args, [exprStmt(sitExpr)]);
let str = makeStr(sitStr, type, generator, args, static, extending);
assertion(str, makeExpr(fun, type, static), extending);
}
}
function assertValidSuperPropTypes(assertion, makeStr, makeExpr, static, extending) { for (let type of ["method", "get", "set"]) { if (type === "method") { // methods can also be generators
assertValidSuperProps(assertion, makeStr, makeExpr, type, true, [], static, extending);
assertValidSuperProps(assertion, makeStr, makeExpr, type, false, [], static, extending); continue;
}
function makeClassSuperPropStr(propStr, type, generator, args, static, extending) { return `class NAME ${extending ? "extends null" : ""} {
constructor() { };
${static ? "static" : ""} ${makeSuperPropMethodStr(propStr, type, generator, args)}
}`;
} function makeClassSuperPropExpr(fun, type, static) { // We are going right into assertClass, so we don't have to build the // entire statement. return [ctorPlaceholder,
classMethod(ident("method"), fun, type, static)];
} function doClassSuperPropAssert(str, expr, extending) {
assertClass(str, expr, extending ? lit(null) : null);
} function assertValidClassSuperPropExtends(extending) { // super.prop and super[prop] are valid, regardless of whether the // method is static or not
assertValidSuperPropTypes(doClassSuperPropAssert, makeClassSuperPropStr, makeClassSuperPropExpr, false, extending);
assertValidSuperPropTypes(doClassSuperPropAssert, makeClassSuperPropStr, makeClassSuperPropExpr, true, extending);
} function assertValidClassSuperProps() { // super.prop and super[prop] are valid, regardless of class heritage
assertValidClassSuperPropExtends(false);
assertValidClassSuperPropExtends(true);
}
function makeOLSuperPropStr(propStr, type, generator, args) {
let str = `({ ${makeSuperPropMethodStr(propStr, type, generator, args)} })`; return str;
} function makeOLSuperPropExpr(fun) { return objExpr([{ type: "Property", key: ident("method"), value: fun }]);
} function assertValidOLSuperProps() {
assertValidSuperPropTypes(assertExpr, makeOLSuperPropStr, makeOLSuperPropExpr);
}
// Check all valid uses of SuperProperty
assertValidClassSuperProps();
assertValidOLSuperProps();
// Or inside functions inside method definitions...
assertClassError("class NAME { constructor() { function nested() { super.prop; }}}", SyntaxError);
// Bare super is forbidden
assertError("super", SyntaxError);
// Even where super is otherwise allowed
assertError("{ foo() { super } }", SyntaxError);
assertClassError("class NAME { constructor() { super; } }", SyntaxError);
/* SuperCall */
// SuperCall is invalid outside derived class constructors.
assertError("super()", SyntaxError);
assertError("(function() { super(); })", SyntaxError);
// Even in class constructors
assertClassError("class NAME { constructor() { super(); } }", SyntaxError);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.