Reading static fields supported
This commit is contained in:
@@ -116,6 +116,9 @@
|
||||
<item name='org.objectweb.asm.tree.FieldInsnNode desc'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='org.objectweb.asm.tree.FieldInsnNode name'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='org.objectweb.asm.tree.FieldInsnNode org.objectweb.asm.tree.AbstractInsnNode clone(java.util.Map<org.objectweb.asm.tree.LabelNode,org.objectweb.asm.tree.LabelNode>)'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
@@ -125,6 +128,9 @@
|
||||
<val name="value" val="{org.jetbrains.kannotator.controlFlow.builder.analysis.NullabilityKey.class}"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.objectweb.asm.tree.FieldInsnNode owner'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='org.objectweb.asm.tree.FieldInsnNode void accept(org.objectweb.asm.MethodVisitor) 0'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
|
||||
@@ -14,27 +14,6 @@ import org.objectweb.asm.tree.TypeInsnNode
|
||||
import org.objectweb.asm.tree.JumpInsnNode
|
||||
import org.objectweb.asm.tree.IincInsnNode
|
||||
|
||||
data class MethodDescription(
|
||||
val ownerInternalName: String,
|
||||
val name: String,
|
||||
val desc: String,
|
||||
val isStatic: Boolean
|
||||
)
|
||||
|
||||
fun MethodDescription(insn: MethodInsnNode): MethodDescription =
|
||||
MethodDescription(
|
||||
insn.owner,
|
||||
insn.name,
|
||||
insn.desc,
|
||||
insn.getOpcode() == INVOKESTATIC
|
||||
)
|
||||
|
||||
val MethodDescription.returnType: Type
|
||||
get() = Type.getReturnType(desc)
|
||||
|
||||
val MethodDescription.parameterTypes: List<Type>
|
||||
get() = Type.getArgumentTypes(desc).toList()
|
||||
|
||||
class UnsupportedByteCodeException(message: String) : RuntimeException(message)
|
||||
|
||||
trait Eval {
|
||||
@@ -50,12 +29,12 @@ trait Eval {
|
||||
fun getArrayElement(array: Value, index: Value): Value
|
||||
fun setArrayElement(array: Value, index: Value, newValue: Value)
|
||||
|
||||
fun getStaticField(fieldDesc: String): Value
|
||||
fun setStaticField(fieldDesc: String, newValue: Value)
|
||||
fun getStaticField(fieldDesc: FieldDescription): Value
|
||||
fun setStaticField(fieldDesc: FieldDescription, newValue: Value)
|
||||
fun invokeStaticMethod(methodDesc: MethodDescription, arguments: List<Value>): Value
|
||||
|
||||
fun getField(instance: Value, fieldDesc: String): Value
|
||||
fun setField(instance: Value, fieldDesc: String, newValue: Value)
|
||||
fun getField(instance: Value, fieldDesc: FieldDescription): Value
|
||||
fun setField(instance: Value, fieldDesc: FieldDescription, newValue: Value)
|
||||
fun invokeMethod(instance: Value, methodDesc: MethodDescription, arguments: List<Value>, invokespecial: Boolean = false): Value
|
||||
}
|
||||
|
||||
@@ -118,7 +97,7 @@ class SingleInstructionInterpreter(private val eval: Eval) : Interpreter<Value>(
|
||||
}
|
||||
}
|
||||
JSR -> LabelValue((insn as JumpInsnNode).label)
|
||||
GETSTATIC -> eval.getStaticField((insn as FieldInsnNode).desc)
|
||||
GETSTATIC -> eval.getStaticField(FieldDescription(insn as FieldInsnNode))
|
||||
NEW -> eval.newInstance(Type.getType((insn as TypeInsnNode).desc))
|
||||
else -> throw UnsupportedByteCodeException("$insn")
|
||||
}
|
||||
@@ -164,11 +143,11 @@ class SingleInstructionInterpreter(private val eval: Eval) : Interpreter<Value>(
|
||||
LOOKUPSWITCH -> throw UnsupportedByteCodeException("Switch is not supported yet")
|
||||
|
||||
PUTSTATIC -> {
|
||||
eval.setStaticField((insn as FieldInsnNode).desc, value)
|
||||
eval.setStaticField(FieldDescription(insn as FieldInsnNode), value)
|
||||
null
|
||||
}
|
||||
|
||||
GETFIELD -> eval.getField(value, (insn as FieldInsnNode).desc)
|
||||
GETFIELD -> eval.getField(value, FieldDescription(insn as FieldInsnNode))
|
||||
|
||||
NEWARRAY -> {
|
||||
val typeStr = when ((insn as IntInsnNode).operand) {
|
||||
@@ -313,7 +292,7 @@ class SingleInstructionInterpreter(private val eval: Eval) : Interpreter<Value>(
|
||||
}
|
||||
|
||||
PUTFIELD -> {
|
||||
eval.setField(value1, (insn as FieldInsnNode).desc, value2)
|
||||
eval.setField(value1, FieldDescription(insn as FieldInsnNode), value2)
|
||||
null
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.jetbrains.eval4j
|
||||
|
||||
import org.objectweb.asm.tree.MethodInsnNode
|
||||
import org.objectweb.asm.Opcodes.*
|
||||
import org.objectweb.asm.*
|
||||
import org.objectweb.asm.tree.FieldInsnNode
|
||||
|
||||
open data class MemberDescription(
|
||||
val ownerInternalName: String,
|
||||
val name: String,
|
||||
val desc: String,
|
||||
val isStatic: Boolean
|
||||
)
|
||||
|
||||
class MethodDescription(
|
||||
ownerInternalName: String,
|
||||
name: String,
|
||||
desc: String,
|
||||
isStatic: Boolean
|
||||
) : MemberDescription(ownerInternalName, name, desc, isStatic)
|
||||
|
||||
fun MethodDescription(insn: MethodInsnNode): MethodDescription =
|
||||
MethodDescription(
|
||||
insn.owner,
|
||||
insn.name,
|
||||
insn.desc,
|
||||
insn.getOpcode() == INVOKESTATIC
|
||||
)
|
||||
|
||||
val MethodDescription.returnType: Type
|
||||
get() = Type.getReturnType(desc)
|
||||
|
||||
val MethodDescription.parameterTypes: List<Type>
|
||||
get() = Type.getArgumentTypes(desc).toList()
|
||||
|
||||
|
||||
class FieldDescription(
|
||||
ownerInternalName: String,
|
||||
name: String,
|
||||
desc: String,
|
||||
isStatic: Boolean
|
||||
) : MemberDescription(ownerInternalName, name, desc, isStatic)
|
||||
|
||||
fun FieldDescription(insn: FieldInsnNode): FieldDescription =
|
||||
FieldDescription(
|
||||
insn.owner,
|
||||
insn.name,
|
||||
insn.desc,
|
||||
insn.getOpcode() in setOf(GETSTATIC, PUTSTATIC)
|
||||
)
|
||||
|
||||
val FieldDescription.fieldType: Type
|
||||
get() = Type.getType(desc)
|
||||
@@ -152,4 +152,8 @@ class TestData {
|
||||
return String.valueOf("str");
|
||||
}
|
||||
|
||||
static Object testGetStaticField() {
|
||||
return String.CASE_INSENSITIVE_ORDER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import org.junit.Assert.*
|
||||
import junit.framework.TestSuite
|
||||
import junit.framework.TestCase
|
||||
import java.lang.reflect.Method
|
||||
import java.lang.reflect.Field
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
fun suite(): TestSuite {
|
||||
val suite = TestSuite()
|
||||
@@ -129,11 +131,18 @@ object REFLECTION_EVAL : Eval {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun getStaticField(fieldDesc: String): Value {
|
||||
throw UnsupportedOperationException()
|
||||
override fun getStaticField(fieldDesc: FieldDescription): Value {
|
||||
assertTrue(fieldDesc.isStatic)
|
||||
val owner = lookup.findClass(fieldDesc.ownerInternalName)
|
||||
assertNotNull("Class not found: ${fieldDesc.ownerInternalName}", owner)
|
||||
val field = owner!!.findField(fieldDesc)
|
||||
assertNotNull("Field not found: $fieldDesc", field)
|
||||
val result = field!!.get(null)
|
||||
return objectToValue(result, fieldDesc.fieldType)
|
||||
}
|
||||
override fun setStaticField(fieldDesc: String, newValue: Value) {
|
||||
throw UnsupportedOperationException()
|
||||
|
||||
override fun setStaticField(fieldDesc: FieldDescription, newValue: Value) {
|
||||
assertTrue(fieldDesc.isStatic)
|
||||
}
|
||||
|
||||
override fun invokeStaticMethod(methodDesc: MethodDescription, arguments: List<Value>): Value {
|
||||
@@ -146,10 +155,10 @@ object REFLECTION_EVAL : Eval {
|
||||
return objectToValue(result, methodDesc.returnType)
|
||||
}
|
||||
|
||||
override fun getField(instance: Value, fieldDesc: String): Value {
|
||||
override fun getField(instance: Value, fieldDesc: FieldDescription): Value {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
override fun setField(instance: Value, fieldDesc: String, newValue: Value) {
|
||||
override fun setField(instance: Value, fieldDesc: FieldDescription, newValue: Value) {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
override fun invokeMethod(instance: Value, methodDesc: MethodDescription, arguments: List<Value>, invokespecial: Boolean): Value {
|
||||
@@ -191,4 +200,27 @@ fun MethodDescription.matches(method: Method): Boolean {
|
||||
return returnType.matches(method.getReturnType()!!)
|
||||
}
|
||||
|
||||
[suppress("UNCHECKED_CAST")]
|
||||
fun Class<Any?>.findField(fieldDesc: FieldDescription): Field? {
|
||||
for (declared in getDeclaredFields()) {
|
||||
if (fieldDesc.matches(declared)) return declared
|
||||
}
|
||||
|
||||
val fromSuperClass = (getSuperclass() as Class<Any?>).findField(fieldDesc)
|
||||
if (fromSuperClass != null) return fromSuperClass
|
||||
|
||||
for (supertype in getInterfaces()) {
|
||||
val fromSuper = (supertype as Class<Any?>).findField(fieldDesc)
|
||||
if (fromSuper != null) return fromSuper
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun FieldDescription.matches(field: Field): Boolean {
|
||||
if (name != field.getName()) return false
|
||||
|
||||
return fieldType.matches(field.getType()!!)
|
||||
}
|
||||
|
||||
fun Type.matches(_class: Class<*>): Boolean = this == Type.getType(_class)
|
||||
Reference in New Issue
Block a user