Refactor ASM builder

This commit is contained in:
Iaroslav Postovalov 2020-12-08 16:16:32 +07:00
parent f001389f4c
commit cc45e3683b
No known key found for this signature in database
GPG Key ID: 46E15E4A31B3BCD7
2 changed files with 53 additions and 78 deletions

View File

@ -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") }
} }
} }

View File

@ -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)
} }