Reading static fields supported
This commit is contained in:
@@ -116,6 +116,9 @@
|
|||||||
<item name='org.objectweb.asm.tree.FieldInsnNode desc'>
|
<item name='org.objectweb.asm.tree.FieldInsnNode desc'>
|
||||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||||
</item>
|
</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>)'>
|
<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'/>
|
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||||
</item>
|
</item>
|
||||||
@@ -125,6 +128,9 @@
|
|||||||
<val name="value" val="{org.jetbrains.kannotator.controlFlow.builder.analysis.NullabilityKey.class}"/>
|
<val name="value" val="{org.jetbrains.kannotator.controlFlow.builder.analysis.NullabilityKey.class}"/>
|
||||||
</annotation>
|
</annotation>
|
||||||
</item>
|
</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'>
|
<item name='org.objectweb.asm.tree.FieldInsnNode void accept(org.objectweb.asm.MethodVisitor) 0'>
|
||||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@@ -14,27 +14,6 @@ import org.objectweb.asm.tree.TypeInsnNode
|
|||||||
import org.objectweb.asm.tree.JumpInsnNode
|
import org.objectweb.asm.tree.JumpInsnNode
|
||||||
import org.objectweb.asm.tree.IincInsnNode
|
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)
|
class UnsupportedByteCodeException(message: String) : RuntimeException(message)
|
||||||
|
|
||||||
trait Eval {
|
trait Eval {
|
||||||
@@ -50,12 +29,12 @@ trait Eval {
|
|||||||
fun getArrayElement(array: Value, index: Value): Value
|
fun getArrayElement(array: Value, index: Value): Value
|
||||||
fun setArrayElement(array: Value, index: Value, newValue: Value)
|
fun setArrayElement(array: Value, index: Value, newValue: Value)
|
||||||
|
|
||||||
fun getStaticField(fieldDesc: String): Value
|
fun getStaticField(fieldDesc: FieldDescription): Value
|
||||||
fun setStaticField(fieldDesc: String, newValue: Value)
|
fun setStaticField(fieldDesc: FieldDescription, newValue: Value)
|
||||||
fun invokeStaticMethod(methodDesc: MethodDescription, arguments: List<Value>): Value
|
fun invokeStaticMethod(methodDesc: MethodDescription, arguments: List<Value>): Value
|
||||||
|
|
||||||
fun getField(instance: Value, fieldDesc: String): Value
|
fun getField(instance: Value, fieldDesc: FieldDescription): Value
|
||||||
fun setField(instance: Value, fieldDesc: String, newValue: Value)
|
fun setField(instance: Value, fieldDesc: FieldDescription, newValue: Value)
|
||||||
fun invokeMethod(instance: Value, methodDesc: MethodDescription, arguments: List<Value>, invokespecial: Boolean = false): 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)
|
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))
|
NEW -> eval.newInstance(Type.getType((insn as TypeInsnNode).desc))
|
||||||
else -> throw UnsupportedByteCodeException("$insn")
|
else -> throw UnsupportedByteCodeException("$insn")
|
||||||
}
|
}
|
||||||
@@ -164,11 +143,11 @@ class SingleInstructionInterpreter(private val eval: Eval) : Interpreter<Value>(
|
|||||||
LOOKUPSWITCH -> throw UnsupportedByteCodeException("Switch is not supported yet")
|
LOOKUPSWITCH -> throw UnsupportedByteCodeException("Switch is not supported yet")
|
||||||
|
|
||||||
PUTSTATIC -> {
|
PUTSTATIC -> {
|
||||||
eval.setStaticField((insn as FieldInsnNode).desc, value)
|
eval.setStaticField(FieldDescription(insn as FieldInsnNode), value)
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
GETFIELD -> eval.getField(value, (insn as FieldInsnNode).desc)
|
GETFIELD -> eval.getField(value, FieldDescription(insn as FieldInsnNode))
|
||||||
|
|
||||||
NEWARRAY -> {
|
NEWARRAY -> {
|
||||||
val typeStr = when ((insn as IntInsnNode).operand) {
|
val typeStr = when ((insn as IntInsnNode).operand) {
|
||||||
@@ -313,7 +292,7 @@ class SingleInstructionInterpreter(private val eval: Eval) : Interpreter<Value>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
PUTFIELD -> {
|
PUTFIELD -> {
|
||||||
eval.setField(value1, (insn as FieldInsnNode).desc, value2)
|
eval.setField(value1, FieldDescription(insn as FieldInsnNode), value2)
|
||||||
null
|
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");
|
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.TestSuite
|
||||||
import junit.framework.TestCase
|
import junit.framework.TestCase
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
|
import java.lang.reflect.Field
|
||||||
|
import kotlin.test.assertNotNull
|
||||||
|
|
||||||
fun suite(): TestSuite {
|
fun suite(): TestSuite {
|
||||||
val suite = TestSuite()
|
val suite = TestSuite()
|
||||||
@@ -129,11 +131,18 @@ object REFLECTION_EVAL : Eval {
|
|||||||
throw UnsupportedOperationException()
|
throw UnsupportedOperationException()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getStaticField(fieldDesc: String): Value {
|
override fun getStaticField(fieldDesc: FieldDescription): Value {
|
||||||
throw UnsupportedOperationException()
|
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 {
|
override fun invokeStaticMethod(methodDesc: MethodDescription, arguments: List<Value>): Value {
|
||||||
@@ -146,10 +155,10 @@ object REFLECTION_EVAL : Eval {
|
|||||||
return objectToValue(result, methodDesc.returnType)
|
return objectToValue(result, methodDesc.returnType)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getField(instance: Value, fieldDesc: String): Value {
|
override fun getField(instance: Value, fieldDesc: FieldDescription): Value {
|
||||||
throw UnsupportedOperationException()
|
throw UnsupportedOperationException()
|
||||||
}
|
}
|
||||||
override fun setField(instance: Value, fieldDesc: String, newValue: Value) {
|
override fun setField(instance: Value, fieldDesc: FieldDescription, newValue: Value) {
|
||||||
throw UnsupportedOperationException()
|
throw UnsupportedOperationException()
|
||||||
}
|
}
|
||||||
override fun invokeMethod(instance: Value, methodDesc: MethodDescription, arguments: List<Value>, invokespecial: Boolean): Value {
|
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()!!)
|
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)
|
fun Type.matches(_class: Class<*>): Boolean = this == Type.getType(_class)
|
||||||
Reference in New Issue
Block a user