Skip to content

Commit db67c68

Browse files
committed
Scala.js: Correctly identify default accessors of native JS def params.
Previously, we tested whether the default accessor itself had the `@js.native` annotation. That is however never the case. Instead, we now test whether the associated method has the annotation. We still emit the *definitions* of those default accessors, even though that they are never called anymore, because of backward binary compatibility. If we stopped emitting them, a library compiled with a newer Scala.js might not link anymore against an application built with a previous Scala.js. This is a forward port of scala-js/scala-js@7e998b4
1 parent dba8f43 commit db67c68

File tree

2 files changed

+66
-21
lines changed

2 files changed

+66
-21
lines changed

compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2007,8 +2007,46 @@ class JSCodeGen()(using genCtx: Context) {
20072007
val args = tree.args
20082008
val sym = tree.fun.symbol
20092009

2010+
/* Is the method a JS default accessor, which should become an
2011+
* `UndefinedParam` rather than being compiled normally.
2012+
*
2013+
* This is true iff one of the following conditions apply:
2014+
* - It is a constructor default param for the constructor of a JS class.
2015+
* - It is a default param of an instance method of a native JS type.
2016+
* - It is a default param of an instance method of a non-native JS type
2017+
* and the attached method is exposed.
2018+
* - It is a default param for a native JS def.
2019+
*/
2020+
def isJSDefaultParam: Boolean = {
2021+
sym.name.is(DefaultGetterName) && {
2022+
val info = new DefaultParamInfo(sym)
2023+
if (info.isForConstructor) {
2024+
/* This is a default accessor for a constructor parameter. Check
2025+
* whether the attached constructor is a JS constructor, which is
2026+
* the case iff the linked class is a JS type.
2027+
*/
2028+
info.constructorOwner.isJSType
2029+
} else {
2030+
if (sym.owner.isJSType) {
2031+
/* The default accessor is in a JS type. It is a JS default
2032+
* param iff the enclosing class is native or the attached method
2033+
* is exposed.
2034+
*/
2035+
!sym.owner.isNonNativeJSClass || info.attachedMethod.isJSExposed
2036+
} else {
2037+
/* The default accessor is in a Scala type. It is a JS default
2038+
* param iff the attached method is a native JS def. This can
2039+
* only happen if the owner is a module class, which we test
2040+
* first as a fast way out.
2041+
*/
2042+
sym.owner.is(ModuleClass) && info.attachedMethod.hasAnnotation(jsdefn.JSNativeAnnot)
2043+
}
2044+
}
2045+
}
2046+
}
2047+
20102048
tree.fun match {
2011-
case _ if sym.isJSDefaultParam =>
2049+
case _ if isJSDefaultParam =>
20122050
js.Transient(UndefinedParam)
20132051

20142052
case Select(Super(_, _), _) =>
@@ -4578,4 +4616,31 @@ object JSCodeGen {
45784616
out.print("<undefined-param>")
45794617
}
45804618

4619+
/** Info about a default param accessor.
4620+
*
4621+
* The method must have a default getter name for this class to make sense.
4622+
*/
4623+
private class DefaultParamInfo(sym: Symbol)(using Context) {
4624+
private val methodName = sym.name.exclude(DefaultGetterName)
4625+
4626+
def isForConstructor: Boolean = methodName == nme.CONSTRUCTOR
4627+
4628+
/** When `isForConstructor` is true, returns the owner of the attached
4629+
* constructor.
4630+
*/
4631+
def constructorOwner: Symbol = sym.owner.linkedClass
4632+
4633+
/** When `isForConstructor` is false, returns the method attached to the
4634+
* specified default accessor.
4635+
*/
4636+
def attachedMethod: Symbol = {
4637+
// If there are overloads, we need to find the one that has default params.
4638+
val overloads = sym.owner.info.decl(methodName)
4639+
if (!overloads.isOverloaded)
4640+
overloads.symbol
4641+
else
4642+
overloads.suchThat(_.is(HasDefaultParams)).symbol
4643+
}
4644+
}
4645+
45814646
}

compiler/src/dotty/tools/dotc/transform/sjs/JSSymUtils.scala

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -156,26 +156,6 @@ object JSSymUtils {
156156
def isJSBracketCall(using Context): Boolean =
157157
sym.hasAnnotation(jsdefn.JSBracketCallAnnot)
158158

159-
/** Is this symbol a default param accessor for a JS method?
160-
*
161-
* For default param accessors of *constructors*, we need to test whether
162-
* the companion *class* of the owner is a JS type; not whether the owner
163-
* is a JS type.
164-
*/
165-
def isJSDefaultParam(using Context): Boolean = {
166-
sym.name.is(DefaultGetterName) && {
167-
val owner = sym.owner
168-
val methName = sym.name.exclude(DefaultGetterName)
169-
if (methName == nme.CONSTRUCTOR) {
170-
owner.linkedClass.isJSType
171-
} else {
172-
def isAttachedMethodExposed: Boolean =
173-
owner.info.decl(methName).hasAltWith(_.symbol.isJSExposed)
174-
owner.isJSType && (!owner.isNonNativeJSClass || isAttachedMethodExposed)
175-
}
176-
}
177-
}
178-
179159
/** Is this symbol a default param accessor for the constructor of a native JS class? */
180160
def isJSNativeCtorDefaultParam(using Context): Boolean = {
181161
sym.name.is(DefaultGetterName)

0 commit comments

Comments
 (0)