Skip to content

Commit

Permalink
Use super.apply(this, arguments) in constructors automatically genera…
Browse files Browse the repository at this point in the history
…ted by

Es6ConvertSuper.

Minor fixes were required in TypeInference and NewTypeInference for this.

This will allow all handling of constructor transpilation to be done
in Es6ConvertSuperConstructorCalls. This is desirable for consistency and
because type information and scope information are available to that later pass.

Related to #1669
Related to #2098
Related to #2102

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=137332082
  • Loading branch information
brad4d authored and blickly committed Oct 26, 2016
1 parent 4814b76 commit 27f9d8d
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 11 deletions.
2 changes: 1 addition & 1 deletion src/com/google/javascript/jscomp/Es6ConvertSuper.java
Expand Up @@ -80,7 +80,7 @@ private void addSyntheticConstructor(NodeTraversal t, Node classNode) {
// transpile, so don't generate it.
if (!classNode.isFromExterns() && !isInterface(classNode)) {
Node exprResult = IR.exprResult(IR.call(
IR.getprop(superClass.cloneTree(), IR.string("apply")),
IR.getprop(IR.superNode(), IR.string("apply")),
IR.thisNode(),
IR.name("arguments")));
body.addChildToFront(exprResult);
Expand Down
Expand Up @@ -87,28 +87,34 @@ private void visitSuperCall(Node superNode, Node superCall) {
}

/**
* Converts {@code super.apply(null, ..args..)} to {@code SuperClass.apply(this, ..args..)}.
* Converts {@code super.apply(null|this, ..args..)} to {@code SuperClass.apply(this, ..args..)}.
*
* @param superNode
* @param superDotApply
*/
private void visitSuperApplyCall(Node superNode, Node superDotApply) {
// super.apply(null, ...)
// super.apply(null, ...) is generated by spread transpilation
// super.apply(this, arguments) is used by Es6ConvertSuper in automatically-generated
// constructors.
checkArgument(superDotApply.isGetProp(), superDotApply);
Node applyNode = checkNotNull(superDotApply.getSecondChild());
checkState(applyNode.getString().equals("apply"), applyNode);

Node superCall = superDotApply.getParent();
checkState(superCall.isCall(), superCall);
checkState(superCall.getFirstChild() == superDotApply, superCall);
Node nullNode = superCall.getSecondChild();
checkState(nullNode.isNull(), nullNode);

Node superClassQName = createSuperClassQNameNode(superCall);
superClassQName.useSourceInfoFromForTree(superNode);
superDotApply.replaceChild(superNode, superClassQName);
Node thisNode = IR.thisNode().useSourceInfoFrom(nullNode);
superCall.replaceChild(nullNode, thisNode);

Node nullOrThisNode = superCall.getSecondChild();
if (nullOrThisNode.isNull()) {
Node thisNode = IR.thisNode().useSourceInfoFrom(nullOrThisNode);
superCall.replaceChild(nullOrThisNode, thisNode);
} else {
checkState(nullOrThisNode.isThis(), nullOrThisNode);
}
compiler.reportCodeChange();
}

Expand Down
21 changes: 19 additions & 2 deletions src/com/google/javascript/jscomp/NewTypeInference.java
Expand Up @@ -175,6 +175,11 @@ final class NewTypeInference implements CompilerPass {
"JSC_NTI_INSTANTIATE_ABSTRACT_CLASS",
"Cannot instantiate abstract class {0}.");

static final DiagnosticType UNDEFINED_SUPER_CLASS =
DiagnosticType.warning(
"JSC_UNDEFINED_SUPER_CLASS",
"Undefined super class for {0}.");

static final DiagnosticType ASSERT_FALSE =
DiagnosticType.warning(
"JSC_NTI_ASSERT_FALSE",
Expand Down Expand Up @@ -2371,7 +2376,13 @@ private EnvTypePair analyzeSuperFwd(Node expr, TypeEnv inEnv) {
if (currentScope.hasThis()) {
NominalType thisClass = Preconditions.checkNotNull(
envGetType(inEnv, THIS_ID).getNominalTypeIfSingletonObj());
NominalType superClass = Preconditions.checkNotNull(thisClass.getInstantiatedSuperclass());
NominalType superClass = thisClass.getInstantiatedSuperclass();
if (superClass == null) {
// This indicates bad code and there will probably be other errors reported.
// In particular JSC_NTI_INHERITANCE_CYCLE for `class Foo extends Foo ...`.
warnings.add(JSError.make(expr, UNDEFINED_SUPER_CLASS, thisClass.toString()));
return new EnvTypePair(inEnv, UNKNOWN);
}
if (currentScope.isConstructor()) {
JSType superCtor = commonTypes.fromFunctionType(superClass.getConstructorFunction());
return new EnvTypePair(inEnv, superCtor);
Expand All @@ -2384,7 +2395,13 @@ private EnvTypePair analyzeSuperFwd(Node expr, TypeEnv inEnv) {
JSType thisClassAsJstype = analyzeExprFwd(classNameNode, inEnv).type;
FunctionType thisCtor = thisClassAsJstype.getFunTypeIfSingletonObj();
NominalType thisClass = thisCtor.getThisType().getNominalTypeIfSingletonObj();
NominalType superClass = Preconditions.checkNotNull(thisClass.getInstantiatedSuperclass());
NominalType superClass = thisClass.getInstantiatedSuperclass();
if (superClass == null) {
// This indicates bad code and there will probably be other errors reported.
// In particular JSC_NTI_INHERITANCE_CYCLE for `class Foo extends Foo ...`.
warnings.add(JSError.make(expr, UNDEFINED_SUPER_CLASS, funName.toString()));
return new EnvTypePair(inEnv, UNKNOWN);
}
return new EnvTypePair(inEnv, superClass.getNamespaceType());
}

Expand Down
3 changes: 3 additions & 0 deletions src/com/google/javascript/jscomp/TypeInference.java
Expand Up @@ -483,7 +483,10 @@ private void traverseSuper(Node superNode) {
superNode.setJSType(
new FunctionBuilder(registry)
.copyFromOtherFunction(superConstructorType)
// Invocations of super() don't use new
.setIsConstructor(false)
// Even if the super class is abstract, we still need to call its constructor.
.withIsAbstract(false) //
.withTypeOfThis(null)
.build());
} else {
Expand Down
Expand Up @@ -165,8 +165,10 @@ public void testClass() {
"(new Bar).prop = '3'"),
NewTypeInference.MISTYPED_ASSIGN_RHS);

typeCheck("class Foo extends Foo {}",
JSTypeCreatorFromJSDoc.INHERITANCE_CYCLE);
typeCheck(
"class Foo extends Foo {}",
JSTypeCreatorFromJSDoc.INHERITANCE_CYCLE,
NewTypeInference.UNDEFINED_SUPER_CLASS);
}

public void testInterface() {
Expand Down

0 comments on commit 27f9d8d

Please sign in to comment.