Skip to content

Commit

Permalink
Add transpilation support for reading super properties and computed p…
Browse files Browse the repository at this point in the history
…roperties

Closes #2418

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=158983919
  • Loading branch information
ChadKillingsworth authored and Tyler Breisacher committed Jun 14, 2017
1 parent d6b015d commit 467c4ed
Show file tree
Hide file tree
Showing 4 changed files with 415 additions and 45 deletions.
51 changes: 40 additions & 11 deletions src/com/google/javascript/jscomp/Es6ConvertSuper.java
Expand Up @@ -133,6 +133,7 @@ public boolean apply(Node n) {
case MEMBER_FUNCTION_DEF:
case GETTER_DEF:
case SETTER_DEF:
case COMPUTED_PROP:
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,36 @@ 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();

if (NodeUtil.isLValue(parent)) {
// We don't support assigning to a super property
compiler.report(
JSError.make(parent, CANNOT_CONVERT_YET, "assigning to a super property"));
return;
}

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, "super");
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
Expand Up @@ -1025,7 +1025,7 @@ public void testAlternativeSuperCalls() {
}

public void testComputedSuper() {
testError(
test(
LINE_JOINER.join(
"class Foo {",
" ['m']() { return 1; }",
Expand All @@ -1036,7 +1036,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 @@ -1268,28 +1275,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

0 comments on commit 467c4ed

Please sign in to comment.