@@ -42,7 +42,6 @@ import org.scalajs.ir.Trees.OptimizerHints
4242import dotty .tools .dotc .transform .sjs .JSSymUtils ._
4343
4444import JSEncoding ._
45- import JSInterop ._
4645import ScopedVar .withScopedVars
4746
4847/** Main codegen for Scala.js IR.
@@ -526,7 +525,7 @@ class JSCodeGen()(using genCtx: Context) {
526525 /* We add symbols that we have to expose here. This way we also
527526 * get inherited stuff that is implemented in this class.
528527 */
529- dispatchMethodNames += jsNameOf( sym)
528+ dispatchMethodNames += sym.jsName
530529 }
531530 }
532531
@@ -790,7 +789,21 @@ class JSCodeGen()(using genCtx: Context) {
790789
791790 def isExcluded (m : Symbol ): Boolean = {
792791 def hasAccessBoundary = m.accessBoundary(defn.RootClass ) ne defn.RootClass
793- m.is(Deferred ) || m.isConstructor || hasAccessBoundary || (m.owner eq defn.ObjectClass )
792+
793+ def isOfJLObject : Boolean = m.owner eq defn.ObjectClass
794+
795+ def isDefaultParamOfJSNativeDef : Boolean = {
796+ m.name.is(DefaultGetterName ) && {
797+ val info = new DefaultParamInfo (m)
798+ ! info.isForConstructor && info.attachedMethod.hasAnnotation(jsdefn.JSNativeAnnot )
799+ }
800+ }
801+
802+ m.is(Deferred )
803+ || m.isConstructor
804+ || hasAccessBoundary
805+ || isOfJLObject
806+ || m.hasAnnotation(jsdefn.JSNativeAnnot ) || isDefaultParamOfJSNativeDef // #4557
794807 }
795808
796809 val forwarders = for {
@@ -1403,6 +1416,53 @@ class JSCodeGen()(using genCtx: Context) {
14031416 val vparamss = dd.termParamss
14041417 val rhs = dd.rhs
14051418
1419+ /* Is this method a default accessor that should be ignored?
1420+ *
1421+ * This is the case iff one of the following applies:
1422+ * - It is a constructor default accessor and the linked class is a
1423+ * native JS class.
1424+ * - It is a default accessor for a native JS def, but with the caveat
1425+ * that its rhs must be `js.native` because of #4553.
1426+ *
1427+ * Both of those conditions can only happen if the default accessor is in
1428+ * a module class, so we use that as a fast way out. (But omitting that
1429+ * condition would not change the result.)
1430+ *
1431+ * This is different than `isJSDefaultParam` in `genApply`: we do not
1432+ * ignore default accessors of *non-native* JS types. Neither for
1433+ * constructor default accessor nor regular default accessors. We also
1434+ * do not need to worry about non-constructor members of native JS types,
1435+ * since for those, the entire member list is ignored in `genJSClassData`.
1436+ */
1437+ def isIgnorableDefaultParam : Boolean = {
1438+ sym.name.is(DefaultGetterName ) && sym.owner.is(ModuleClass ) && {
1439+ val info = new DefaultParamInfo (sym)
1440+ if (info.isForConstructor) {
1441+ /* This is a default accessor for a constructor parameter. Check
1442+ * whether the attached constructor is a native JS constructor,
1443+ * which is the case iff the linked class is a native JS type.
1444+ */
1445+ info.constructorOwner.hasAnnotation(jsdefn.JSNativeAnnot )
1446+ } else {
1447+ /* #4553 We need to ignore default accessors for JS native defs.
1448+ * However, because Scala.js <= 1.7.0 actually emitted code calling
1449+ * those accessors, we must keep default accessors that would
1450+ * compile. The only accessors we can actually get rid of are those
1451+ * that are `= js.native`.
1452+ */
1453+ ! sym.owner.isJSType &&
1454+ info.attachedMethod.hasAnnotation(jsdefn.JSNativeAnnot ) && {
1455+ dd.rhs match {
1456+ case MaybeAsInstanceOf (Apply (fun, _)) =>
1457+ fun.symbol == jsdefn.JSPackage_native
1458+ case _ =>
1459+ false
1460+ }
1461+ }
1462+ }
1463+ }
1464+ }
1465+
14061466 withPerMethodBodyState(sym) {
14071467 assert(vparamss.isEmpty || vparamss.tail.isEmpty,
14081468 " Malformed parameter list: " + vparamss)
@@ -1422,7 +1482,7 @@ class JSCodeGen()(using genCtx: Context) {
14221482 Some (js.MethodDef (js.MemberFlags .empty, methodName, originalName,
14231483 jsParams, toIRType(patchedResultType(sym)), None )(
14241484 OptimizerHints .empty, None ))
1425- } else if (sym.isJSNativeCtorDefaultParam ) {
1485+ } else if (isIgnorableDefaultParam ) {
14261486 // #11592
14271487 None
14281488 } else if (sym.is(Bridge ) && sym.name.is(DefaultGetterName ) && currentClassSym.isNonNativeJSClass) {
@@ -2008,8 +2068,52 @@ class JSCodeGen()(using genCtx: Context) {
20082068 val args = tree.args
20092069 val sym = tree.fun.symbol
20102070
2071+ /* Is the method a JS default accessor, which should become an
2072+ * `UndefinedParam` rather than being compiled normally.
2073+ *
2074+ * This is true iff one of the following conditions apply:
2075+ * - It is a constructor default param for the constructor of a JS class.
2076+ * - It is a default param of an instance method of a native JS type.
2077+ * - It is a default param of an instance method of a non-native JS type
2078+ * and the attached method is exposed.
2079+ * - It is a default param for a native JS def.
2080+ *
2081+ * This is different than `isIgnorableDefaultParam` in
2082+ * `genMethodWithCurrentLocalNameScope`: we include here the default
2083+ * accessors of *non-native* JS types (unless the corresponding methods are
2084+ * not exposed). We also need to handle non-constructor members of native
2085+ * JS types.
2086+ */
2087+ def isJSDefaultParam : Boolean = {
2088+ sym.name.is(DefaultGetterName ) && {
2089+ val info = new DefaultParamInfo (sym)
2090+ if (info.isForConstructor) {
2091+ /* This is a default accessor for a constructor parameter. Check
2092+ * whether the attached constructor is a JS constructor, which is
2093+ * the case iff the linked class is a JS type.
2094+ */
2095+ info.constructorOwner.isJSType
2096+ } else {
2097+ if (sym.owner.isJSType) {
2098+ /* The default accessor is in a JS type. It is a JS default
2099+ * param iff the enclosing class is native or the attached method
2100+ * is exposed.
2101+ */
2102+ ! sym.owner.isNonNativeJSClass || info.attachedMethod.isJSExposed
2103+ } else {
2104+ /* The default accessor is in a Scala type. It is a JS default
2105+ * param iff the attached method is a native JS def. This can
2106+ * only happen if the owner is a module class, which we test
2107+ * first as a fast way out.
2108+ */
2109+ sym.owner.is(ModuleClass ) && info.attachedMethod.hasAnnotation(jsdefn.JSNativeAnnot )
2110+ }
2111+ }
2112+ }
2113+ }
2114+
20112115 tree.fun match {
2012- case _ if sym. isJSDefaultParam =>
2116+ case _ if isJSDefaultParam =>
20132117 js.Transient (UndefinedParam )
20142118
20152119 case Select (Super (_, _), _) =>
@@ -4579,4 +4683,31 @@ object JSCodeGen {
45794683 out.print(" <undefined-param>" )
45804684 }
45814685
4686+ /** Info about a default param accessor.
4687+ *
4688+ * The method must have a default getter name for this class to make sense.
4689+ */
4690+ private class DefaultParamInfo (sym : Symbol )(using Context ) {
4691+ private val methodName = sym.name.exclude(DefaultGetterName )
4692+
4693+ def isForConstructor : Boolean = methodName == nme.CONSTRUCTOR
4694+
4695+ /** When `isForConstructor` is true, returns the owner of the attached
4696+ * constructor.
4697+ */
4698+ def constructorOwner : Symbol = sym.owner.linkedClass
4699+
4700+ /** When `isForConstructor` is false, returns the method attached to the
4701+ * specified default accessor.
4702+ */
4703+ def attachedMethod : Symbol = {
4704+ // If there are overloads, we need to find the one that has default params.
4705+ val overloads = sym.owner.info.decl(methodName)
4706+ if (! overloads.isOverloaded)
4707+ overloads.symbol
4708+ else
4709+ overloads.suchThat(_.is(HasDefaultParams )).symbol
4710+ }
4711+ }
4712+
45824713}
0 commit comments