Skip to content
This repository has been archived by the owner on Apr 16, 2024. It is now read-only.

Commit

Permalink
feat: lazy evaluation (#77)
Browse files Browse the repository at this point in the history
  • Loading branch information
jitsedesmet authored Jul 31, 2023
1 parent 8883dc3 commit e3a2e11
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 108 deletions.
2 changes: 1 addition & 1 deletion examples/2-methods.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"codeAttributes":[{"clazz":"PartialEvaluatorDummy","method":"test0()I","instructions":[{"offset":0,"instruction":"iconst_0","finalVariablesBefore":["LPartialEvaluatorDummy;!"],"finalStackBefore":[]},{"offset":1,"instruction":"ireturn","finalVariablesBefore":["LPartialEvaluatorDummy;!"],"finalStackBefore":["0"],"finalTargetInstructions":[]}],"parameters":["LPartialEvaluatorDummy;!"],"blockEvaluations":[{"startOffset":0,"evaluations":[{"skipEvaluation":false,"isGeneralization":false,"evaluationCount":0,"instruction":"iconst_0","instructionOffset":0,"variablesBefore":["LPartialEvaluatorDummy;!"],"stackBefore":[]},{"skipEvaluation":false,"isGeneralization":false,"evaluationCount":0,"instruction":"ireturn","instructionOffset":1,"variablesBefore":["LPartialEvaluatorDummy;!"],"stackBefore":["0"]}],"branchEvaluationStack":[],"startVariables":["LPartialEvaluatorDummy;!"],"startStack":[]}]},{"clazz":"PartialEvaluatorDummy","method":"test1()I","instructions":[{"offset":0,"instruction":"iconst_1","finalVariablesBefore":["LPartialEvaluatorDummy;!"],"finalStackBefore":[]},{"offset":1,"instruction":"ireturn","finalVariablesBefore":["LPartialEvaluatorDummy;!"],"finalStackBefore":["1"],"finalTargetInstructions":[]}],"parameters":["LPartialEvaluatorDummy;!"],"blockEvaluations":[{"startOffset":0,"evaluations":[{"skipEvaluation":false,"isGeneralization":false,"evaluationCount":0,"instruction":"iconst_1","instructionOffset":0,"variablesBefore":["LPartialEvaluatorDummy;!"],"stackBefore":[]},{"skipEvaluation":false,"isGeneralization":false,"evaluationCount":0,"instruction":"ireturn","instructionOffset":1,"variablesBefore":["LPartialEvaluatorDummy;!"],"stackBefore":["1"]}],"branchEvaluationStack":[],"startVariables":["LPartialEvaluatorDummy;!"],"startStack":[]}]}]}
{"codeAttributes":[{"clazz":"PartialEvaluatorDummy","method":"test0()I","instructions":[{"offset":0,"instruction":"iconst_0","finalVariablesBefore":["LPartialEvaluatorDummy;!"],"finalStackBefore":[]},{"offset":1,"instruction":"ireturn","finalVariablesBefore":["LPartialEvaluatorDummy;!"],"finalStackBefore":["0"],"finalTargetInstructions":[]}],"parameters":["LPartialEvaluatorDummy;!"],"blockEvaluations":[{"startOffset":0,"evaluations":[{"skipEvaluation":false,"isGeneralization":false,"evaluationCount":0,"instruction":"iconst_0","instructionOffset":0,"variablesBefore":["LPartialEvaluatorDummy;!"],"stackBefore":[]},{"skipEvaluation":false,"isGeneralization":false,"evaluationCount":0,"instruction":"ireturn","instructionOffset":1,"variablesBefore":["LPartialEvaluatorDummy;!"],"stackBefore":["0"]}],"branchEvaluationStack":[],"startVariables":["LPartialEvaluatorDummy;!"],"startStack":[]}]},{"clazz":"PartialEvaluatorDummy","method":"test0()V","instructions":[{"offset":0,"instruction":"iconst_1","finalVariablesBefore":["LPartialEvaluatorDummy;!"],"finalStackBefore":[]},{"offset":1,"instruction":"return","finalVariablesBefore":["LPartialEvaluatorDummy;!"],"finalStackBefore":["1"],"finalTargetInstructions":[]}],"parameters":["LPartialEvaluatorDummy;!"],"blockEvaluations":[{"startOffset":0,"evaluations":[{"skipEvaluation":false,"isGeneralization":false,"evaluationCount":0,"instruction":"iconst_1","instructionOffset":0,"variablesBefore":["LPartialEvaluatorDummy;!"],"stackBefore":[]},{"skipEvaluation":false,"isGeneralization":false,"evaluationCount":0,"instruction":"return","instructionOffset":1,"variablesBefore":["LPartialEvaluatorDummy;!"],"stackBefore":["1"]}],"branchEvaluationStack":[],"startVariables":["LPartialEvaluatorDummy;!"],"startStack":[]}]}]}
4 changes: 2 additions & 2 deletions src/jvmMain/kotlin/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ fun App() {

Box(Modifier.fillMaxSize().padding(all = 16.dp)) {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Controls(viewModel.currentCodeAttribute) {
Controls(viewModel.currentCodeAttributeViewModel) {
showFilePicker = it
}

Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
FileViewer(viewModel)
StateViewer(viewModel.currentCodeAttribute)
StateViewer(viewModel.currentCodeAttributeViewModel)
}
}

Expand Down
113 changes: 113 additions & 0 deletions src/jvmMain/kotlin/data/LoadUtil.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package data

import proguard.classfile.ClassPool
import proguard.classfile.Clazz
import proguard.classfile.Method
import proguard.classfile.attribute.Attribute
import proguard.classfile.attribute.CodeAttribute
import proguard.classfile.attribute.visitor.AllAttributeVisitor
import proguard.classfile.attribute.visitor.AttributeNameFilter
import proguard.classfile.attribute.visitor.AttributeVisitor
import proguard.classfile.visitor.AllClassVisitor
import proguard.classfile.visitor.AllMethodVisitor
import proguard.classfile.visitor.ClassPoolFiller
import proguard.classfile.visitor.FilteredClassVisitor
import proguard.evaluation.PartialEvaluator
import proguard.evaluation.util.jsonPrinter.JsonPrinter
import proguard.io.ClassFilter
import proguard.io.ClassReader
import proguard.io.DataEntrySource
import proguard.io.FileSource
import proguard.io.JarReader
import viewmodel.CodeAttributeViewModel
import java.nio.file.Path

class LoadUtil {
companion object {
fun getClassPoolFromJAR(path: Path): ClassPool {
val classPool = ClassPool()

val source: DataEntrySource = FileSource(
path.toFile(),
)

source.pumpDataEntries(
JarReader(
false,
ClassFilter(
ClassReader(
false,
false,
false,
false,
null,
ClassPoolFiller(classPool),
),
),
),
)

return classPool
}

fun classMethodMap(classPool: ClassPool): Map<String, Map<String, CodeAttributeViewModel?>> {
val classMap: MutableMap<String, MutableMap<String, CodeAttributeViewModel?>> = HashMap()
classPool.accept(
AllClassVisitor(
AllMethodVisitor(
AllAttributeVisitor(
AttributeNameFilter(
Attribute.CODE,
object : AttributeVisitor {
override fun visitCodeAttribute(
clazz: Clazz,
method: Method,
codeAttribute: CodeAttribute,
) {
classMap.getOrPut(clazz.name) { HashMap() }[method.getName(clazz) + method.getDescriptor(clazz)] = null
}
},
),
),
),
),
)
return classMap
}

fun evalSingleMethod(classPool: ClassPool, clazz: String, method: String): StateTracker? {
val tracker = JsonPrinter()
val pe = PartialEvaluator.Builder.create()
.setEvaluateAllCode(true).setStateTracker(tracker).build()
classPool.accept(
FilteredClassVisitor(
clazz,
AllMethodVisitor(
AllAttributeVisitor(
AttributeNameFilter(
Attribute.CODE,
object : AttributeVisitor {
override fun visitCodeAttribute(
clazz: Clazz,
visitedMethod: Method,
codeAttribute: CodeAttribute,
) {
if (visitedMethod.getName(clazz) + visitedMethod.getDescriptor(clazz) == method) {
pe.visitCodeAttribute(clazz, visitedMethod, codeAttribute)
}
}
},
),
),
),
),
)
try {
return StateTracker.fromJson(tracker.json)
} catch (e: Exception) {
println("Error while parsing json file: $e")
}
return null
}
}
}
29 changes: 11 additions & 18 deletions src/jvmMain/kotlin/ui/fileview/FileTree.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@ import androidx.compose.ui.unit.dp
import cafe.adriel.bonsai.core.node.Branch
import cafe.adriel.bonsai.core.node.Leaf
import cafe.adriel.bonsai.core.tree.Tree
import viewmodel.File
import java.nio.file.Path
import kotlin.io.path.name

/**
* Returns a bonsai tree where the content is the Pair<FileIndex, CodeAttributeIndex>
*/
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun FileTree(files: List<File>, closeFile: (Int) -> Unit): Tree<Pair<Int, Int>?> {
fun MethodTree(methods: Map<Path, Map<String, List<String>>>, closeFile: (Path) -> Unit): Tree<Pair<Path, Pair<String, String>>?> {
return Tree {
files.forEachIndexed { fileIndex, file ->
methods.toSortedMap().forEach { (path, clazzMap) ->
Branch(null, customName = {
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
Expand All @@ -50,7 +50,7 @@ fun FileTree(files: List<File>, closeFile: (Int) -> Unit): Tree<Pair<Int, Int>?>
var remove by remember { mutableStateOf(false) }
if (remove) {
remove = false
closeFile(fileIndex)
closeFile(path)
}

// An IconButton is currently fixed to 48.dp, so we need to make our own.
Expand Down Expand Up @@ -80,29 +80,22 @@ fun FileTree(files: List<File>, closeFile: (Int) -> Unit): Tree<Pair<Int, Int>?>
shape = MaterialTheme.shapes.extraSmall,
) {
Text(
text = file.path.name,
text = path.name,
modifier = Modifier.padding(4.dp),
style = MaterialTheme.typography.labelMedium,
)
}
}) {
Text(
file.path.name,
path.name,
style = MaterialTheme.typography.labelMedium,
overflow = TextOverflow.Ellipsis,
softWrap = false,
)
}
}
}) {
// Clazz name to <attributeIndex, method>
val branches = HashMap<String, MutableList<Pair<Int, String>>>()
file.codeAttributeViewModels.forEachIndexed { attributesIndex, codeAttributeViewModels ->
branches.getOrPut(codeAttributeViewModels.codeAttribute.clazz) { mutableListOf() }
.add(Pair(attributesIndex, codeAttributeViewModels.codeAttribute.method))
}

branches.forEach { (clazz, methods) ->
clazzMap.toSortedMap().forEach { (clazz, methods) ->
Branch(null, customName = {
TooltipArea(tooltip = {
Surface(
Expand All @@ -118,9 +111,9 @@ fun FileTree(files: List<File>, closeFile: (Int) -> Unit): Tree<Pair<Int, Int>?>
}
}) { Text(clazz, style = MaterialTheme.typography.labelMedium) }
}) {
methods.forEach { method ->
methods.sorted().forEach { method ->
Leaf(
Pair(fileIndex, method.first),
Pair(path, Pair(clazz, method)),
customName = {
TooltipArea(tooltip = {
Surface(
Expand All @@ -129,12 +122,12 @@ fun FileTree(files: List<File>, closeFile: (Int) -> Unit): Tree<Pair<Int, Int>?>
shape = MaterialTheme.shapes.extraSmall,
) {
Text(
text = method.second,
text = method,
modifier = Modifier.padding(4.dp),
style = MaterialTheme.typography.labelMedium,
)
}
}) { Text(method.second, style = MaterialTheme.typography.labelSmall) }
}) { Text(method, style = MaterialTheme.typography.labelSmall) }
},
)
}
Expand Down
10 changes: 7 additions & 3 deletions src/jvmMain/kotlin/ui/fileview/FileViewer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ fun FileViewer(viewModel: FilesViewModel) {
shape = MaterialTheme.shapes.medium,
).clip(MaterialTheme.shapes.medium),
) {
val tree = FileTree(viewModel.files) { index -> viewModel.closeFile(index) }
val tree = MethodTree(viewModel.files.mapValues { it.value.mapValues { inner -> inner.value.keys.toList() } }) { viewModel.closeFile(it) }
Bonsai(
tree = tree,
style = BonsaiStyle(
Expand All @@ -48,7 +48,11 @@ fun FileViewer(viewModel: FilesViewModel) {
tree.clearSelection()
tree.toggleExpansion(node)
tree.selectNode(node)
node.content?.let { viewModel.selectCodeAttribute(it.first, it.second) }
node.content?.let {
viewModel.curPath = it.first
viewModel.curClazz = it.second.first
viewModel.curMethod = it.second.second
}
},
)

Expand All @@ -59,7 +63,7 @@ fun FileViewer(viewModel: FilesViewModel) {
)

Column {
viewModel.currentCodeAttribute?.let {
viewModel.currentCodeAttributeViewModel?.let {
MethodHeader(it.codeAttribute)
CodeViewer(it)
}
Expand Down
11 changes: 0 additions & 11 deletions src/jvmMain/kotlin/viewmodel/File.kt

This file was deleted.

Loading

0 comments on commit e3a2e11

Please sign in to comment.