Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add transpilation support for properties and computed properties from super #2418

Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 33 additions & 11 deletions src/com/google/javascript/jscomp/Es6ConvertSuper.java
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ public boolean apply(Node n) {
case MEMBER_FUNCTION_DEF:
case GETTER_DEF:
case SETTER_DEF:
case COMPUTED_PROP:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why add COMPUTED_PROP? Are you trying to support something like this?

class Foo extends Bar {
  [super.something]() {}
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No - need to recognize access to super within a computed property function:

class Foo extends Bar {
  [someThing]() {
    super.otherThing();
  }
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return true;
default:
return false;
Expand All @@ -143,24 +144,20 @@ public boolean apply(Node n) {
if (parent.isCall()) {
// super(...)
visitSuperCall(node, parent, enclosingMemberDef);
} else if (parent.isGetProp()) {
} else if (parent.isGetProp() || parent.isGetElem()) {
if (parent.getFirstChild() == node) {
if (parent.getParent().isCall() && NodeUtil.isCallOrNewTarget(parent)) {
// super.something(...)
// super.something(...) or super['something'](..)
visitSuperPropertyCall(node, parent, enclosingMemberDef);
} else {
// super.something
compiler.report(JSError.make(node, CANNOT_CONVERT_YET,
"Only calls to super or to a method of super are supported."));
// super.something or super['something']
visitSuperPropertyAccess(node, parent, enclosingMemberDef);
}
} else {
// super.something used in some other way
compiler.report(JSError.make(node, CANNOT_CONVERT_YET,
"Only calls to super or to a method of super are supported."));
}
} else if (parent.isGetElem()) {
compiler.report(JSError.make(node, CANNOT_CONVERT_YET,
"Only calls to super or to a method of super are supported."));
} else if (parent.isNew()) {
// new super(...)
compiler.report(JSError.make(node, INVALID_SUPER_CALL));
Expand Down Expand Up @@ -190,8 +187,10 @@ private void visitSuperCall(Node node, Node parent, Node enclosingMemberDef) {
// implementation that should be instantiated.
// A call to super() shouldn't actually exist for these cases and is problematic to
// transpile, so just drop it.
NodeUtil.getEnclosingStatement(node).detach();
compiler.reportCodeChange();
Node enclosingStatement = NodeUtil.getEnclosingStatement(node);
Node enclosingStatementParent = enclosingStatement.getParent();
enclosingStatement.detach();
compiler.reportChangeToEnclosingScope(enclosingStatementParent);
}
// Calls to super() constructors will be transpiled by Es6ConvertSuperConstructorCalls
// later.
Expand All @@ -204,7 +203,7 @@ private void visitSuperCall(Node node, Node parent, Node enclosingMemberDef) {
}

private void visitSuperPropertyCall(Node node, Node parent, Node enclosingMemberDef) {
Preconditions.checkState(parent.isGetProp(), parent);
Preconditions.checkState(parent.isGetProp() || parent.isGetElem(), parent);
Preconditions.checkState(node.isSuper(), node);
Node grandparent = parent.getParent();
Preconditions.checkState(grandparent.isCall());
Expand Down Expand Up @@ -236,6 +235,29 @@ private void visitSuperPropertyCall(Node node, Node parent, Node enclosingMember
compiler.reportChangeToEnclosingScope(grandparent);
}

private void visitSuperPropertyAccess(Node node, Node parent, Node enclosingMemberDef) {
Preconditions.checkState(parent.isGetProp() || parent.isGetElem(), parent);
Preconditions.checkState(node.isSuper(), node);
Node grandparent = parent.getParent();

Node clazz = NodeUtil.getEnclosingClass(node);
Node superName = clazz.getSecondChild();
if (!superName.isQualifiedName()) {
// This will be reported as an error in Es6ToEs3Converter.
return;
}

if (enclosingMemberDef.isStaticMember()) {
node.replaceWith(superName.cloneTree());
} else {
String newPropName = Joiner.on('.').join(superName.getQualifiedName(), "prototype");
Node newprop = NodeUtil.newQName(compiler, newPropName);
node.replaceWith(newprop);
}

compiler.reportChangeToEnclosingScope(grandparent);
}

@Override
public void process(Node externs, Node root) {
// Might need to synthesize constructors for ambient classes in .d.ts externs
Expand Down
160 changes: 142 additions & 18 deletions test/com/google/javascript/jscomp/Es6RewriteClassTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1017,7 +1017,7 @@ public void testAlternativeSuperCalls() {
}

public void testComputedSuper() {
testError(
test(
LINE_JOINER.join(
"class Foo {",
" ['m']() { return 1; }",
Expand All @@ -1028,7 +1028,14 @@ public void testComputedSuper() {
" return super['m']() + 1;",
" }",
"}"),
CANNOT_CONVERT_YET);
LINE_JOINER.join(
"/** @constructor @struct */",
"let Foo = function() {};",
"Foo.prototype['m'] = function() { return 1; };",
"/** @constructor @struct @extends {Foo} @param {...?} var_args */",
"let Bar = function(var_args) { Foo.apply(this, arguments); };",
"$jscomp.inherits(Bar, Foo);",
"Bar.prototype['m'] = function () { return Foo.prototype['m'].call(this) + 1; };"));
}

public void testSuperMethodInGetter() {
Expand Down Expand Up @@ -1260,28 +1267,145 @@ public void testClassNested() {
}

public void testSuperGet() {
testError("class D {} class C extends D { f() {var i = super.c;} }",
CANNOT_CONVERT_YET);
test(
"class D { d() {} } class C extends D { f() {var i = super.d;} }",
LINE_JOINER.join(
"/** @constructor @struct */",
"let D = function() {};",
"D.prototype.d = function() {};",
"/**",
" * @constructor @struct",
" * @param {...?} var_args",
" * @extends{D} */",
"let C = function(var_args) {",
" D.apply(this, arguments); ",
"};",
"$jscomp.inherits(C, D);",
"C.prototype.f = function() {",
" var i = D.prototype.d;",
"};"));

test(
"class D { ['d']() {} } class C extends D { f() {var i = super['d'];} }",
LINE_JOINER.join(
"/** @constructor @struct */",
"let D = function() {};",
"D.prototype['d'] = function() {};",
"/**",
" * @constructor @struct",
" * @param {...?} var_args",
" * @extends{D} */",
"let C = function(var_args) {",
" D.apply(this, arguments); ",
"};",
"$jscomp.inherits(C, D);",
"C.prototype.f = function() {",
" var i = D.prototype['d'];",
"};"));

testError("class D {} class C extends D { static f() {var i = super.c;} }",
CANNOT_CONVERT_YET);
test(
"class D { d() {}} class C extends D { static f() {var i = super.d;} }",
LINE_JOINER.join(
"/** @constructor @struct */",
"let D = function() {};",
"D.prototype.d = function() {};",
"/**",
" * @constructor @struct",
" * @param {...?} var_args",
" * @extends{D} */",
"let C = function(var_args) {",
" D.apply(this, arguments); ",
"};",
"$jscomp.inherits(C, D);",
"C.f = function() {",
" var i = D.d;",
"};"));

testError("class D {} class C extends D { f() {var i; i = super[s];} }",
CANNOT_CONVERT_YET);
test(
"class D { ['d']() {}} class C extends D { static f() {var i = super['d'];} }",
LINE_JOINER.join(
"/** @constructor @struct */",
"let D = function() {};",
"D.prototype['d'] = function() {};",
"/**",
" * @constructor @struct",
" * @param {...?} var_args",
" * @extends{D} */",
"let C = function(var_args) {",
" D.apply(this, arguments); ",
"};",
"$jscomp.inherits(C, D);",
"C.f = function() {",
" var i = D['d'];",
"};"));

testError("class D {} class C extends D { f() {return super.s;} }",
CANNOT_CONVERT_YET);
test(
"class D {} class C extends D { f() {return super.s;} }",
LINE_JOINER.join(
"/** @constructor @struct */",
"let D = function() {};",
"/**",
" * @constructor @struct",
" * @param {...?} var_args",
" * @extends{D} */",
"let C = function(var_args) {",
" D.apply(this, arguments); ",
"};",
"$jscomp.inherits(C, D);",
"C.prototype.f = function() {",
" return D.prototype.s;",
"};"));

testError("class D {} class C extends D { f() {m(super.s);} }",
CANNOT_CONVERT_YET);
test(
"class D {} class C extends D { f() { m(super.s);} }",
LINE_JOINER.join(
"/** @constructor @struct */",
"let D = function() {};",
"/**",
" * @constructor @struct",
" * @param {...?} var_args",
" * @extends{D} */",
"let C = function(var_args) {",
" D.apply(this, arguments); ",
"};",
"$jscomp.inherits(C, D);",
"C.prototype.f = function() {",
" m(D.prototype.s);",
"};"));

testError(
"class D {} class C extends D { foo() { return super.m.foo(); } }",
CANNOT_CONVERT_YET);
test(
"class D {} class C extends D { foo() { return super.m.foo();} }",
LINE_JOINER.join(
"/** @constructor @struct */",
"let D = function() {};",
"/**",
" * @constructor @struct",
" * @param {...?} var_args",
" * @extends{D} */",
"let C = function(var_args) {",
" D.apply(this, arguments); ",
"};",
"$jscomp.inherits(C, D);",
"C.prototype.foo = function() {",
" return D.prototype.m.foo();",
"};"));

testError(
"class D {} class C extends D { static foo() { return super.m.foo(); } }",
CANNOT_CONVERT_YET);
test(
"class D {} class C extends D { static foo() { return super.m.foo();} }",
LINE_JOINER.join(
"/** @constructor @struct */",
"let D = function() {};",
"/**",
" * @constructor @struct",
" * @param {...?} var_args",
" * @extends{D} */",
"let C = function(var_args) {",
" D.apply(this, arguments); ",
"};",
"$jscomp.inherits(C, D);",
"C.foo = function() {",
" return D.m.foo();",
"};"));
}

public void testSuperNew() {
Expand Down
Loading