@@ -42,7 +42,6 @@ import org.scalajs.ir.Trees.OptimizerHints
42
42
import dotty .tools .dotc .transform .sjs .JSSymUtils ._
43
43
44
44
import JSEncoding ._
45
- import JSInterop ._
46
45
import ScopedVar .withScopedVars
47
46
48
47
/** Main codegen for Scala.js IR.
@@ -526,7 +525,7 @@ class JSCodeGen()(using genCtx: Context) {
526
525
/* We add symbols that we have to expose here. This way we also
527
526
* get inherited stuff that is implemented in this class.
528
527
*/
529
- dispatchMethodNames += jsNameOf( sym)
528
+ dispatchMethodNames += sym.jsName
530
529
}
531
530
}
532
531
@@ -790,7 +789,21 @@ class JSCodeGen()(using genCtx: Context) {
790
789
791
790
def isExcluded (m : Symbol ): Boolean = {
792
791
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
794
807
}
795
808
796
809
val forwarders = for {
@@ -1403,6 +1416,53 @@ class JSCodeGen()(using genCtx: Context) {
1403
1416
val vparamss = dd.termParamss
1404
1417
val rhs = dd.rhs
1405
1418
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
+
1406
1466
withPerMethodBodyState(sym) {
1407
1467
assert(vparamss.isEmpty || vparamss.tail.isEmpty,
1408
1468
" Malformed parameter list: " + vparamss)
@@ -1422,7 +1482,7 @@ class JSCodeGen()(using genCtx: Context) {
1422
1482
Some (js.MethodDef (js.MemberFlags .empty, methodName, originalName,
1423
1483
jsParams, toIRType(patchedResultType(sym)), None )(
1424
1484
OptimizerHints .empty, None ))
1425
- } else if (sym.isJSNativeCtorDefaultParam ) {
1485
+ } else if (isIgnorableDefaultParam ) {
1426
1486
// #11592
1427
1487
None
1428
1488
} else if (sym.is(Bridge ) && sym.name.is(DefaultGetterName ) && currentClassSym.isNonNativeJSClass) {
@@ -2008,8 +2068,52 @@ class JSCodeGen()(using genCtx: Context) {
2008
2068
val args = tree.args
2009
2069
val sym = tree.fun.symbol
2010
2070
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
+
2011
2115
tree.fun match {
2012
- case _ if sym. isJSDefaultParam =>
2116
+ case _ if isJSDefaultParam =>
2013
2117
js.Transient (UndefinedParam )
2014
2118
2015
2119
case Select (Super (_, _), _) =>
@@ -4579,4 +4683,31 @@ object JSCodeGen {
4579
4683
out.print(" <undefined-param>" )
4580
4684
}
4581
4685
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
+
4582
4713
}
0 commit comments