зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
2816d774f0
Коммит
df599b65c0
|
@ -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) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче