Bug 1192412 - Part 1: Set class constructor name to class name. r=efaust

--HG--
extra : rebase_source : 652f849bf960c5038a088e54a2fa72613783ef6a
extra : amend_source : 23aa57602920dc9c44492ce6cbc6d53e0f586c32
This commit is contained in:
Tooru Fujisawa 2015-08-11 08:39:01 +09:00
Родитель 2816d774f0
Коммит df599b65c0
3 изменённых файлов: 294 добавлений и 33 удалений

Просмотреть файл

@ -6392,6 +6392,10 @@ Parser<FullParseHandler>::classDefinition(YieldHandling yieldHandling,
case PropertyType::Setter:
funName = nullptr;
break;
case PropertyType::Constructor:
case PropertyType::DerivedConstructor:
funName = name;
break;
default:
if (tokenStream.isCurrentTokenType(TOK_NAME))
funName = tokenStream.currentName();

Просмотреть файл

@ -0,0 +1,246 @@
function testName(C, name, hasValue, hasGetter, hasSetter, isFunction=false) {
if (isFunction) {
assertEq(typeof C.name, "function");
} else {
assertEq(C.name, name);
}
var desc = Object.getOwnPropertyDescriptor(C, "name");
if (!hasValue && !hasGetter && !hasSetter) {
assertEq(desc, undefined);
return;
}
if (hasValue) {
assertEq("value" in desc, true);
if (isFunction) {
assertEq(typeof desc.value, "function");
} else {
assertEq(desc.value, name);
}
// FIXME: Methods defined in classes should not be enumerable
// (bug 1144630).
// assertEq(desc.enumerable, false);
assertEq(desc.configurable, true);
assertEq("get" in desc, false);
assertEq("set" in desc, false);
return;
}
assertEq("value" in desc, false);
if (hasGetter) {
assertEq("get" in desc, true);
assertEq(desc.get(), name);
// assertEq(desc.enumerable, false);
assertEq(desc.configurable, true);
} else {
assertEq("get" in desc, true);
assertEq(desc.get, undefined);
// assertEq(desc.enumerable, false);
assertEq(desc.configurable, true);
}
if (hasSetter) {
assertEq("set" in desc, true);
assertEq(typeof desc.set, "function");
// assertEq(desc.enumerable, false);
assertEq(desc.configurable, true);
} else {
assertEq("set" in desc, true);
assertEq(desc.set, undefined);
// assertEq(desc.enumerable, false);
assertEq(desc.configurable, true);
}
}
var test = `
// ---- declaration ---
class Decl {
constructor() {}
}
testName(Decl, "Decl", true, false, false);
class DeclWithFunc {
constructor() {}
static name() {}
}
testName(DeclWithFunc, "DeclWithFunc", true, false, false, true);
class DeclWithGetter {
constructor() {}
static get name() { return "base"; }
}
testName(DeclWithGetter, "base", false, true, false);
class DeclWithSetter {
constructor() {}
static set name(v) {}
}
testName(DeclWithSetter, undefined, false, false, true);
class DeclWithGetterSetter {
constructor() {}
static get name() { return "base"; }
static set name(v) {}
}
testName(DeclWithGetterSetter, "base", false, true, true);
function prop() {
return "name";
}
class DeclWithComputed {
constructor() {}
static get [prop()]() { return "base"; }
}
testName(DeclWithGetterSetter, "base", false, true, true);
class ExtendedDecl1 extends Decl {
constructor() {}
}
testName(ExtendedDecl1, "ExtendedDecl1", true, false, false);
delete ExtendedDecl1.name;
testName(ExtendedDecl1, "Decl", false, false, false);
class ExtendedDecl2 extends DeclWithGetterSetter {
constructor() {}
static get name() { return "extend"; }
}
testName(ExtendedDecl2, "extend", false, true, false);
delete ExtendedDecl2.name;
testName(ExtendedDecl2, "base", false, false, false);
class ExtendedDecl3 extends DeclWithGetterSetter {
constructor() {}
static set name(v) {}
}
testName(ExtendedDecl3, undefined, false, false, true);
delete ExtendedDecl3.name;
testName(ExtendedDecl3, "base", false, false, false);
// ---- expression ---
let Expr = class Expr {
constructor() {}
};
testName(Expr, "Expr", true, false, false);
let ExprWithGetter = class ExprWithGetter {
constructor() {}
static get name() { return "base"; }
};
testName(ExprWithGetter, "base", false, true, false);
let ExprWithSetter = class ExprWithSetter {
constructor() {}
static set name(v) {}
};
testName(ExprWithSetter, undefined, false, false, true);
let ExprWithGetterSetter = class ExprWithGetterSetter {
constructor() {}
static get name() { return "base"; }
static set name(v) {}
};
testName(ExprWithGetterSetter, "base", false, true, true);
let ExtendedExpr1 = class ExtendedExpr1 extends Expr {
constructor() {}
};
testName(ExtendedExpr1, "ExtendedExpr1", true, false, false);
delete ExtendedExpr1.name;
testName(ExtendedExpr1, "Expr", false, false, false);
let ExtendedExpr2 = class ExtendedExpr2 extends ExprWithGetterSetter {
constructor() {}
static get name() { return "extend"; }
};
testName(ExtendedExpr2, "extend", false, true, false);
delete ExtendedExpr2.name;
testName(ExtendedExpr2, "base", false, false, false);
let ExtendedExpr3 = class ExtendedExpr3 extends ExprWithGetterSetter {
constructor() {}
static set name(v) {}
};
testName(ExtendedExpr3, undefined, false, false, true);
delete ExtendedExpr3.name;
testName(ExtendedExpr3, "base", false, false, false);
// ---- anonymous ----
let Anon = class {
constructor() {}
};
testName(Anon, "", true, false, false);
let AnonWithGetter = class {
constructor() {}
static get name() { return "base"; }
};
testName(AnonWithGetter, "base", false, true, false);
let AnonWithSetter = class {
constructor() {}
static set name(v) {}
};
testName(AnonWithSetter, undefined, false, false, true);
let AnonWithGetterSetter = class {
constructor() {}
static get name() { return "base"; }
static set name(v) {}
};
testName(AnonWithGetterSetter, "base", false, true, true);
let ExtendedAnon1 = class extends Anon {
constructor() {}
};
testName(ExtendedAnon1, "", true, false, false);
delete ExtendedAnon1.name;
testName(ExtendedAnon1, "", false, false, false);
let ExtendedAnon2 = class extends AnonWithGetterSetter {
constructor() {}
static get name() { return "extend"; }
};
testName(ExtendedAnon2, "extend", false, true, false);
delete ExtendedAnon2.name;
testName(ExtendedAnon2, "base", false, false, false);
let ExtendedAnon3 = class extends AnonWithGetterSetter {
constructor() {}
static set name(v) {}
};
testName(ExtendedAnon3, undefined, false, false, true);
delete ExtendedAnon3.name;
testName(ExtendedAnon3, "base", false, false, false);
// ---- mixed ----
let ExtendedExpr4 = class ExtendedExpr4 extends Anon {
constructor() {}
}
testName(ExtendedExpr4, "ExtendedExpr4", true, false, false);
delete ExtendedExpr4.name;
testName(ExtendedExpr4, "", false, false, false);
let ExtendedExpr5 = class ExtendedExpr5 extends AnonWithGetterSetter {
constructor() {}
static get name() { return "extend"; }
}
testName(ExtendedExpr5, "extend", false, true, false);
delete ExtendedExpr5.name;
testName(ExtendedExpr5, "base", false, false, false);
`;
if (classesEnabled())
eval(test);
if (typeof reportCompare === "function")
reportCompare(0, 0, "OK");

Просмотреть файл

@ -24,6 +24,11 @@ function testClasses() {
methodFun(id, kind, generator, args),
kind, isStatic);
}
function ctorWithName(id) {
return classMethod(ident("constructor"),
methodFun(id, "method", false, []),
"method", false);
}
function emptyCPNMethod(id, isStatic) {
return classMethod(computedName(lit(id)),
funExpr(null, [], blockStmt([])),
@ -34,9 +39,16 @@ function testClasses() {
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) {
let namelessStr = str.replace("NAME", "");
let namedStr = str.replace("NAME", "Foo");
let namedCtor = ctorWithName("NAME");
let namelessCtor = ctorWithName(null);
let namelessMethods = methods.map(x => x == ctorPlaceholder ? namedCtor : x);
let namedMethods = methods.map(x => x == ctorPlaceholder ? namedCtor : x);
assertClassExpr(namelessStr, methods, heritage);
assertClassExpr(namedStr, methods, heritage, ident("Foo"));
@ -52,13 +64,11 @@ function testClasses() {
assertError("(" + str.replace("NAME", "") + ")", error);
}
let simpleConstructor = simpleMethod("constructor", "method", false);
/* Trivial classes */
// Unnamed class statements are forbidden, but unnamed class expressions are
// just fine.
assertError("class { constructor() { } }", SyntaxError);
assertClass("class NAME { constructor() { } }", [simpleConstructor]);
assertClass("class NAME { constructor() { } }", [ctorPlaceholder]);
// A class name must actually be a name
assertNamedClassError("class x.y { constructor() {} }", SyntaxError);
@ -68,13 +78,13 @@ function testClasses() {
// Allow methods and accessors
assertClass("class NAME { constructor() { } method() { } }",
[simpleConstructor, simpleMethod("method", "method", false)]);
[ctorPlaceholder, simpleMethod("method", "method", false)]);
assertClass("class NAME { constructor() { } get method() { } }",
[simpleConstructor, simpleMethod("method", "get", false)]);
[ctorPlaceholder, simpleMethod("method", "get", false)]);
assertClass("class NAME { constructor() { } set method(x) { } }",
[simpleConstructor, simpleMethod("method", "set", false, ["x"])]);
[ctorPlaceholder, simpleMethod("method", "set", false, ["x"])]);
/* Static */
assertClass(`class NAME {
@ -84,7 +94,7 @@ function testClasses() {
static get getter() { };
static set setter(x) { }
}`,
[simpleConstructor,
[ctorPlaceholder,
simpleMethod("method", "method", false, [], true),
simpleMethod("methodGen", "method", true, [], true),
simpleMethod("getter", "get", false, [], true),
@ -92,13 +102,13 @@ function testClasses() {
// It's not an error to have a method named static, static, or not.
assertClass("class NAME { constructor() { } static() { } }",
[simpleConstructor, simpleMethod("static", "method", false)]);
[ctorPlaceholder, simpleMethod("static", "method", false)]);
assertClass("class NAME { static static() { }; constructor() { } }",
[simpleMethod("static", "method", false, [], true), simpleConstructor]);
[simpleMethod("static", "method", false, [], true), ctorPlaceholder]);
assertClass("class NAME { static get static() { }; constructor() { } }",
[simpleMethod("static", "get", false, [], true), simpleConstructor]);
[simpleMethod("static", "get", false, [], true), ctorPlaceholder]);
assertClass("class NAME { constructor() { }; static set static(x) { } }",
[simpleConstructor, simpleMethod("static", "set", false, ["x"], true)]);
[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);
@ -112,7 +122,7 @@ function testClasses() {
// You are, however, allowed to have a CPN called prototype as a static
assertClass("class NAME { constructor() { }; static [\"prototype\"]() { } }",
[simpleConstructor, emptyCPNMethod("prototype", true)]);
[ctorPlaceholder, emptyCPNMethod("prototype", true)]);
/* Constructor */
// Currently, we do not allow default constructors
@ -141,14 +151,14 @@ function testClasses() {
let str = "class NAME { constructor() { } " +
methods[i][0] + " " + methods[j][0] +
" }";
assertClass(str, [simpleConstructor, methods[i][1], methods[j][1]]);
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\"] () { } }",
[simpleConstructor, emptyCPNMethod("constructor", false)]);
[ctorPlaceholder, emptyCPNMethod("constructor", false)]);
// It is an error to have a generator or accessor named constructor
assertClassError("class NAME { *constructor() { } }", SyntaxError);
@ -157,14 +167,14 @@ function testClasses() {
/* Semicolons */
// Allow Semicolons in Class Definitions
assertClass("class NAME { constructor() { }; }", [simpleConstructor]);
assertClass("class NAME { constructor() { }; }", [ctorPlaceholder]);
// Allow more than one semicolon, even in otherwise trivial classses
assertClass("class NAME { ;;; constructor() { } }", [simpleConstructor]);
assertClass("class NAME { ;;; constructor() { } }", [ctorPlaceholder]);
// Semicolons are optional, even if the methods share a line
assertClass("class NAME { method() { } constructor() { } }",
[simpleMethod("method", "method", false), simpleConstructor]);
[simpleMethod("method", "method", false), ctorPlaceholder]);
/* Generators */
// No yield as a class name inside a generator
@ -184,7 +194,7 @@ function testClasses() {
assertClassError("class NAME { constructor() { } *set foo() { } }", SyntaxError);
assertClass("class NAME { *method() { } constructor() { } }",
[simpleMethod("method", "method", true), simpleConstructor]);
[simpleMethod("method", "method", true), ctorPlaceholder]);
/* Strictness */
// yield is a strict-mode keyword, and class definitions are always strict.
@ -197,14 +207,15 @@ function testClasses() {
/* 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() { } } }", TypeError);
assertStmt("{ let Foo; (class Foo { constructor() { } }) }",
blockStmt([letDecl([{id: ident("Foo"), init: null}]),
exprStmt(classExpr(ident("Foo"), null, [simpleConstructor]))]));
exprStmt(classExpr(ident("Foo"), null, [FooCtor]))]));
assertError("{ const Foo = 0; class Foo { constructor() { } } }", TypeError);
assertStmt("{ const Foo = 0; (class Foo { constructor() { } }) }",
blockStmt([constDecl([{id: ident("Foo"), init: lit(0)}]),
exprStmt(classExpr(ident("Foo"), null, [simpleConstructor]))]));
exprStmt(classExpr(ident("Foo"), null, [FooCtor]))]));
assertError("{ class Foo { constructor() { } } class Foo { constructor() { } } }", TypeError);
assertStmt(`{
(class Foo {
@ -214,16 +225,16 @@ function testClasses() {
constructor() { }
});
}`,
blockStmt([exprStmt(seqExpr([classExpr(ident("Foo"), null, [simpleConstructor]),
classExpr(ident("Foo"), null, [simpleConstructor])]))]));
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, [simpleConstructor])
init: classExpr(ident("Foo"), null, [FooCtor])
}]),
classStmt(ident("Foo"), null, [simpleConstructor])]));
classStmt(ident("Foo"), null, [FooCtor])]));
// Can't make a lexical binding without a block.
@ -242,36 +253,36 @@ function testClasses() {
// "extends" is still a valid name for a method
assertClass("class NAME { constructor() { }; extends() { } }",
[simpleConstructor, simpleMethod("extends", "method", false)]);
[ctorPlaceholder, simpleMethod("extends", "method", false)]);
// Immediate expression
assertClass("class NAME extends null { constructor() { } }",
[simpleConstructor], lit(null));
[ctorPlaceholder], lit(null));
// Sequence expresson
assertClass("class NAME extends (undefined, undefined) { constructor() { } }",
[simpleConstructor], seqExpr([ident("undefined"), ident("undefined")]));
[ctorPlaceholder], seqExpr([ident("undefined"), ident("undefined")]));
// Function expression
let emptyFunction = funExpr(null, [], blockStmt([]));
assertClass("class NAME extends function(){ } { constructor() { } }",
[simpleConstructor], emptyFunction);
[ctorPlaceholder], emptyFunction);
// New expression
assertClass("class NAME extends new function(){ }() { constructor() { } }",
[simpleConstructor], newExpr(emptyFunction, []));
[ctorPlaceholder], newExpr(emptyFunction, []));
// Call expression
assertClass("class NAME extends function(){ }() { constructor() { } }",
[simpleConstructor], callExpr(emptyFunction, []));
[ctorPlaceholder], callExpr(emptyFunction, []));
// Dot expression
assertClass("class NAME extends {}.foo { constructor() { } }",
[simpleConstructor], dotExpr(objExpr([]), ident("foo")));
[ctorPlaceholder], dotExpr(objExpr([]), ident("foo")));
// Member expression
assertClass("class NAME extends {}[foo] { constructor() { } }",
[simpleConstructor], memExpr(objExpr([]), ident("foo")));
[ctorPlaceholder], memExpr(objExpr([]), ident("foo")));
/* SuperProperty */
// NOTE: Some of these tests involve object literals, as SuperProperty is a
@ -367,7 +378,7 @@ function testClasses() {
function makeClassSuperPropExpr(fun, type, static) {
// We are going right into assertClass, so we don't have to build the
// entire statement.
return [simpleConstructor,
return [ctorPlaceholder,
classMethod(ident("method"), fun, type, static)];
}
function doClassSuperPropAssert(str, expr, extending) {