Skip to content

Commit

Permalink
Merge pull request #4802 from sjrd/fix-super-call-to-path-dep-super-j…
Browse files Browse the repository at this point in the history
…s-class

Fix #4801: Rebase the super JS type as seen from the this type in JS super call.
  • Loading branch information
gzm0 committed Jan 31, 2023
2 parents 9a2df76 + 3cef9d0 commit c1050f0
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 6 deletions.
Expand Up @@ -317,15 +317,15 @@ abstract class ExplicitLocalJS[G <: Global with Singleton](val global: G)
case Apply(fun @ Select(sup: Super, _), _)
if !fun.symbol.isConstructor &&
isInnerOrLocalJSClass(sup.symbol.superClass) =>
wrapWithContextualJSClassValue(sup.symbol.superClass.tpe_*) {
wrapWithContextualSuperJSClassValue(sup.symbol.superClass) {
super.transform(tree)
}

// Same for a super call with type parameters
case Apply(TypeApply(fun @ Select(sup: Super, _), _), _)
if !fun.symbol.isConstructor &&
isInnerOrLocalJSClass(sup.symbol.superClass) =>
wrapWithContextualJSClassValue(sup.symbol.superClass.tpe_*) {
wrapWithContextualSuperJSClassValue(sup.symbol.superClass) {
super.transform(tree)
}

Expand Down Expand Up @@ -394,6 +394,38 @@ abstract class ExplicitLocalJS[G <: Global with Singleton](val global: G)
}
}

/** Wraps with the contextual super JS class value for super calls. */
private def wrapWithContextualSuperJSClassValue(superClass: Symbol)(
tree: Tree): Tree = {
/* #4801 We need to interpret the superClass type as seen from the
* current class' thisType.
*
* For example, in the test NestedJSClassTest.extendInnerJSClassInClass,
* the original `superClass.tpe_*` is
*
* OuterNativeClass_Issue4402.this.InnerClass
*
* because `InnerClass` is path-dependent. However, the path
* `OuterNativeClass.this` is only valid within `OuterNativeClass`
* itself. In the context of the current local class `Subclass`, this
* path must be replaced by the actual path `outer.`. This is precisely
* the role of `asSeenFrom`. We tell it to replace any `superClass.this`
* by `currentClass.this`, and it also transitively replaces paths for
* outer classes of `superClass`, matching them with the corresponding
* outer paths of `currentClass.thisType` if necessary. The result for
* that test case is
*
* outer.InnerClass
*/
val jsClassTypeInSuperClass = superClass.tpe_*
val jsClassTypeAsSeenFromThis =
jsClassTypeInSuperClass.asSeenFrom(currentClass.thisType, superClass)

wrapWithContextualJSClassValue(jsClassTypeAsSeenFromThis) {
tree
}
}

private def wrapWithContextualJSClassValue(jsClassType: Type)(
tree: Tree): Tree = {
wrapWithContextualJSClassValue(genJSConstructorOf(tree, jsClassType)) {
Expand Down
Expand Up @@ -649,31 +649,39 @@ class NestedJSClassTest {
}

@Test
def extendInnerJSClassInClass_Issue4402(): Unit = {
def extendInnerJSClassInClass_Issue4402_Issue4801(): Unit = {
val msg = "hello world"

val outer = js.Dynamic.literal(
InnerClass = js.constructorOf[DynamicInnerClass_Issue4402]
).asInstanceOf[OuterNativeClass_Issue4402]

class Subclass(arg: String) extends outer.InnerClass(arg)
class Subclass(arg: String) extends outer.InnerClass(arg) {
override def methodSuper_Issue4801(x: Int): String =
super.methodSuper_Issue4801(x) + " overridden"
}

val obj = new Subclass(msg)
assertEquals(msg, obj.message)
assertEquals(msg + "3 overridden", obj.methodSuper_Issue4801(3))
}

@Test
def extendInnerJSClassInTrait_Issue4402(): Unit = {
def extendInnerJSClassInTrait_Issue4402_Issue4801(): Unit = {
val msg = "hello world"

val outer = js.Dynamic.literal(
InnerClass = js.constructorOf[DynamicInnerClass_Issue4402]
).asInstanceOf[OuterNativeTrait_Issue4402]

class Subclass(arg: String) extends outer.InnerClass(arg)
class Subclass(arg: String) extends outer.InnerClass(arg) {
override def methodSuper_Issue4801(x: Int): String =
super.methodSuper_Issue4801(x) + " overridden"
}

val obj = new Subclass(msg)
assertEquals(msg, obj.message)
assertEquals(msg + "3 overridden", obj.methodSuper_Issue4801(3))
}
}

Expand Down Expand Up @@ -900,6 +908,8 @@ object NestedJSClassTest {

class DynamicInnerClass_Issue4402(arg: String) extends js.Object {
val message: String = arg

def methodSuper_Issue4801(x: Int): String = arg + x
}

@js.native
Expand All @@ -908,6 +918,8 @@ object NestedJSClassTest {
@js.native
class InnerClass(arg: String) extends js.Object {
def message: String = js.native

def methodSuper_Issue4801(x: Int): String = js.native
}
}

Expand All @@ -916,6 +928,8 @@ object NestedJSClassTest {
@js.native
class InnerClass(arg: String) extends js.Object {
def message: String = js.native

def methodSuper_Issue4801(x: Int): String = js.native
}
}
}
Expand Down

0 comments on commit c1050f0

Please sign in to comment.