@@ -1747,9 +1747,37 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
1747
1747
1748
1748
// Generate a method -------------------------------------------------------
1749
1749
1750
+ /** Maybe gen JS code for a method definition.
1751
+ *
1752
+ * Some methods are not emitted at all:
1753
+ *
1754
+ * - Primitives, since they are never actually called (with exceptions)
1755
+ * - Abstract methods in non-native JS classes
1756
+ * - Default accessor of a native JS constructor
1757
+ * - Constructors of hijacked classes
1758
+ * - Methods with the {{{@JavaDefaultMethod}}} annotation mixed in classes.
1759
+ */
1750
1760
def genMethod (dd : DefDef ): Option [js.MethodDef ] = {
1751
- withNewLocalNameScope {
1752
- genMethodWithCurrentLocalNameScope(dd)
1761
+ val sym = dd.symbol
1762
+ val isAbstract = isAbstractMethod(dd)
1763
+
1764
+ if (scalaPrimitives.isPrimitive(sym)) {
1765
+ None
1766
+ } else if (isAbstract && isNonNativeJSClass(currentClassSym)) {
1767
+ // #4409: Do not emit abstract methods in non-native JS classes
1768
+ None
1769
+ } else if (isJSNativeCtorDefaultParam(sym)) {
1770
+ None
1771
+ } else if (sym.isClassConstructor && isHijackedClass(sym.owner)) {
1772
+ None
1773
+ } else if (scalaUsesImplClasses && ! isImplClass(sym.owner) &&
1774
+ ! isAbstract && sym.hasAnnotation(JavaDefaultMethodAnnotation )) {
1775
+ // Do not emit trait impl forwarders with @JavaDefaultMethod
1776
+ None
1777
+ } else {
1778
+ withNewLocalNameScope {
1779
+ Some (genMethodWithCurrentLocalNameScope(dd))
1780
+ }
1753
1781
}
1754
1782
}
1755
1783
@@ -1758,42 +1786,30 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
1758
1786
* of the Scala method, as described in `JSEncoding`, to support
1759
1787
* overloading.
1760
1788
*
1761
- * Some methods are not emitted at all:
1762
- * * Primitives, since they are never actually called (with exceptions)
1763
- * * Abstract methods
1764
- * * Constructors of hijacked classes
1765
- * * Methods with the {{{@JavaDefaultMethod}}} annotation mixed in classes.
1766
- *
1767
1789
* Constructors are emitted by generating their body as a statement.
1768
1790
*
1769
1791
* Interface methods with the {{{@JavaDefaultMethod}}} annotation produce
1770
1792
* default methods forwarding to the trait impl class method.
1771
1793
*
1772
1794
* Other (normal) methods are emitted with `genMethodDef()`.
1773
1795
*/
1774
- def genMethodWithCurrentLocalNameScope (dd : DefDef ): Option [ js.MethodDef ] = {
1796
+ def genMethodWithCurrentLocalNameScope (dd : DefDef ): js.MethodDef = {
1775
1797
implicit val pos = dd.pos
1776
- val DefDef (mods, name, _, vparamss, _, rhs) = dd
1777
1798
val sym = dd.symbol
1778
1799
1779
1800
withPerMethodBodyState(sym) {
1780
- assert(vparamss.isEmpty || vparamss.tail.isEmpty,
1781
- " Malformed parameter list: " + vparamss)
1782
- val params = if (vparamss.isEmpty) Nil else vparamss.head map (_.symbol)
1783
-
1784
1801
val methodName = encodeMethodSym(sym)
1785
1802
val originalName = originalNameOfMethod(sym)
1786
1803
1787
- val isAbstract = isAbstractMethod(dd)
1788
-
1789
- def jsParams = params.map(genParamDef(_))
1804
+ val jsParams = {
1805
+ val vparamss = dd.vparamss
1806
+ assert(vparamss.isEmpty || vparamss.tail.isEmpty,
1807
+ " Malformed parameter list: " + vparamss)
1808
+ val params = if (vparamss.isEmpty) Nil else vparamss.head.map(_.symbol)
1809
+ params.map(genParamDef(_))
1810
+ }
1790
1811
1791
- if (scalaPrimitives.isPrimitive(sym)) {
1792
- None
1793
- } else if (isAbstract && isNonNativeJSClass(currentClassSym)) {
1794
- // #4409: Do not emit abstract methods in non-native JS classes
1795
- None
1796
- } else if (isAbstract) {
1812
+ val jsMethodDef = if (isAbstractMethod(dd)) {
1797
1813
val body = if (scalaUsesImplClasses &&
1798
1814
sym.hasAnnotation(JavaDefaultMethodAnnotation )) {
1799
1815
/* For an interface method with @JavaDefaultMethod, make it a
@@ -1814,17 +1830,9 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
1814
1830
} else {
1815
1831
None
1816
1832
}
1817
- Some ( js.MethodDef (js.MemberFlags .empty, methodName, originalName,
1833
+ js.MethodDef (js.MemberFlags .empty, methodName, originalName,
1818
1834
jsParams, toIRType(sym.tpe.resultType), body)(
1819
- OptimizerHints .empty, None ))
1820
- } else if (isJSNativeCtorDefaultParam(sym)) {
1821
- None
1822
- } else if (sym.isClassConstructor && isHijackedClass(sym.owner)) {
1823
- None
1824
- } else if (scalaUsesImplClasses && ! isImplClass(sym.owner) &&
1825
- sym.hasAnnotation(JavaDefaultMethodAnnotation )) {
1826
- // Do not emit trait impl forwarders with @JavaDefaultMethod
1827
- None
1835
+ OptimizerHints .empty, None )
1828
1836
} else {
1829
1837
def isTraitImplForwarder = dd.rhs match {
1830
1838
case app : Apply => isImplClass(app.symbol.owner)
@@ -1853,7 +1861,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
1853
1861
val namespace = js.MemberNamespace .Constructor
1854
1862
js.MethodDef (
1855
1863
js.MemberFlags .empty.withNamespace(namespace), methodName,
1856
- originalName, jsParams, jstpe.NoType , Some (genStat(rhs)))(
1864
+ originalName, jsParams, jstpe.NoType , Some (genStat(dd. rhs)))(
1857
1865
optimizerHints, None )
1858
1866
} else {
1859
1867
val resultIRType = toIRType(sym.tpe.resultType)
@@ -1866,8 +1874,8 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
1866
1874
else js.MemberNamespace .Public
1867
1875
}
1868
1876
}
1869
- genMethodDef(namespace, methodName, originalName, params ,
1870
- resultIRType, rhs, optimizerHints)
1877
+ genMethodDef(namespace, methodName, originalName, jsParams ,
1878
+ resultIRType, dd. rhs, optimizerHints)
1871
1879
}
1872
1880
}
1873
1881
@@ -1889,7 +1897,25 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
1889
1897
}
1890
1898
}
1891
1899
1892
- Some (methodDefWithoutUselessVars)
1900
+ methodDefWithoutUselessVars
1901
+ }
1902
+
1903
+ /* #3953 Patch the param defs to have the type advertised by the method's type.
1904
+ * This works around https://github.com/scala/bug/issues/11884, whose fix
1905
+ * upstream is blocked because it is not binary compatible. The fix here
1906
+ * only affects the inside of the js.MethodDef, so it is binary compat.
1907
+ */
1908
+ val paramTypeRewrites = jsParams.zip(sym.tpe.paramTypes.map(toIRType(_))).collect {
1909
+ case (js.ParamDef (name, _, tpe, _), sigType) if tpe != sigType => name.name -> sigType
1910
+ }
1911
+ if (paramTypeRewrites.isEmpty) {
1912
+ // Overwhelmingly common case: all the types match, so there is nothing to do
1913
+ jsMethodDef
1914
+ } else {
1915
+ devWarning(
1916
+ " Working around https://github.com/scala/bug/issues/11884 " +
1917
+ s " for ${sym.fullName} at ${sym.pos}" )
1918
+ patchTypeOfParamDefs(jsMethodDef, paramTypeRewrites.toMap)
1893
1919
}
1894
1920
}
1895
1921
}
@@ -1950,6 +1976,42 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
1950
1976
newBody)(methodDef.optimizerHints, None )(methodDef.pos)
1951
1977
}
1952
1978
1979
+ /** Patches the type of selected param defs in a [[js.MethodDef ]].
1980
+ *
1981
+ * @param patches
1982
+ * Map from local name to new type. For param defs not in the map, the
1983
+ * type is untouched.
1984
+ */
1985
+ private def patchTypeOfParamDefs (methodDef : js.MethodDef ,
1986
+ patches : Map [LocalName , jstpe.Type ]): js.MethodDef = {
1987
+
1988
+ def newType (name : js.LocalIdent , oldType : jstpe.Type ): jstpe.Type =
1989
+ patches.getOrElse(name.name, oldType)
1990
+
1991
+ val js .MethodDef (flags, methodName, originalName, params, resultType, body) =
1992
+ methodDef
1993
+ val newParams = for {
1994
+ p @ js.ParamDef (name, originalName, ptpe, mutable) <- params
1995
+ } yield {
1996
+ js.ParamDef (name, originalName, newType(name, ptpe), mutable)(p.pos)
1997
+ }
1998
+ val transformer = new ir.Transformers .Transformer {
1999
+ override def transform (tree : js.Tree , isStat : Boolean ): js.Tree = tree match {
2000
+ case tree @ js.VarRef (name) =>
2001
+ js.VarRef (name)(newType(name, tree.tpe))(tree.pos)
2002
+ case js.Closure (arrow, captureParams, params, restParam, body, captureValues) =>
2003
+ js.Closure (arrow, captureParams, params, restParam, body,
2004
+ captureValues.map(transformExpr))(tree.pos)
2005
+ case _ =>
2006
+ super .transform(tree, isStat)
2007
+ }
2008
+ }
2009
+ val newBody = body.map(
2010
+ b => transformer.transform(b, isStat = resultType == jstpe.NoType ))
2011
+ js.MethodDef (flags, methodName, originalName, newParams, resultType,
2012
+ newBody)(methodDef.optimizerHints, None )(methodDef.pos)
2013
+ }
2014
+
1953
2015
/** Generates the JSNativeMemberDef of a JS native method. */
1954
2016
def genJSNativeMemberDef (tree : DefDef ): js.JSNativeMemberDef = {
1955
2017
implicit val pos = tree.pos
@@ -1972,13 +2034,11 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
1972
2034
* `this`.
1973
2035
*/
1974
2036
def genMethodDef (namespace : js.MemberNamespace , methodName : js.MethodIdent ,
1975
- originalName : OriginalName , paramsSyms : List [Symbol ],
2037
+ originalName : OriginalName , jsParams : List [js. ParamDef ],
1976
2038
resultIRType : jstpe.Type , tree : Tree ,
1977
2039
optimizerHints : OptimizerHints ): js.MethodDef = {
1978
2040
implicit val pos = tree.pos
1979
2041
1980
- val jsParams = paramsSyms.map(genParamDef(_))
1981
-
1982
2042
val bodyIsStat = resultIRType == jstpe.NoType
1983
2043
1984
2044
def genBodyWithinReturnableScope (): js.Tree = tree match {
@@ -5823,8 +5883,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
5823
5883
tryingToGenMethodAsJSFunction := true
5824
5884
) {
5825
5885
try {
5826
- genMethodWithCurrentLocalNameScope(applyDef).getOrElse(
5827
- abort(s " Oops, $applyDef did not produce a method " ))
5886
+ genMethodWithCurrentLocalNameScope(applyDef)
5828
5887
} catch {
5829
5888
case e : CancelGenMethodAsJSFunction =>
5830
5889
fail(e.getMessage)
0 commit comments