2021-10-20 20:16:26 +00:00
|
|
|
package prog8tests
|
|
|
|
|
2021-11-07 23:16:58 +00:00
|
|
|
import io.kotest.assertions.withClue
|
|
|
|
import io.kotest.core.spec.style.FunSpec
|
|
|
|
import io.kotest.matchers.maps.shouldContainKey
|
|
|
|
import io.kotest.matchers.maps.shouldNotContainKey
|
|
|
|
import io.kotest.matchers.shouldBe
|
2021-10-20 20:16:26 +00:00
|
|
|
import prog8.ast.statements.Block
|
|
|
|
import prog8.ast.statements.Subroutine
|
2021-12-28 13:23:36 +00:00
|
|
|
import prog8.codegen.target.C64Target
|
2021-10-29 03:00:30 +00:00
|
|
|
import prog8.compilerinterface.CallGraph
|
2021-10-20 20:16:26 +00:00
|
|
|
import prog8tests.helpers.assertSuccess
|
|
|
|
import prog8tests.helpers.compileText
|
|
|
|
|
2021-11-07 23:16:58 +00:00
|
|
|
class TestCallgraph: FunSpec({
|
|
|
|
test("testGraphForEmptySubs") {
|
2021-10-20 20:16:26 +00:00
|
|
|
val sourcecode = """
|
|
|
|
%import string
|
|
|
|
main {
|
|
|
|
sub start() {
|
|
|
|
}
|
|
|
|
sub empty() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
val result = compileText(C64Target, false, sourcecode).assertSuccess()
|
2021-10-29 22:25:34 +00:00
|
|
|
val graph = CallGraph(result.program)
|
2021-10-20 20:16:26 +00:00
|
|
|
|
2021-11-07 23:16:58 +00:00
|
|
|
graph.imports.size shouldBe 1
|
|
|
|
graph.importedBy.size shouldBe 1
|
2021-10-29 22:25:34 +00:00
|
|
|
val toplevelModule = result.program.toplevelModule
|
2021-10-20 20:16:26 +00:00
|
|
|
val importedModule = graph.imports.getValue(toplevelModule).single()
|
2021-11-07 23:16:58 +00:00
|
|
|
importedModule.name shouldBe "string"
|
2021-10-20 20:16:26 +00:00
|
|
|
val importedBy = graph.importedBy.getValue(importedModule).single()
|
2021-11-07 23:16:58 +00:00
|
|
|
importedBy.name.startsWith("on_the_fly_test") shouldBe true
|
2021-10-20 20:16:26 +00:00
|
|
|
|
2021-11-07 23:16:58 +00:00
|
|
|
graph.unused(toplevelModule) shouldBe false
|
|
|
|
graph.unused(importedModule) shouldBe false
|
2021-10-20 20:16:26 +00:00
|
|
|
|
|
|
|
val mainBlock = toplevelModule.statements.filterIsInstance<Block>().single()
|
|
|
|
for(stmt in mainBlock.statements) {
|
|
|
|
val sub = stmt as Subroutine
|
2021-11-07 23:16:58 +00:00
|
|
|
graph.calls shouldNotContainKey sub
|
|
|
|
graph.calledBy shouldNotContainKey sub
|
2021-10-20 20:16:26 +00:00
|
|
|
|
2021-10-29 22:25:34 +00:00
|
|
|
if(sub === result.program.entrypoint)
|
2021-11-07 23:16:58 +00:00
|
|
|
withClue("start() should always be marked as used to avoid having it removed") {
|
|
|
|
graph.unused(sub) shouldBe false
|
|
|
|
}
|
2021-10-20 20:16:26 +00:00
|
|
|
else
|
2021-11-07 23:16:58 +00:00
|
|
|
graph.unused(sub) shouldBe true
|
2021-10-20 20:16:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-07 23:16:58 +00:00
|
|
|
test("testGraphForEmptyButReferencedSub") {
|
2021-10-20 20:16:26 +00:00
|
|
|
val sourcecode = """
|
|
|
|
%import string
|
|
|
|
main {
|
|
|
|
sub start() {
|
|
|
|
uword xx = &empty
|
|
|
|
xx++
|
|
|
|
}
|
|
|
|
sub empty() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
val result = compileText(C64Target, false, sourcecode).assertSuccess()
|
2021-10-29 22:25:34 +00:00
|
|
|
val graph = CallGraph(result.program)
|
2021-10-20 20:16:26 +00:00
|
|
|
|
2021-11-07 23:16:58 +00:00
|
|
|
graph.imports.size shouldBe 1
|
|
|
|
graph.importedBy.size shouldBe 1
|
2021-10-29 22:25:34 +00:00
|
|
|
val toplevelModule = result.program.toplevelModule
|
2021-10-20 20:16:26 +00:00
|
|
|
val importedModule = graph.imports.getValue(toplevelModule).single()
|
2021-11-07 23:16:58 +00:00
|
|
|
importedModule.name shouldBe "string"
|
2021-10-20 20:16:26 +00:00
|
|
|
val importedBy = graph.importedBy.getValue(importedModule).single()
|
2021-11-07 23:16:58 +00:00
|
|
|
importedBy.name.startsWith("on_the_fly_test") shouldBe true
|
2021-10-20 20:16:26 +00:00
|
|
|
|
2021-11-07 23:16:58 +00:00
|
|
|
graph.unused(toplevelModule) shouldBe false
|
|
|
|
graph.unused(importedModule) shouldBe false
|
2021-10-20 20:16:26 +00:00
|
|
|
|
|
|
|
val mainBlock = toplevelModule.statements.filterIsInstance<Block>().single()
|
|
|
|
val startSub = mainBlock.statements.filterIsInstance<Subroutine>().single{it.name=="start"}
|
|
|
|
val emptySub = mainBlock.statements.filterIsInstance<Subroutine>().single{it.name=="empty"}
|
|
|
|
|
2021-11-07 23:16:58 +00:00
|
|
|
withClue("start 'calls' (references) empty") {
|
|
|
|
graph.calls shouldContainKey startSub
|
|
|
|
}
|
|
|
|
withClue("empty doesn't call anything") {
|
|
|
|
graph.calls shouldNotContainKey emptySub
|
|
|
|
}
|
|
|
|
withClue("empty gets 'called'") {
|
|
|
|
graph.calledBy shouldContainKey emptySub
|
|
|
|
}
|
|
|
|
withClue( "start doesn't get called (except as entrypoint ofc.)") {
|
|
|
|
graph.calledBy shouldNotContainKey startSub
|
|
|
|
}
|
2021-10-20 20:16:26 +00:00
|
|
|
}
|
2021-11-07 23:16:58 +00:00
|
|
|
})
|