diff options
Diffstat (limited to 'js/src/tests/non262/reflect-parse/classes.js')
-rw-r--r-- | js/src/tests/non262/reflect-parse/classes.js | 552 |
1 files changed, 552 insertions, 0 deletions
diff --git a/js/src/tests/non262/reflect-parse/classes.js b/js/src/tests/non262/reflect-parse/classes.js new file mode 100644 index 0000000000..609c0d1e10 --- /dev/null +++ b/js/src/tests/non262/reflect-parse/classes.js @@ -0,0 +1,552 @@ +// |reftest| slow skip-if(!xulRuntime.shell) +// Classes +function testClasses() { + function methodFun(id, kind, generator, args, body = []) { + assertEq(generator && kind === "method", generator); + assertEq(typeof id === 'string' || id === null, true); + let methodName; + switch (kind) { + case "method": + methodName = typeof id === 'string' ? ident(id) : null; + break; + case "get": + case "set": + methodName = ident(`${kind} ${typeof id === 'string' ? id : ""}`); + break; + default: + methodName = null; + break; + } + return generator + ? genFunExpr("es6", methodName, args.map(ident), blockStmt(body)) + : funExpr(methodName, args.map(ident), blockStmt(body)); + } + + function simpleMethod(id, kind, generator, args=[], isStatic=false) { + return classMethod(ident(id), + methodFun(id, kind, generator, args), + kind, isStatic); + } + function ctorWithName(id, body = []) { + return classMethod(ident("constructor"), + methodFun(id, "method", false, [], body), + "method", false); + } + function emptyCPNMethod(id, isStatic) { + return classMethod(computedName(lit(id)), + funExpr(null, [], blockStmt([])), + "method", isStatic); + } + + 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")); + + let template = classStmt(ident("Foo"), heritage, namedMethods); + assertStmt(namedStr, template); + } + function assertNamedClassError(str, error) { + assertError(str, error); + assertError("(" + str + ")", error); + } + function assertClassError(str, error) { + assertNamedClassError(str, error); + assertError("(" + str.replace("NAME", "") + ")", error); + } + + /* 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"])]); + + /* Static */ + assertClass(`class NAME { + constructor() { }; + static method() { }; + static *methodGen() { }; + static get getter() { }; + static set setter(x) { } + }`, + [ctorPlaceholder, + simpleMethod("method", "method", false, [], true), + simpleMethod("methodGen", "method", true, [], true), + simpleMethod("getter", "get", false, [], true), + simpleMethod("setter", "set", false, ["x"], true)]); + + // 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); + + assertClass("class NAME { *method() { } constructor() { } }", + [simpleMethod("method", "method", true), ctorPlaceholder]); + + /* 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)]); + + // Immediate expression + assertClass("class NAME extends null { constructor() { } }", + [ctorPlaceholder], lit(null)); + + // Sequence expresson + assertClass("class NAME extends (undefined, undefined) { constructor() { } }", + [ctorPlaceholder], seqExpr([ident("undefined"), ident("undefined")])); + + // Function expression + let emptyFunction = funExpr(null, [], blockStmt([])); + assertClass("class NAME extends function(){ } { constructor() { } }", + [ctorPlaceholder], emptyFunction); + + // New expression + assertClass("class NAME extends new function(){ }() { constructor() { } }", + [ctorPlaceholder], newExpr(emptyFunction, [])); + + // Call expression + assertClass("class NAME extends function(){ }() { constructor() { } }", + [ctorPlaceholder], callExpr(emptyFunction, [])); + + // Dot expression + assertClass("class NAME extends {}.foo { constructor() { } }", + [ctorPlaceholder], dotExpr(objExpr([]), ident("foo"))); + + // Member expression + assertClass("class NAME extends {}[foo] { constructor() { } }", + [ctorPlaceholder], memExpr(objExpr([]), ident("foo"))); + + if (getRealmConfiguration().privateMethods) { + // #constructor is an invalid private method name. + assertClassError("class NAME { #constructor() { } }", SyntaxError); + + const method = ["#method() { }", simpleMethod("#method", "method", false)]; + const generator = ["*#method() { }", simpleMethod("#method", "method", true)]; + const getter = ["get #method() { }", simpleMethod("#method", "get", false)]; + const setter = ["set #method(x) { }", simpleMethod("#method", "set", false, ["x"])]; + for (const [source, parsed] of [method, generator, getter, setter]) { + assertClass(`class NAME { constructor() { } ${source} }`, [ctorPlaceholder, parsed]); + } + + // 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")); + + let situations = [ + ["super.prop", superProperty], + ["super['prop']", superMember], + ["super.prop()", callExpr(superProperty, [])], + ["super['prop']()", callExpr(superMember, [])], + ["new super.prop()", newExpr(superProperty, [])], + ["new super['prop']()", newExpr(superMember, [])], + ["delete super.prop", unExpr("delete", superProperty)], + ["delete super['prop']", unExpr("delete", superMember)], + ["++super.prop", updExpr("++", superProperty, true)], + ["super['prop']--", updExpr("--", superMember, false)], + ["super.prop = 4", aExpr("=", superProperty, lit(4))], + ["super['prop'] = 4", aExpr("=", superMember, lit(4))], + ["super.prop += 4", aExpr("+=", superProperty, lit(4))], + ["super['prop'] += 4", aExpr("+=", superMember, lit(4))], + ["super.prop -= 4", aExpr("-=", superProperty, lit(4))], + ["super['prop'] -= 4", aExpr("-=", superMember, lit(4))], + ["super.prop *= 4", aExpr("*=", superProperty, lit(4))], + ["super['prop'] *= 4", aExpr("*=", superMember, lit(4))], + ["super.prop /= 4", aExpr("/=", superProperty, lit(4))], + ["super['prop'] /= 4", aExpr("/=", superMember, lit(4))], + ["super.prop %= 4", aExpr("%=", superProperty, lit(4))], + ["super['prop'] %= 4", aExpr("%=", superMember, lit(4))], + ["super.prop <<= 4", aExpr("<<=", superProperty, lit(4))], + ["super['prop'] <<= 4", aExpr("<<=", superMember, lit(4))], + ["super.prop >>= 4", aExpr(">>=", superProperty, lit(4))], + ["super['prop'] >>= 4", aExpr(">>=", superMember, lit(4))], + ["super.prop >>>= 4", aExpr(">>>=", superProperty, lit(4))], + ["super['prop'] >>>= 4", aExpr(">>>=", superMember, lit(4))], + ["super.prop |= 4", aExpr("|=", superProperty, lit(4))], + ["super['prop'] |= 4", aExpr("|=", superMember, lit(4))], + ["super.prop ^= 4", aExpr("^=", superProperty, lit(4))], + ["super['prop'] ^= 4", aExpr("^=", superMember, lit(4))], + ["super.prop &= 4", aExpr("&=", superProperty, lit(4))], + ["super['prop'] &= 4", aExpr("&=", superMember, lit(4))], + + // 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; + } + + // Setters require 1 argument, and getters require 0 + assertValidSuperProps(assertion, makeStr, makeExpr, type, false, + type === "set" ? ["x"] : [], static, extending); + } + } + + function makeSuperPropMethodStr(propStr, type, generator, args) { + return `${type === "method" ? "" : type} ${generator ? "*" : ""} method(${args.join(",")}) + { + ${propStr}; + }`; + } + + 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(); + + // SuperProperty is forbidden outside of method definitions. + assertError("function foo () { super.prop; }", SyntaxError); + assertError("(function () { super.prop; }", SyntaxError); + assertError("(()=>super.prop)", SyntaxError); + assertError("function *foo() { super.prop; }", SyntaxError); + assertError("super.prop", SyntaxError); + assertError("function foo () { super['prop']; }", SyntaxError); + assertError("(function () { super['prop']; }", SyntaxError); + assertError("(()=>super['prop'])", SyntaxError); + assertError("function *foo() { super['prop']; }", SyntaxError); + assertError("super['prop']", SyntaxError); + + // 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); + + function superConstructor(args) { + return classMethod(ident("constructor"), + methodFun("NAME", "method", false, + [], [exprStmt(superCallExpr(args))]), + "method", false); + } + + function superCallBody(args) { + return [exprStmt(superCallExpr(args))]; + } + + // SuperCall works with various argument configurations. + assertClass("class NAME extends null { constructor() { super() } }", + [ctorPlaceholder], lit(null), superCallBody([])); + assertClass("class NAME extends null { constructor() { super(1) } }", + [ctorPlaceholder], lit(null), superCallBody([lit(1)])); + assertClass("class NAME extends null { constructor() { super(1, a) } }", + [ctorPlaceholder], lit(null), superCallBody([lit(1), ident("a")])); + assertClass("class NAME extends null { constructor() { super(...[]) } }", + [ctorPlaceholder], lit(null), superCallBody([spread(arrExpr([]))])); + + /* EOF */ + // Clipped classes should throw a syntax error + assertClassError("class NAME {", SyntaxError); + assertClassError("class NAME {;", SyntaxError); + assertClassError("class NAME { constructor", SyntaxError); + assertClassError("class NAME { constructor(", SyntaxError); + assertClassError("class NAME { constructor()", SyntaxError); + assertClassError("class NAME { constructor()", SyntaxError); + assertClassError("class NAME { constructor() {", SyntaxError); + assertClassError("class NAME { constructor() { }", SyntaxError); + assertClassError("class NAME { static", SyntaxError); + assertClassError("class NAME { static y", SyntaxError); + assertClassError("class NAME { static *", SyntaxError); + assertClassError("class NAME { static *y", SyntaxError); + assertClassError("class NAME { static get", SyntaxError); + assertClassError("class NAME { static get y", SyntaxError); + assertClassError("class NAME { static ;", SyntaxError); + assertClassError("class NAME extends", SyntaxError); + assertClassError("class NAME { constructor() { super", SyntaxError); + assertClassError("class NAME { constructor() { super.", SyntaxError); + assertClassError("class NAME { constructor() { super.x", SyntaxError); + assertClassError("class NAME { constructor() { super.m(", SyntaxError); + assertClassError("class NAME { constructor() { super[", SyntaxError); + assertClassError("class NAME { constructor() { super(", SyntaxError); + + // Can not omit curly brackets + assertClassError("class NAME { constructor() ({}) }", SyntaxError); + assertClassError("class NAME { constructor() void 0 }", SyntaxError); + assertClassError("class NAME { constructor() 1 }", SyntaxError); + assertClassError("class NAME { constructor() false }", SyntaxError); + assertClassError("class NAME { constructor() {} a() ({}) }", SyntaxError); + assertClassError("class NAME { constructor() {} a() void 0 }", SyntaxError); + assertClassError("class NAME { constructor() {} a() 1 }", SyntaxError); + assertClassError("class NAME { constructor() {} a() false }", SyntaxError); + +} + +runtest(testClasses); |