Provide dynamic operations currying for Algebra<T> instead of eager calls and add JS code generation support #162
@ -6,6 +6,7 @@ import kscience.kmath.expressions.Expression
|
||||
import kscience.kmath.operations.Algebra
|
||||
import org.objectweb.asm.*
|
||||
import org.objectweb.asm.Opcodes.*
|
||||
import org.objectweb.asm.Type.*
|
||||
import org.objectweb.asm.commons.InstructionAdapter
|
||||
import java.util.stream.Collectors.toMap
|
||||
import kotlin.contracts.InvocationKind
|
||||
@ -17,13 +18,13 @@ import kotlin.contracts.contract
|
||||
*
|
||||
* @property T the type of AsmExpression to unwrap.
|
||||
* @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
|
||||
*/
|
||||
internal class AsmBuilder<T> internal constructor(
|
||||
internal class AsmBuilder<T>(
|
||||
classOfT: Class<*>,
|
||||
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.
|
||||
@ -45,7 +46,7 @@ internal class AsmBuilder<T> internal constructor(
|
||||
/**
|
||||
* 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.
|
||||
@ -57,11 +58,6 @@ internal class AsmBuilder<T> internal constructor(
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@ -69,6 +65,8 @@ internal class AsmBuilder<T> internal constructor(
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val instance: Expression<T> by lazy {
|
||||
val hasConstants: Boolean
|
||||
|
||||
val classWriter = ClassWriter(ClassWriter.COMPUTE_FRAMES) {
|
||||
visit(
|
||||
V1_8,
|
||||
@ -82,14 +80,14 @@ internal class AsmBuilder<T> internal constructor(
|
||||
visitMethod(
|
||||
ACC_PUBLIC or ACC_FINAL,
|
||||
"invoke",
|
||||
Type.getMethodDescriptor(tType, MAP_TYPE),
|
||||
getMethodDescriptor(tType, MAP_TYPE),
|
||||
"(L${MAP_TYPE.internalName}<${STRING_TYPE.descriptor}+${tType.descriptor}>;)${tType.descriptor}",
|
||||
null
|
||||
).instructionAdapter {
|
||||
invokeMethodVisitor = this
|
||||
visitCode()
|
||||
val l0 = label()
|
||||
invokeLabel0Visitor()
|
||||
callbackAtInvokeL0()
|
||||
areturn(tType)
|
||||
val l1 = label()
|
||||
|
||||
@ -99,7 +97,7 @@ internal class AsmBuilder<T> internal constructor(
|
||||
null,
|
||||
l0,
|
||||
l1,
|
||||
invokeThisVar
|
||||
0
|
||||
)
|
||||
|
||||
visitLocalVariable(
|
||||
@ -108,7 +106,7 @@ internal class AsmBuilder<T> internal constructor(
|
||||
"L${MAP_TYPE.internalName}<${STRING_TYPE.descriptor}+${tType.descriptor}>;",
|
||||
l0,
|
||||
l1,
|
||||
invokeArgumentsVar
|
||||
1
|
||||
)
|
||||
|
||||
visitMaxs(0, 2)
|
||||
@ -118,7 +116,7 @@ internal class AsmBuilder<T> internal constructor(
|
||||
visitMethod(
|
||||
ACC_PUBLIC or ACC_FINAL or ACC_BRIDGE or ACC_SYNTHETIC,
|
||||
"invoke",
|
||||
Type.getMethodDescriptor(OBJECT_TYPE, MAP_TYPE),
|
||||
getMethodDescriptor(OBJECT_TYPE, MAP_TYPE),
|
||||
null,
|
||||
null
|
||||
).instructionAdapter {
|
||||
@ -128,7 +126,7 @@ internal class AsmBuilder<T> internal constructor(
|
||||
val l0 = label()
|
||||
load(thisVar, OBJECT_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)
|
||||
val l1 = label()
|
||||
|
||||
@ -160,32 +158,30 @@ internal class AsmBuilder<T> internal constructor(
|
||||
visitMethod(
|
||||
ACC_PUBLIC,
|
||||
"<init>",
|
||||
Type.getMethodDescriptor(Type.VOID_TYPE, *OBJECT_ARRAY_TYPE.wrapToArrayIf { hasConstants }),
|
||||
getMethodDescriptor(VOID_TYPE, *OBJECT_ARRAY_TYPE.wrapToArrayIf { hasConstants }),
|
||||
null,
|
||||
null
|
||||
).instructionAdapter {
|
||||
val thisVar = 0
|
||||
val constantsVar = 1
|
||||
val l0 = label()
|
||||
load(thisVar, classType)
|
||||
invokespecial(OBJECT_TYPE.internalName, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE), false)
|
||||
load(0, classType)
|
||||
invokespecial(OBJECT_TYPE.internalName, "<init>", getMethodDescriptor(VOID_TYPE), false)
|
||||
label()
|
||||
load(thisVar, classType)
|
||||
load(0, classType)
|
||||
|
||||
if (hasConstants) {
|
||||
label()
|
||||
load(thisVar, classType)
|
||||
load(constantsVar, OBJECT_ARRAY_TYPE)
|
||||
load(0, classType)
|
||||
load(1, OBJECT_ARRAY_TYPE)
|
||||
putfield(classType.internalName, "constants", OBJECT_ARRAY_TYPE.descriptor)
|
||||
}
|
||||
|
||||
label()
|
||||
visitInsn(RETURN)
|
||||
val l4 = label()
|
||||
visitLocalVariable("this", classType.descriptor, null, l0, l4, thisVar)
|
||||
visitLocalVariable("this", classType.descriptor, null, l0, l4, 0)
|
||||
|
||||
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)
|
||||
visitEnd()
|
||||
@ -218,7 +214,7 @@ internal class AsmBuilder<T> internal constructor(
|
||||
/**
|
||||
* 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
|
||||
@ -230,12 +226,12 @@ internal class AsmBuilder<T> internal constructor(
|
||||
|
||||
if (primitive != null) {
|
||||
when (primitive) {
|
||||
Type.BYTE_TYPE -> invokeMethodVisitor.iconst(value.toInt())
|
||||
Type.DOUBLE_TYPE -> invokeMethodVisitor.dconst(value.toDouble())
|
||||
Type.FLOAT_TYPE -> invokeMethodVisitor.fconst(value.toFloat())
|
||||
Type.LONG_TYPE -> invokeMethodVisitor.lconst(value.toLong())
|
||||
Type.INT_TYPE -> invokeMethodVisitor.iconst(value.toInt())
|
||||
Type.SHORT_TYPE -> invokeMethodVisitor.iconst(value.toInt())
|
||||
BYTE_TYPE -> invokeMethodVisitor.iconst(value.toInt())
|
||||
DOUBLE_TYPE -> invokeMethodVisitor.dconst(value.toDouble())
|
||||
FLOAT_TYPE -> invokeMethodVisitor.fconst(value.toFloat())
|
||||
LONG_TYPE -> invokeMethodVisitor.lconst(value.toLong())
|
||||
INT_TYPE -> invokeMethodVisitor.iconst(value.toInt())
|
||||
SHORT_TYPE -> invokeMethodVisitor.iconst(value.toInt())
|
||||
}
|
||||
|
||||
box(primitive)
|
||||
@ -254,7 +250,7 @@ internal class AsmBuilder<T> internal constructor(
|
||||
invokeMethodVisitor.invokestatic(
|
||||
r.internalName,
|
||||
"valueOf",
|
||||
Type.getMethodDescriptor(r, primitive),
|
||||
getMethodDescriptor(r, primitive),
|
||||
false
|
||||
)
|
||||
}
|
||||
@ -263,13 +259,13 @@ internal class AsmBuilder<T> internal constructor(
|
||||
* Loads a variable [name] from arguments [Map] parameter of [Expression.invoke].
|
||||
*/
|
||||
fun loadVariable(name: String): Unit = invokeMethodVisitor.run {
|
||||
load(invokeArgumentsVar, MAP_TYPE)
|
||||
load(1, MAP_TYPE)
|
||||
aconst(name)
|
||||
|
||||
invokestatic(
|
||||
MAP_INTRINSICS_TYPE.internalName,
|
||||
"getOrFail",
|
||||
Type.getMethodDescriptor(OBJECT_TYPE, MAP_TYPE, STRING_TYPE),
|
||||
getMethodDescriptor(OBJECT_TYPE, MAP_TYPE, STRING_TYPE),
|
||||
false
|
||||
)
|
||||
|
||||
@ -283,100 +279,81 @@ internal class AsmBuilder<T> internal constructor(
|
||||
val arity = `interface`.methods.find { it.name == "invoke" }?.parameterCount
|
||||
?: error("Provided function object doesn't contain invoke method")
|
||||
|
||||
val type = Type.getType(`interface`)
|
||||
val type = getType(`interface`)
|
||||
loadObjectConstant(function, type)
|
||||
parameters(this)
|
||||
|
||||
invokeMethodVisitor.invokeinterface(
|
||||
type.internalName,
|
||||
"invoke",
|
||||
Type.getMethodDescriptor(OBJECT_TYPE, *Array(arity) { OBJECT_TYPE}),
|
||||
getMethodDescriptor(OBJECT_TYPE, *Array(arity) { OBJECT_TYPE }),
|
||||
)
|
||||
|
||||
invokeMethodVisitor.checkcast(tType)
|
||||
}
|
||||
|
||||
internal 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
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
private val PRIMITIVES_TO_BOXED: Map<Type, Type> by lazy {
|
||||
BOXED_TO_PRIMITIVES.entries.stream().collect(
|
||||
toMap(
|
||||
Map.Entry<Type, Type>::value,
|
||||
Map.Entry<Type, Type>::key
|
||||
)
|
||||
toMap(Map.Entry<Type, Type>::value, Map.Entry<Type, Type>::key),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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].
|
||||
*/
|
||||
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].
|
||||
*/
|
||||
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].
|
||||
*/
|
||||
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].
|
||||
*/
|
||||
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "RemoveRedundantQualifierName")
|
||||
internal val OBJECT_ARRAY_TYPE: Type by lazy { Type.getType("[Ljava/lang/Object;") }
|
||||
val OBJECT_ARRAY_TYPE: Type by lazy { getType("[Ljava/lang/Object;") }
|
||||
|
||||
/**
|
||||
* 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].
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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) }
|
||||
return visitField(access, name, descriptor, signature, value).apply(block)
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user