diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt index 536d6136d..a291ba4ee 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/AsmBuilder.kt @@ -18,6 +18,7 @@ import kotlin.reflect.KClass * @param T the type of AsmExpression to unwrap. * @param algebra the algebra the applied AsmExpressions use. * @param className the unique class name of new loaded class. + * @param invokeLabel0Visitor the function to apply to this object when generating invoke method, label 0. */ internal class AsmBuilder internal constructor( private val classOfT: KClass<*>, @@ -37,8 +38,19 @@ internal class AsmBuilder internal constructor( */ private val classLoader: ClassLoader = ClassLoader(javaClass.classLoader) + /** + * ASM Type for [algebra] + */ private val tAlgebraType: Type = algebra::class.asm + + /** + * ASM type for [T] + */ internal val tType: Type = classOfT.asm + + /** + * ASM type for new class + */ private val classType: Type = Type.getObjectType(className.replace(oldChar = '.', newChar = '/'))!! /** @@ -60,14 +72,30 @@ internal class AsmBuilder internal constructor( * Method visitor of `invoke` method of the subclass. */ private lateinit var invokeMethodVisitor: InstructionAdapter + + /** + * State if [T] a primitive type, so [AsmBuilder] may generate direct primitive calls. + */ internal var primitiveMode = false - @Suppress("PropertyName") - internal var PRIMITIVE_MASK: Type = OBJECT_TYPE + /** + * Primitive type to apple for specific primitive calls. Use [OBJECT_TYPE], if not in [primitiveMode]. + */ + internal var primitiveMask: Type = OBJECT_TYPE - @Suppress("PropertyName") - internal var PRIMITIVE_MASK_BOXED: Type = OBJECT_TYPE + /** + * Boxed primitive type to apple for specific primitive calls. Use [OBJECT_TYPE], if not in [primitiveMode]. + */ + internal var primitiveMaskBoxed: Type = OBJECT_TYPE + + /** + * Stack of useful objects types on stack to verify types. + */ private val typeStack = Stack() + + /** + * Stack of useful objects types on stack expected by algebra calls. + */ internal val expectationStack: Stack = Stack().apply { push(tType) } /** @@ -86,8 +114,8 @@ internal class AsmBuilder internal constructor( if (SIGNATURE_LETTERS.containsKey(classOfT)) { primitiveMode = true - PRIMITIVE_MASK = SIGNATURE_LETTERS.getValue(classOfT) - PRIMITIVE_MASK_BOXED = tType + primitiveMask = SIGNATURE_LETTERS.getValue(classOfT) + primitiveMaskBoxed = tType } val classWriter = ClassWriter(ClassWriter.COMPUTE_FRAMES) { @@ -238,34 +266,43 @@ internal class AsmBuilder internal constructor( } /** - * Loads a constant from + * Loads a [T] constant from [constants]. */ internal fun loadTConstant(value: T) { if (classOfT in INLINABLE_NUMBERS) { val expectedType = expectationStack.pop()!! val mustBeBoxed = expectedType.sort == Type.OBJECT loadNumberConstant(value as Number, mustBeBoxed) - if (mustBeBoxed) typeStack.push(tType) else typeStack.push(PRIMITIVE_MASK) + if (mustBeBoxed) typeStack.push(tType) else typeStack.push(primitiveMask) return } loadConstant(value as Any, tType) } + /** + * Boxes the current value and pushes it. + */ private fun box(): Unit = invokeMethodVisitor.invokestatic( tType.internalName, "valueOf", - Type.getMethodDescriptor(tType, PRIMITIVE_MASK), + Type.getMethodDescriptor(tType, primitiveMask), false ) + /** + * Unboxes the current boxed value and pushes it. + */ private fun unbox(): Unit = invokeMethodVisitor.invokevirtual( NUMBER_TYPE.internalName, - NUMBER_CONVERTER_METHODS.getValue(PRIMITIVE_MASK), - Type.getMethodDescriptor(PRIMITIVE_MASK), + NUMBER_CONVERTER_METHODS.getValue(primitiveMask), + Type.getMethodDescriptor(primitiveMask), false ) + /** + * Loads [java.lang.Object] constant from constants. + */ private fun loadConstant(value: Any, type: Type): Unit = invokeMethodVisitor.run { val idx = if (value in constants) constants.indexOf(value) else constants.apply { add(value) }.lastIndex loadThis() @@ -275,6 +312,9 @@ internal class AsmBuilder internal constructor( checkcast(type) } + /** + * Loads this variable. + */ private fun loadThis(): Unit = invokeMethodVisitor.load(invokeThisVar, classType) /** @@ -344,7 +384,7 @@ internal class AsmBuilder internal constructor( typeStack.push(tType) else { unbox() - typeStack.push(PRIMITIVE_MASK) + typeStack.push(primitiveMask) } } @@ -393,7 +433,7 @@ internal class AsmBuilder internal constructor( typeStack.push(tType) else { unbox() - typeStack.push(PRIMITIVE_MASK) + typeStack.push(primitiveMask) } } @@ -404,7 +444,7 @@ internal class AsmBuilder internal constructor( internal companion object { /** - * Maps JVM primitive numbers boxed types to their letters of JVM signature convention. + * Maps JVM primitive numbers boxed types to their primitive ASM types. */ private val SIGNATURE_LETTERS: Map, Type> by lazy { hashMapOf( @@ -417,8 +457,14 @@ internal class AsmBuilder internal constructor( ) } + /** + * Maps JVM primitive numbers boxed ASM types to their primitive ASM types. + */ private val BOXED_TO_PRIMITIVES: Map by lazy { SIGNATURE_LETTERS.mapKeys { (k, _) -> k.asm } } + /** + * Maps primitive ASM types to [Number] functions unboxing them. + */ private val NUMBER_CONVERTER_METHODS: Map by lazy { hashMapOf( Type.BYTE_TYPE to "byteValue", @@ -434,14 +480,41 @@ internal class AsmBuilder internal constructor( * Provides boxed number types values of which can be stored in JVM bytecode constant pool. */ private val INLINABLE_NUMBERS: Set> by lazy { SIGNATURE_LETTERS.keys } + + /** + * ASM type for [Expression]. + */ internal val EXPRESSION_TYPE: Type by lazy { Expression::class.asm } + + /** + * ASM type for [java.lang.Number]. + */ internal val NUMBER_TYPE: Type by lazy { java.lang.Number::class.asm } + + /** + * ASM type for [java.util.Map]. + */ internal val MAP_TYPE: Type by lazy { java.util.Map::class.asm } + + /** + * ASM type for [java.lang.Object]. + */ internal val OBJECT_TYPE: Type by lazy { java.lang.Object::class.asm } + /** + * ASM type for array of [java.lang.Object]. + */ @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "RemoveRedundantQualifierName") internal val OBJECT_ARRAY_TYPE: Type by lazy { Array::class.asm } + + /** + * ASM type for [Algebra]. + */ internal val ALGEBRA_TYPE: Type by lazy { Algebra::class.asm } + + /** + * ASM type for [java.lang.String]. + */ internal val STRING_TYPE: Type by lazy { java.lang.String::class.asm } } } diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt index af5c1049d..00093aaa7 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/classWriters.kt @@ -2,7 +2,6 @@ package scientifik.kmath.asm.internal import org.objectweb.asm.ClassWriter import org.objectweb.asm.FieldVisitor -import org.objectweb.asm.MethodVisitor internal inline fun ClassWriter(flags: Int, block: ClassWriter.() -> Unit): ClassWriter = ClassWriter(flags).apply(block) diff --git a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt index 4c7a0d57e..252509d59 100644 --- a/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt +++ b/kmath-ast/src/jvmMain/kotlin/scientifik/kmath/asm/internal/specialization.kt @@ -22,7 +22,7 @@ internal fun AsmBuilder.buildExpectationStack(context: Algebra, name: val aName = methodNameAdapters[name] ?: name val hasSpecific = context.javaClass.methods.find { it.name == aName && it.parameters.size == arity } != null - val t = if (primitiveMode && hasSpecific) PRIMITIVE_MASK else tType + val t = if (primitiveMode && hasSpecific) primitiveMask else tType repeat(arity) { expectationStack.push(t) } return hasSpecific @@ -52,7 +52,7 @@ internal fun AsmBuilder.tryInvokeSpecific(context: Algebra, name: Stri invokeAlgebraOperation( owner = owner, method = aName, - descriptor = Type.getMethodDescriptor(PRIMITIVE_MASK_BOXED, *Array(arity) { PRIMITIVE_MASK }), + descriptor = Type.getMethodDescriptor(primitiveMaskBoxed, *Array(arity) { primitiveMask }), tArity = arity, opcode = Opcodes.INVOKEVIRTUAL )