Skip to content

Commit 1972c0e

Browse files
authored
Merge pull request #4895 from gzm0/no-override-abstract
Analyzer: Do not override abstract methods with missing methods
2 parents a26e215 + f365f70 commit 1972c0e

File tree

2 files changed

+59
-11
lines changed

2 files changed

+59
-11
lines changed

linker/shared/src/main/scala/org/scalajs/linker/analyzer/Analyzer.scala

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -657,10 +657,14 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean,
657657
}
658658

659659
private def createNonExistentPublicMethod(methodName: MethodName): MethodInfo = {
660-
val syntheticData = makeSyntheticMethodInfo(methodName, MemberNamespace.Public)
661-
val m = new MethodInfo(this, syntheticData, nonExistent = true)
662-
publicMethodInfos += methodName -> m
663-
m
660+
/* Use getOrElseUpdate to avoid overriding an abstract method:
661+
* When being called from lookupMethod, it is possible that an abstract
662+
* method exists.
663+
*/
664+
publicMethodInfos.getOrElseUpdate(methodName, {
665+
val syntheticData = makeSyntheticMethodInfo(methodName, MemberNamespace.Public)
666+
new MethodInfo(this, syntheticData, nonExistent = true)
667+
})
664668
}
665669

666670
def tryLookupMethod(methodName: MethodName): Option[MethodInfo] = {
@@ -959,6 +963,7 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean,
959963

960964
def tryLookupStaticLikeMethod(namespace: MemberNamespace,
961965
methodName: MethodName): Option[MethodInfo] = {
966+
assert(namespace != MemberNamespace.Public)
962967
methodInfos(namespace).get(methodName)
963968
}
964969

@@ -1246,10 +1251,7 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean,
12461251
s"$owner.${methodName.simpleName.nameString}"
12471252

12481253
def reachStatic()(implicit from: From): Unit = {
1249-
assert(!isAbstract,
1250-
s"Trying to reach statically the abstract method $this")
1251-
1252-
checkExistent()
1254+
checkConcrete()
12531255

12541256
calledFrom ::= from
12551257
if (!isReachable) {
@@ -1272,14 +1274,12 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean,
12721274
def reach(inClass: ClassInfo)(implicit from: From): Unit = {
12731275
assert(!namespace.isStatic,
12741276
s"Trying to dynamically reach the static method $this")
1275-
assert(!isAbstract,
1276-
s"Trying to dynamically reach the abstract method $this")
12771277
assert(owner.isAnyClass,
12781278
s"Trying to dynamically reach the non-class method $this")
12791279
assert(!namespace.isConstructor,
12801280
s"Trying to dynamically reach the constructor $this")
12811281

1282-
checkExistent()
1282+
checkConcrete()
12831283

12841284
calledFrom ::= from
12851285
instantiatedSubclasses ::= inClass
@@ -1296,6 +1296,11 @@ final class Analyzer(config: CommonPhaseConfig, initial: Boolean,
12961296
_errors += MissingMethod(this, from)
12971297
}
12981298

1299+
private def checkConcrete()(implicit from: From) = {
1300+
if (nonExistent || isAbstract)
1301+
_errors += MissingMethod(this, from)
1302+
}
1303+
12991304
private[this] def doReach(): Unit = {
13001305
followReachabilityInfo(data.reachabilityInfo, owner.staticDependencies,
13011306
owner.externalDependencies, owner.dynamicDependencies)(FromMethod(this))

linker/shared/src/test/scala/org/scalajs/linker/AnalyzerTest.scala

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,49 @@ class AnalyzerTest {
364364
}
365365
}
366366

367+
@Test
368+
def callAbstractMethod(): AsyncResult = await {
369+
val fooMethodName = m("foo", Nil, IntRef)
370+
371+
val classDefs = Seq(
372+
classDef("A", superClass = Some(ObjectClass),
373+
methods = List(
374+
trivialCtor("A"),
375+
MethodDef(EMF, fooMethodName, NON, Nil, IntType, None)(EOH, UNV)
376+
)
377+
)
378+
)
379+
380+
val analysis = computeAnalysis(classDefs,
381+
reqsFactory.instantiateClass("A", NoArgConstructorName) ++
382+
reqsFactory.callMethod("A", fooMethodName))
383+
384+
assertContainsError("MissingMethod(A.foo;I)", analysis) {
385+
case MissingMethod(MethInfo("A", "foo;I"), FromDispatch(ClsInfo("A"),`fooMethodName`)) => true
386+
}
387+
}
388+
389+
@Test
390+
def staticCallAbstractMethod(): AsyncResult = await {
391+
val fooMethodName = m("foo", Nil, IntRef)
392+
393+
val classDefs = Seq(
394+
classDef("A", superClass = Some(ObjectClass),
395+
methods = List(
396+
trivialCtor("A"),
397+
MethodDef(EMF, fooMethodName, NON, Nil, IntType, None)(EOH, UNV)
398+
)
399+
)
400+
)
401+
402+
val analysis = computeAnalysis(classDefs,
403+
reqsFactory.callMethodStatically("A", fooMethodName))
404+
405+
assertContainsError("MissingMethod(A.foo;I)", analysis) {
406+
case MissingMethod(MethInfo("A", "foo;I"), `fromUnitTest`) => true
407+
}
408+
}
409+
367410
@Test
368411
def missingJSNativeMember(): AsyncResult = await {
369412
val mainName = m("main", Nil, V)

0 commit comments

Comments
 (0)