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

feat: working implementation for picking value factory #84

Merged
merged 4 commits into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 1 addition & 15 deletions src/jvmMain/kotlin/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,43 +19,29 @@ import androidx.compose.ui.res.loadImageBitmap
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import com.darkrockstudios.libraries.mpfilepicker.FilePicker
import com.jthemedetecor.OsThemeDetector
import com.materialkolor.AnimatedDynamicMaterialTheme
import ui.Controls
import ui.fileview.FileViewer
import ui.stateview.StateViewer
import viewmodel.FileTypes
import viewmodel.FilesViewModel
import java.nio.file.Path
import java.nio.file.Paths
import kotlin.io.path.exists
import kotlin.io.path.inputStream

@Composable
fun App() {
val viewModel = rememberSaveable { FilesViewModel() }
var showFilePicker by remember { mutableStateOf(false) }

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

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

// Accept json files
FilePicker(showFilePicker, fileExtensions = FileTypes.entries.map { it.extension }.toList()) { path ->
showFilePicker = false
if (path != null) {
viewModel.loadFile(Path.of(path.path))
}
}
}
}

Expand Down
95 changes: 93 additions & 2 deletions src/jvmMain/kotlin/data/LoadUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,18 @@ import proguard.classfile.visitor.AllClassVisitor
import proguard.classfile.visitor.AllMethodVisitor
import proguard.classfile.visitor.ClassPoolFiller
import proguard.classfile.visitor.FilteredClassVisitor
import proguard.evaluation.BasicInvocationUnit
import proguard.evaluation.PartialEvaluator
import proguard.evaluation.ReferenceTracingValueFactory
import proguard.evaluation.util.jsonPrinter.JsonPrinter
import proguard.evaluation.value.ArrayReferenceValueFactory
import proguard.evaluation.value.BasicValueFactory
import proguard.evaluation.value.DetailedArrayValueFactory
import proguard.evaluation.value.IdentifiedValueFactory
import proguard.evaluation.value.ParticularValueFactory
import proguard.evaluation.value.RangeValueFactory
import proguard.evaluation.value.TypedReferenceValueFactory
import proguard.evaluation.value.ValueFactory
import proguard.io.ClassReader
import proguard.io.DataEntryNameFilter
import proguard.io.DataEntryReader
Expand Down Expand Up @@ -122,9 +132,14 @@ class LoadUtil {
return classMap
}

fun evalSingleMethod(classPool: ClassPool, clazz: String, method: String): StateTracker? {
fun evalSingleMethod(classPool: ClassPool, clazz: String, method: String, valueFactoryOption: ValueFactoryOption): StateTracker? {
val tracker = JsonPrinter()
val pe = PartialEvaluator.Builder.create().setEvaluateAllCode(true).setStateTracker(tracker).build()
val valueFactory = valueFactoryOption.toValueFactory()
val pe = PartialEvaluator.Builder.create()
.setValueFactory(valueFactory)
.setInvocationUnit(BasicInvocationUnit(valueFactory))
.setStateTracker(tracker)
.build()
classPool.accept(
FilteredClassVisitor(
clazz,
Expand Down Expand Up @@ -156,4 +171,80 @@ class LoadUtil {
return null
}
}

enum class ValueFactoryOption {
Basic {
override fun toString(): String {
return "Basic Value Factory"
}

override fun toValueFactory(): ValueFactory {
return BasicValueFactory()
}
},
Particular {
override fun toString(): String {
return "Particular Value Factory"
}

override fun toValueFactory(): ValueFactory {
return ParticularValueFactory()
}
},
Range {
override fun toString(): String {
return "Range Value Factory"
}

override fun toValueFactory(): ValueFactory {
return RangeValueFactory()
}
},
ArrayReference {
override fun toString(): String {
return "Array Reference Value Factory"
}

override fun toValueFactory(): ValueFactory {
return ArrayReferenceValueFactory()
}
},
Identified {
override fun toString(): String {
return "Identified Value Factory"
}

override fun toValueFactory(): ValueFactory {
return IdentifiedValueFactory()
}
},
ReferenceTracing {
override fun toString(): String {
return "Reference Tracing Value Factory"
}

override fun toValueFactory(): ValueFactory {
return ReferenceTracingValueFactory(BasicValueFactory())
}
},
TypedReference {
override fun toString(): String {
return "Typed Reference Value Factory"
}

override fun toValueFactory(): ValueFactory {
return TypedReferenceValueFactory()
}
},
DetailedArrayReference {
override fun toString(): String {
return "Detailed Array Reference Value Factory"
}

override fun toValueFactory(): ValueFactory {
return DetailedArrayValueFactory()
}
}, ;
abstract fun toValueFactory(): ValueFactory
}
}
34 changes: 20 additions & 14 deletions src/jvmMain/kotlin/ui/Controls.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,46 @@ package ui
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material3.Button
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import viewmodel.CodeAttributeViewModel
import viewmodel.Display
import viewmodel.FilesViewModel

@Composable
fun Controls(viewModel: CodeAttributeViewModel?, setShowFilePicker: (Boolean) -> Unit) {
fun Controls(viewModel: FilesViewModel) {
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.wrapContentSize(align = Alignment.Companion.CenterStart, unbounded = true),
) {
Button(onClick = { setShowFilePicker(true) }) {
Text("Open file")
}
OpenFileButton(viewModel)
EvalPicker(viewModel)

OutlinedButton(enabled = viewModel != null && viewModel.hasPrevious, onClick = { viewModel?.previous() }) {
val currentCodeViewModel = viewModel.currentCodeAttributeViewModel
OutlinedButton(
enabled = currentCodeViewModel != null && currentCodeViewModel.hasPrevious,
onClick = { currentCodeViewModel?.previous() },
) {
Text("Previous")
}

OutlinedButton(enabled = viewModel != null && viewModel.hasNext, onClick = {
viewModel?.next()
}) {
OutlinedButton(
enabled = currentCodeViewModel != null && currentCodeViewModel.hasNext,
onClick = { currentCodeViewModel?.next() },
) {
Text("Next")
}

OutlinedButton(enabled = viewModel != null, onClick = {
viewModel?.switchDisplay()
}) {
when (viewModel?.display) {
OutlinedButton(
enabled = currentCodeViewModel != null,
onClick = { currentCodeViewModel?.switchDisplay() },
) {
when (currentCodeViewModel?.display) {
Display.EVALUATIONS -> Text("Show results")
Display.RESULTS -> Text("Show evaluations")
else -> { Text("Show results") }
Expand Down
54 changes: 54 additions & 0 deletions src/jvmMain/kotlin/ui/EvalPicker.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package ui

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.KeyboardArrowDown
import androidx.compose.material.icons.rounded.KeyboardArrowRight
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import data.LoadUtil
import viewmodel.FilesViewModel

@Composable
fun EvalPicker(viewModel: FilesViewModel) {
Box() {
var menuOpened by remember { mutableStateOf(false) }
val buttonEnabled = viewModel.files[viewModel.curPath]?.first != null
OutlinedButton(
enabled = buttonEnabled,
onClick = { menuOpened = true },
) {
Row() {
if (menuOpened) {
Icon(Icons.Rounded.KeyboardArrowDown, contentDescription = "Drawer handle is open")
}
if (!menuOpened) {
Icon(Icons.Rounded.KeyboardArrowRight, contentDescription = "Drawer handle is closed")
}
Text(if (buttonEnabled) viewModel.valueFactoryOption.toString() else "Cannot reevaluate")
}
}
DropdownMenu(menuOpened, { menuOpened = false }) {
LoadUtil.ValueFactoryOption.entries.sorted().forEach { evalOption ->
DropdownMenuItem(
{
Text(evalOption.toString())
},
onClick = {
menuOpened = false
viewModel.setEvalFactoryAndUpdate(evalOption)
},
)
}
}
}
}
30 changes: 30 additions & 0 deletions src/jvmMain/kotlin/ui/OpenFileButton.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package ui

import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import com.darkrockstudios.libraries.mpfilepicker.FilePicker
import viewmodel.FileTypes
import viewmodel.FilesViewModel
import java.nio.file.Path

@Composable
fun OpenFileButton(viewModel: FilesViewModel) {
var showFilePicker by remember { mutableStateOf(false) }

Button(onClick = { showFilePicker = true }) {
Text("Open file")
}

// Accept json files
FilePicker(showFilePicker, fileExtensions = FileTypes.entries.map { it.extension }.toList()) { path ->
showFilePicker = false
if (path != null) {
viewModel.loadFile(Path.of(path.path))
}
}
}
36 changes: 25 additions & 11 deletions src/jvmMain/kotlin/viewmodel/FilesViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ class FilesViewModel {
*/
var curMethod by mutableStateOf<String?>(null)

var valueFactoryOption by mutableStateOf(LoadUtil.ValueFactoryOption.Basic)
private set

fun setEvalFactoryAndUpdate(evaluationMethod: LoadUtil.ValueFactoryOption) {
this.valueFactoryOption = evaluationMethod
reEvalCurMethod()
}

/**
* Close a file
*/
Expand All @@ -48,34 +56,40 @@ class FilesViewModel {
}
}

/**
* Current viewModel, if not try to load it from the ClassPool
*/
val currentCodeAttributeViewModel by derivedStateOf {
private fun reEvalCurMethod(): CodeAttributeViewModel? {
curPath?.let { path ->
curClazz?.let { clazz ->
curMethod?.let { method ->
files[curPath]?.second?.get(curClazz)?.get(curMethod)?.let { return@derivedStateOf it }

files[curPath]?.first?.let { classPool ->
LoadUtil.evalSingleMethod(classPool, clazz, method)?.let {
files[path]?.first?.let { classPool ->
LoadUtil.evalSingleMethod(classPool, clazz, method, valueFactoryOption)?.let {
val codeAttribute = it.codeAttributes[0]
val newViewModel = CodeAttributeViewModel(codeAttribute)
val clazzMap = files.getValue(path).second
val methodMap = clazzMap.getValue(clazz)
files = files.plus(
Pair(
path,
Pair(classPool, clazzMap.plus(Pair(clazz, methodMap.plus(Pair(method, newViewModel))))),
Pair(
classPool,
clazzMap.plus(Pair(clazz, methodMap.plus(Pair(method, newViewModel)))),
),
),
)
return@derivedStateOf newViewModel
return newViewModel
}
}
}
}
}
return@derivedStateOf null
return null
}

/**
* Current viewModel, if not try to load it from the ClassPool
*/
val currentCodeAttributeViewModel by derivedStateOf {
files[curPath]?.second?.get(curClazz)?.get(curMethod)?.let { return@derivedStateOf it }
return@derivedStateOf reEvalCurMethod()
}

/**
Expand Down
Loading