Refactor ASM builder
This commit is contained in:
parent
f001389f4c
commit
cc45e3683b
@ -6,6 +6,7 @@ import kscience.kmath.expressions.Expression
|
|||||||
import kscience.kmath.operations.Algebra
|
import kscience.kmath.operations.Algebra
|
||||||
import org.objectweb.asm.*
|
import org.objectweb.asm.*
|
||||||
import org.objectweb.asm.Opcodes.*
|
import org.objectweb.asm.Opcodes.*
|
||||||
|
import org.objectweb.asm.Type.*
|
||||||
import org.objectweb.asm.commons.InstructionAdapter
|
import org.objectweb.asm.commons.InstructionAdapter
|
||||||
import java.util.stream.Collectors.toMap
|
import java.util.stream.Collectors.toMap
|
||||||
import kotlin.contracts.InvocationKind
|
import kotlin.contracts.InvocationKind
|
||||||
@ -17,13 +18,13 @@ import kotlin.contracts.contract
|
|||||||
*
|
*
|
||||||
* @property T the type of AsmExpression to unwrap.
|
* @property T the type of AsmExpression to unwrap.
|
||||||
* @property className the unique class name of new loaded class.
|
* @property className the unique class name of new loaded class.
|
||||||
* @property invokeLabel0Visitor the function to apply to this object when generating invoke method, label 0.
|
* @property callbackAtInvokeL0 the function to apply to this object when generating invoke method, label 0.
|
||||||
* @author Iaroslav Postovalov
|
* @author Iaroslav Postovalov
|
||||||
*/
|
*/
|
||||||
internal class AsmBuilder<T> internal constructor(
|
internal class AsmBuilder<T>(
|
||||||
classOfT: Class<*>,
|
classOfT: Class<*>,
|
||||||
private val className: String,
|
private val className: String,
|
||||||
private val invokeLabel0Visitor: AsmBuilder<T>.() -> Unit,
|
private val callbackAtInvokeL0: AsmBuilder<T>.() -> Unit,
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* Internal classloader of [AsmBuilder] with alias to define class from byte array.
|
* Internal classloader of [AsmBuilder] with alias to define class from byte array.
|
||||||
@ -45,7 +46,7 @@ internal class AsmBuilder<T> internal constructor(
|
|||||||
/**
|
/**
|
||||||
* ASM type for new class.
|
* ASM type for new class.
|
||||||
*/
|
*/
|
||||||
private val classType: Type = Type.getObjectType(className.replace(oldChar = '.', newChar = '/'))!!
|
private val classType: Type = getObjectType(className.replace(oldChar = '.', newChar = '/'))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of constants to provide to the subclass.
|
* List of constants to provide to the subclass.
|
||||||
@ -57,11 +58,6 @@ internal class AsmBuilder<T> internal constructor(
|
|||||||
*/
|
*/
|
||||||
private lateinit var invokeMethodVisitor: InstructionAdapter
|
private lateinit var invokeMethodVisitor: InstructionAdapter
|
||||||
|
|
||||||
/**
|
|
||||||
* States whether this [AsmBuilder] needs to generate constants field.
|
|
||||||
*/
|
|
||||||
private var hasConstants: Boolean = true
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subclasses, loads and instantiates [Expression] for given parameters.
|
* Subclasses, loads and instantiates [Expression] for given parameters.
|
||||||
*
|
*
|
||||||
@ -69,6 +65,8 @@ internal class AsmBuilder<T> internal constructor(
|
|||||||
*/
|
*/
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val instance: Expression<T> by lazy {
|
val instance: Expression<T> by lazy {
|
||||||
|
val hasConstants: Boolean
|
||||||
|
|
||||||
val classWriter = ClassWriter(ClassWriter.COMPUTE_FRAMES) {
|
val classWriter = ClassWriter(ClassWriter.COMPUTE_FRAMES) {
|
||||||
visit(
|
visit(
|
||||||
V1_8,
|
V1_8,
|
||||||
@ -82,14 +80,14 @@ internal class AsmBuilder<T> internal constructor(
|
|||||||
visitMethod(
|
visitMethod(
|
||||||
ACC_PUBLIC or ACC_FINAL,
|
ACC_PUBLIC or ACC_FINAL,
|
||||||
"invoke",
|
"invoke",
|
||||||
Type.getMethodDescriptor(tType, MAP_TYPE),
|
getMethodDescriptor(tType, MAP_TYPE),
|
||||||
"(L${MAP_TYPE.internalName}<${STRING_TYPE.descriptor}+${tType.descriptor}>;)${tType.descriptor}",
|
"(L${MAP_TYPE.internalName}<${STRING_TYPE.descriptor}+${tType.descriptor}>;)${tType.descriptor}",
|
||||||
null
|
null
|
||||||
).instructionAdapter {
|
).instructionAdapter {
|
||||||
invokeMethodVisitor = this
|
invokeMethodVisitor = this
|
||||||
visitCode()
|
visitCode()
|
||||||
val l0 = label()
|
val l0 = label()
|
||||||
invokeLabel0Visitor()
|
callbackAtInvokeL0()
|
||||||
areturn(tType)
|
areturn(tType)
|
||||||
val l1 = label()
|
val l1 = label()
|
||||||
|
|
||||||
@ -99,7 +97,7 @@ internal class AsmBuilder<T> internal constructor(
|
|||||||
null,
|
null,
|
||||||
l0,
|
l0,
|
||||||
l1,
|
l1,
|
||||||
invokeThisVar
|
0
|
||||||
)
|
)
|
||||||
|
|
||||||
visitLocalVariable(
|
visitLocalVariable(
|
||||||
@ -108,7 +106,7 @@ internal class AsmBuilder<T> internal constructor(
|
|||||||
"L${MAP_TYPE.internalName}<${STRING_TYPE.descriptor}+${tType.descriptor}>;",
|
"L${MAP_TYPE.internalName}<${STRING_TYPE.descriptor}+${tType.descriptor}>;",
|
||||||
l0,
|
l0,
|
||||||
l1,
|
l1,
|
||||||
invokeArgumentsVar
|
1
|
||||||
)
|
)
|
||||||
|
|
||||||
visitMaxs(0, 2)
|
visitMaxs(0, 2)
|
||||||
@ -118,7 +116,7 @@ internal class AsmBuilder<T> internal constructor(
|
|||||||
visitMethod(
|
visitMethod(
|
||||||
ACC_PUBLIC or ACC_FINAL or ACC_BRIDGE or ACC_SYNTHETIC,
|
ACC_PUBLIC or ACC_FINAL or ACC_BRIDGE or ACC_SYNTHETIC,
|
||||||
"invoke",
|
"invoke",
|
||||||
Type.getMethodDescriptor(OBJECT_TYPE, MAP_TYPE),
|
getMethodDescriptor(OBJECT_TYPE, MAP_TYPE),
|
||||||
null,
|
null,
|
||||||
null
|
null
|
||||||
).instructionAdapter {
|
).instructionAdapter {
|
||||||
@ -128,7 +126,7 @@ internal class AsmBuilder<T> internal constructor(
|
|||||||
val l0 = label()
|
val l0 = label()
|
||||||
load(thisVar, OBJECT_TYPE)
|
load(thisVar, OBJECT_TYPE)
|
||||||
load(argumentsVar, MAP_TYPE)
|
load(argumentsVar, MAP_TYPE)
|
||||||
invokevirtual(classType.internalName, "invoke", Type.getMethodDescriptor(tType, MAP_TYPE), false)
|
invokevirtual(classType.internalName, "invoke", getMethodDescriptor(tType, MAP_TYPE), false)
|
||||||
areturn(tType)
|
areturn(tType)
|
||||||
val l1 = label()
|
val l1 = label()
|
||||||
|
|
||||||
@ -160,32 +158,30 @@ internal class AsmBuilder<T> internal constructor(
|
|||||||
visitMethod(
|
visitMethod(
|
||||||
ACC_PUBLIC,
|
ACC_PUBLIC,
|
||||||
"<init>",
|
"<init>",
|
||||||
Type.getMethodDescriptor(Type.VOID_TYPE, *OBJECT_ARRAY_TYPE.wrapToArrayIf { hasConstants }),
|
getMethodDescriptor(VOID_TYPE, *OBJECT_ARRAY_TYPE.wrapToArrayIf { hasConstants }),
|
||||||
null,
|
null,
|
||||||
null
|
null
|
||||||
).instructionAdapter {
|
).instructionAdapter {
|
||||||
val thisVar = 0
|
|
||||||
val constantsVar = 1
|
|
||||||
val l0 = label()
|
val l0 = label()
|
||||||
load(thisVar, classType)
|
load(0, classType)
|
||||||
invokespecial(OBJECT_TYPE.internalName, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE), false)
|
invokespecial(OBJECT_TYPE.internalName, "<init>", getMethodDescriptor(VOID_TYPE), false)
|
||||||
label()
|
label()
|
||||||
load(thisVar, classType)
|
load(0, classType)
|
||||||
|
|
||||||
if (hasConstants) {
|
if (hasConstants) {
|
||||||
label()
|
label()
|
||||||
load(thisVar, classType)
|
load(0, classType)
|
||||||
load(constantsVar, OBJECT_ARRAY_TYPE)
|
load(1, OBJECT_ARRAY_TYPE)
|
||||||
putfield(classType.internalName, "constants", OBJECT_ARRAY_TYPE.descriptor)
|
putfield(classType.internalName, "constants", OBJECT_ARRAY_TYPE.descriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
label()
|
label()
|
||||||
visitInsn(RETURN)
|
visitInsn(RETURN)
|
||||||
val l4 = label()
|
val l4 = label()
|
||||||
visitLocalVariable("this", classType.descriptor, null, l0, l4, thisVar)
|
visitLocalVariable("this", classType.descriptor, null, l0, l4, 0)
|
||||||
|
|
||||||
if (hasConstants)
|
if (hasConstants)
|
||||||
visitLocalVariable("constants", OBJECT_ARRAY_TYPE.descriptor, null, l0, l4, constantsVar)
|
visitLocalVariable("constants", OBJECT_ARRAY_TYPE.descriptor, null, l0, l4, 1)
|
||||||
|
|
||||||
visitMaxs(0, 3)
|
visitMaxs(0, 3)
|
||||||
visitEnd()
|
visitEnd()
|
||||||
@ -218,7 +214,7 @@ internal class AsmBuilder<T> internal constructor(
|
|||||||
/**
|
/**
|
||||||
* Loads `this` variable.
|
* Loads `this` variable.
|
||||||
*/
|
*/
|
||||||
private fun loadThis(): Unit = invokeMethodVisitor.load(invokeThisVar, classType)
|
private fun loadThis(): Unit = invokeMethodVisitor.load(0, classType)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Either loads a numeric constant [value] from the class's constants field or boxes a primitive
|
* Either loads a numeric constant [value] from the class's constants field or boxes a primitive
|
||||||
@ -230,12 +226,12 @@ internal class AsmBuilder<T> internal constructor(
|
|||||||
|
|
||||||
if (primitive != null) {
|
if (primitive != null) {
|
||||||
when (primitive) {
|
when (primitive) {
|
||||||
Type.BYTE_TYPE -> invokeMethodVisitor.iconst(value.toInt())
|
BYTE_TYPE -> invokeMethodVisitor.iconst(value.toInt())
|
||||||
Type.DOUBLE_TYPE -> invokeMethodVisitor.dconst(value.toDouble())
|
DOUBLE_TYPE -> invokeMethodVisitor.dconst(value.toDouble())
|
||||||
Type.FLOAT_TYPE -> invokeMethodVisitor.fconst(value.toFloat())
|
FLOAT_TYPE -> invokeMethodVisitor.fconst(value.toFloat())
|
||||||
Type.LONG_TYPE -> invokeMethodVisitor.lconst(value.toLong())
|
LONG_TYPE -> invokeMethodVisitor.lconst(value.toLong())
|
||||||
Type.INT_TYPE -> invokeMethodVisitor.iconst(value.toInt())
|
INT_TYPE -> invokeMethodVisitor.iconst(value.toInt())
|
||||||
Type.SHORT_TYPE -> invokeMethodVisitor.iconst(value.toInt())
|
SHORT_TYPE -> invokeMethodVisitor.iconst(value.toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
box(primitive)
|
box(primitive)
|
||||||
@ -254,7 +250,7 @@ internal class AsmBuilder<T> internal constructor(
|
|||||||
invokeMethodVisitor.invokestatic(
|
invokeMethodVisitor.invokestatic(
|
||||||
r.internalName,
|
r.internalName,
|
||||||
"valueOf",
|
"valueOf",
|
||||||
Type.getMethodDescriptor(r, primitive),
|
getMethodDescriptor(r, primitive),
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -263,13 +259,13 @@ internal class AsmBuilder<T> internal constructor(
|
|||||||
* Loads a variable [name] from arguments [Map] parameter of [Expression.invoke].
|
* Loads a variable [name] from arguments [Map] parameter of [Expression.invoke].
|
||||||
*/
|
*/
|
||||||
fun loadVariable(name: String): Unit = invokeMethodVisitor.run {
|
fun loadVariable(name: String): Unit = invokeMethodVisitor.run {
|
||||||
load(invokeArgumentsVar, MAP_TYPE)
|
load(1, MAP_TYPE)
|
||||||
aconst(name)
|
aconst(name)
|
||||||
|
|
||||||
invokestatic(
|
invokestatic(
|
||||||
MAP_INTRINSICS_TYPE.internalName,
|
MAP_INTRINSICS_TYPE.internalName,
|
||||||
"getOrFail",
|
"getOrFail",
|
||||||
Type.getMethodDescriptor(OBJECT_TYPE, MAP_TYPE, STRING_TYPE),
|
getMethodDescriptor(OBJECT_TYPE, MAP_TYPE, STRING_TYPE),
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -283,100 +279,81 @@ internal class AsmBuilder<T> internal constructor(
|
|||||||
val arity = `interface`.methods.find { it.name == "invoke" }?.parameterCount
|
val arity = `interface`.methods.find { it.name == "invoke" }?.parameterCount
|
||||||
?: error("Provided function object doesn't contain invoke method")
|
?: error("Provided function object doesn't contain invoke method")
|
||||||
|
|
||||||
val type = Type.getType(`interface`)
|
val type = getType(`interface`)
|
||||||
loadObjectConstant(function, type)
|
loadObjectConstant(function, type)
|
||||||
parameters(this)
|
parameters(this)
|
||||||
|
|
||||||
invokeMethodVisitor.invokeinterface(
|
invokeMethodVisitor.invokeinterface(
|
||||||
type.internalName,
|
type.internalName,
|
||||||
"invoke",
|
"invoke",
|
||||||
Type.getMethodDescriptor(OBJECT_TYPE, *Array(arity) { OBJECT_TYPE}),
|
getMethodDescriptor(OBJECT_TYPE, *Array(arity) { OBJECT_TYPE }),
|
||||||
)
|
)
|
||||||
|
|
||||||
invokeMethodVisitor.checkcast(tType)
|
invokeMethodVisitor.checkcast(tType)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal companion object {
|
companion object {
|
||||||
/**
|
|
||||||
* Index of `this` variable in invoke method of the built subclass.
|
|
||||||
*/
|
|
||||||
private const val invokeThisVar: Int = 0
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Index of `arguments` variable in invoke method of the built subclass.
|
|
||||||
*/
|
|
||||||
private const val invokeArgumentsVar: Int = 1
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps JVM primitive numbers boxed types to their primitive ASM types.
|
|
||||||
*/
|
|
||||||
private val SIGNATURE_LETTERS: Map<Class<out Any>, Type> by lazy {
|
|
||||||
hashMapOf(
|
|
||||||
java.lang.Byte::class.java to Type.BYTE_TYPE,
|
|
||||||
java.lang.Short::class.java to Type.SHORT_TYPE,
|
|
||||||
java.lang.Integer::class.java to Type.INT_TYPE,
|
|
||||||
java.lang.Long::class.java to Type.LONG_TYPE,
|
|
||||||
java.lang.Float::class.java to Type.FLOAT_TYPE,
|
|
||||||
java.lang.Double::class.java to Type.DOUBLE_TYPE
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps JVM primitive numbers boxed ASM types to their primitive ASM types.
|
* Maps JVM primitive numbers boxed ASM types to their primitive ASM types.
|
||||||
*/
|
*/
|
||||||
private val BOXED_TO_PRIMITIVES: Map<Type, Type> by lazy { SIGNATURE_LETTERS.mapKeys { (k, _) -> k.asm } }
|
private val BOXED_TO_PRIMITIVES: Map<Type, Type> by lazy {
|
||||||
|
hashMapOf(
|
||||||
|
Byte::class.java.asm to BYTE_TYPE,
|
||||||
|
Short::class.java.asm to SHORT_TYPE,
|
||||||
|
Integer::class.java.asm to INT_TYPE,
|
||||||
|
Long::class.java.asm to LONG_TYPE,
|
||||||
|
Float::class.java.asm to FLOAT_TYPE,
|
||||||
|
Double::class.java.asm to DOUBLE_TYPE,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps JVM primitive numbers boxed ASM types to their primitive ASM types.
|
* Maps JVM primitive numbers boxed ASM types to their primitive ASM types.
|
||||||
*/
|
*/
|
||||||
private val PRIMITIVES_TO_BOXED: Map<Type, Type> by lazy {
|
private val PRIMITIVES_TO_BOXED: Map<Type, Type> by lazy {
|
||||||
BOXED_TO_PRIMITIVES.entries.stream().collect(
|
BOXED_TO_PRIMITIVES.entries.stream().collect(
|
||||||
toMap(
|
toMap(Map.Entry<Type, Type>::value, Map.Entry<Type, Type>::key),
|
||||||
Map.Entry<Type, Type>::value,
|
|
||||||
Map.Entry<Type, Type>::key
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ASM type for [Expression].
|
* ASM type for [Expression].
|
||||||
*/
|
*/
|
||||||
internal val EXPRESSION_TYPE: Type by lazy { Type.getObjectType("kscience/kmath/expressions/Expression") }
|
val EXPRESSION_TYPE: Type by lazy { getObjectType("kscience/kmath/expressions/Expression") }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ASM type for [java.lang.Number].
|
* ASM type for [java.lang.Number].
|
||||||
*/
|
*/
|
||||||
internal val NUMBER_TYPE: Type by lazy { Type.getObjectType("java/lang/Number") }
|
val NUMBER_TYPE: Type by lazy { getObjectType("java/lang/Number") }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ASM type for [java.util.Map].
|
* ASM type for [java.util.Map].
|
||||||
*/
|
*/
|
||||||
internal val MAP_TYPE: Type by lazy { Type.getObjectType("java/util/Map") }
|
val MAP_TYPE: Type by lazy { getObjectType("java/util/Map") }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ASM type for [java.lang.Object].
|
* ASM type for [java.lang.Object].
|
||||||
*/
|
*/
|
||||||
internal val OBJECT_TYPE: Type by lazy { Type.getObjectType("java/lang/Object") }
|
val OBJECT_TYPE: Type by lazy { getObjectType("java/lang/Object") }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ASM type for array of [java.lang.Object].
|
* ASM type for array of [java.lang.Object].
|
||||||
*/
|
*/
|
||||||
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "RemoveRedundantQualifierName")
|
val OBJECT_ARRAY_TYPE: Type by lazy { getType("[Ljava/lang/Object;") }
|
||||||
internal val OBJECT_ARRAY_TYPE: Type by lazy { Type.getType("[Ljava/lang/Object;") }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ASM type for [Algebra].
|
* ASM type for [Algebra].
|
||||||
*/
|
*/
|
||||||
internal val ALGEBRA_TYPE: Type by lazy { Type.getObjectType("kscience/kmath/operations/Algebra") }
|
val ALGEBRA_TYPE: Type by lazy { getObjectType("kscience/kmath/operations/Algebra") }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ASM type for [java.lang.String].
|
* ASM type for [java.lang.String].
|
||||||
*/
|
*/
|
||||||
internal val STRING_TYPE: Type by lazy { Type.getObjectType("java/lang/String") }
|
val STRING_TYPE: Type by lazy { getObjectType("java/lang/String") }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ASM type for MapIntrinsics.
|
* ASM type for MapIntrinsics.
|
||||||
*/
|
*/
|
||||||
internal val MAP_INTRINSICS_TYPE: Type by lazy { Type.getObjectType("kscience/kmath/asm/internal/MapIntrinsics") }
|
val MAP_INTRINSICS_TYPE: Type by lazy { getObjectType("kscience/kmath/asm/internal/MapIntrinsics") }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,5 +91,3 @@ internal inline fun ClassWriter.visitField(
|
|||||||
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
|
||||||
return visitField(access, name, descriptor, signature, value).apply(block)
|
return visitField(access, name, descriptor, signature, value).apply(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user