Skip to content

Commit

Permalink
[WIP] add IR deserialization, instructions, instList
Browse files Browse the repository at this point in the history
  • Loading branch information
MForest7 committed Feb 1, 2024
1 parent 8083c50 commit 81f875e
Show file tree
Hide file tree
Showing 14 changed files with 1,759 additions and 0 deletions.
20 changes: 20 additions & 0 deletions jacodb-panda-static/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
plugins {
kotlin("plugin.serialization")
}

repositories {
mavenCentral()
}

dependencies {
api(project(":jacodb-core"))
api(project(":jacodb-api-jvm"))
api(project(":jacodb-api-core"))

implementation(Libs.kotlin_logging)
implementation(Libs.kotlinx_serialization_json)
}

tasks.test {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2022 UnitTestBot contributors (utbot.org)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jacodb.panda.staticvm

data class AccessFlags(val flags: Int) {
val isPublic: Boolean
get() = flags and 0x0001 > 0
val isPrivate: Boolean
get() = flags and 0x0002 > 0
val isProtected: Boolean
get() = flags and 0x0004 > 0
val isPackagePrivate: Boolean
get() = false
val isStatic: Boolean
get() = flags and 0x0008 > 0
val isFinal: Boolean
get() = flags and 0x0010 > 0
val isInterface: Boolean
get() = flags and 0x0200 > 0
val isAbstract: Boolean
get() = flags and 0x0400 > 0
val isSynthetic: Boolean
get() = flags and 0x1000 > 0
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright 2022 UnitTestBot contributors (utbot.org)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jacodb.panda.staticvm

data class SimpleDirectedGraph<T>(
val nodes: MutableSet<T> = mutableSetOf(),
private val predecessorsMap: MutableMap<T, MutableList<T>> = mutableMapOf(),
private val successorsMap: MutableMap<T, MutableList<T>> = mutableMapOf()
) {
fun predecessors(t: T): List<T> = predecessorsMap.getOrDefault(t, emptyList())

fun successors(t: T): List<T> = successorsMap.getOrDefault(t, emptyList())

fun withNode(node: T): SimpleDirectedGraph<T> {
nodes.add(node)
return this
}

fun withEdge(from: T, to: T): SimpleDirectedGraph<T> {
nodes.add(from)
nodes.add(to)
predecessorsMap.getOrPut(to, ::mutableListOf).add(from)
successorsMap.getOrPut(from, ::mutableListOf).add(to)
return this
}

fun induced(subset: Collection<T>) = SimpleDirectedGraph(
nodes.intersect(subset.toSet()).toMutableSet(),
predecessorsMap.mapNotNull { (key, value) -> if (subset.contains(key)) key to subset.intersect(value.toSet()).toMutableList() else null }
.toMap().toMutableMap(),
successorsMap.mapNotNull { (key, value) -> if (subset.contains(key)) key to subset.intersect(value.toSet()).toMutableList() else null }
.toMap().toMutableMap()
)
fun weaklyConnectedComponents(): List<SimpleDirectedGraph<T>> = components(nodes) {
predecessors(it) + successors(it)
}.map(::induced)

fun topsort(): List<T> {
val visited = hashSetOf<T>()
val order = mutableListOf<T>()
val onStack = hashSetOf<T>()

fun dfs(node: T) = node.takeIf(visited::add)?.also(onStack::add)?.let {
successors(it).forEach(::dfs)
order.add(node)
}

nodes.forEach { if (it !in visited) dfs(it) }
return order.reversed()
}

companion object {
fun <T> union(lhs: SimpleDirectedGraph<T>, rhs: SimpleDirectedGraph<T>) = SimpleDirectedGraph(
lhs.nodes.plus(rhs.nodes).toMutableSet(),
lhs.predecessorsMap.plus(rhs.predecessorsMap).toMutableMap(),
lhs.successorsMap.plus(rhs.successorsMap).toMutableMap()
)
}
}

fun <T> search(start: T, successors: (T) -> List<T>, visitor: (T) -> Unit): List<T> {
val visited = hashSetOf<T>()
fun dfs(node: T) = node.takeIf(visited::add)?.also(visitor)?.let { successors(it).forEach(::dfs) }
dfs(start)
return visited.toList()
}

fun <T> components(starts: Iterable<T>, successors: (T) -> List<T>): List<List<T>> {
val visited = hashSetOf<T>()
return starts.mapNotNull { start ->
if (start !in visited) search(start, { successors(it).filter { it !in visited } }, visited::add)
else null
}
}

fun <T> reachable(starts: Iterable<T>, successors: (T) -> List<T>): List<T> {
val visited = hashSetOf<T>()
starts.forEach { start ->
if (start !in visited)
visited.addAll(search(start, { successors(it).filter { it !in visited } }, {}))
}
return visited.toList()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
* Copyright 2022 UnitTestBot contributors (utbot.org)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jacodb.panda.staticvm

import org.jacodb.api.core.CoreMethod
import org.jacodb.api.core.cfg.ControlFlowGraph
import org.jacodb.api.jvm.cfg.JcInst

data class PandaField(
val project: PandaProject,
val declaringClassType: PandaClassName,
val type: PandaTypeName,
val name: String,
val access: AccessFlags
) {
val isNullable: Boolean
get() = !type.isPrimitive

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as PandaField

if (declaringClassType != other.declaringClassType) return false
if (type != other.type) return false
if (name != other.name) return false
if (access != other.access) return false

return true
}

override fun hashCode(): Int {
var result = declaringClassType.hashCode()
result = 31 * result + type.hashCode()
result = 31 * result + name.hashCode()
result = 31 * result + access.hashCode()
return result
}

override fun toString(): String {
return "PandaField(declaringClassType=$declaringClassType, type=$type, name='$name', access=$access, isNullable=$isNullable)"
}
}

data class PandaMethod(
val project: PandaProject,
val declaringClassType: PandaClassName,
val returnType: PandaTypeName,
val args: List<PandaTypeName>,
val name: String,
val body: SimpleDirectedGraph<PandaBasicBlockInfo>?,
val access: AccessFlags
) : CoreMethod<JcInst> {

override fun flowGraph(): ControlFlowGraph<JcInst> {
TODO("Not yet implemented")
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as PandaMethod

if (declaringClassType != other.declaringClassType) return false
if (returnType != other.returnType) return false
if (args != other.args) return false
if (name != other.name) return false
if (access != other.access) return false

return true
}

override fun hashCode(): Int {
var result = declaringClassType.hashCode()
result = 31 * result + returnType.hashCode()
result = 31 * result + args.hashCode()
result = 31 * result + name.hashCode()
result = 31 * result + access.hashCode()
return result
}

override fun toString(): String {
return "PandaMethod(declaringClassType=$declaringClassType, returnType=$returnType, args=$args, name='$name', access=$access)"
}

val instList: List<JcInst>
get() = TODO("Not yet implemented")
}

data class PandaClass(
val project: PandaProject,
val declaredFields: List<PandaField>,
val declaredMethods: List<PandaMethod>,
val name: String,
val superClassTypeName: PandaClassName?,
val interfacesTypeNames: List<PandaClassName>,
val access: AccessFlags
) {
val superClass: PandaClass?
get() = superClassTypeName?.let { project.classOf(it) }
val interfaces: List<PandaClass>
get() = project.typeOf(this).directSuperInterfaces.map {
project.classOf(it.arkName)
}
val isInterface: Boolean
get() = project.typeOf(this) is PandaInterfaceNode

val fields: List<PandaField>
get() = (superClass?.fields ?: listOf()) + declaredFields

val methods: List<PandaMethod>
get() = (superClass?.methods ?: listOf()) + declaredMethods

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as PandaClass

if (name != other.name) return false
if (superClassTypeName != other.superClassTypeName) return false
if (interfacesTypeNames != other.interfacesTypeNames) return false
if (access != other.access) return false

return true
}

override fun hashCode(): Int {
var result = name.hashCode()
result = 31 * result + (superClassTypeName?.hashCode() ?: 0)
result = 31 * result + interfacesTypeNames.hashCode()
result = 31 * result + access.hashCode()
return result
}
}
Loading

0 comments on commit 81f875e

Please sign in to comment.