Merge branch 'adv-expr' into adv-expr-improved-trigonometry

This commit is contained in:
Commander Tvis 2020-06-15 21:02:58 +07:00
commit c10901ae98
No known key found for this signature in database
GPG Key ID: 70D5F4DCB0972F1B
11 changed files with 231 additions and 216 deletions

4
kmath-ast/README.md Normal file
View File

@ -0,0 +1,4 @@
# AST based expression representation and operations
## Dynamic expression code generation
Contributed by [Iaroslav Postovalov](https://github.com/CommanderTvis).

View File

@ -11,7 +11,7 @@ sealed class MST {
/**
* A node containing unparsed string
*/
data class Singular(val value: String) : MST()
data class Symbolic(val value: String) : MST()
/**
* A node containing a number
@ -43,7 +43,7 @@ sealed class MST {
fun <T> NumericAlgebra<T>.evaluate(node: MST): T {
return when (node) {
is MST.Numeric -> number(node.value)
is MST.Singular -> symbol(node.value)
is MST.Symbolic -> symbol(node.value)
is MST.Unary -> unaryOperation(node.operation, evaluate(node.value))
is MST.Binary -> when {
node.left is MST.Numeric && node.right is MST.Numeric -> {

View File

@ -1,56 +0,0 @@
package scientifik.kmath.asm
import scientifik.kmath.asm.internal.AsmGenerationContext
import scientifik.kmath.ast.MST
import scientifik.kmath.ast.evaluate
import scientifik.kmath.expressions.Expression
import scientifik.kmath.operations.*
@PublishedApi
internal fun buildName(expression: AsmNode<*>, collision: Int = 0): String {
val name = "scientifik.kmath.expressions.generated.AsmCompiledExpression_${expression.hashCode()}_$collision"
try {
Class.forName(name)
} catch (ignored: ClassNotFoundException) {
return name
}
return buildName(expression, collision + 1)
}
@PublishedApi
internal inline fun <reified T> AsmNode<T>.compile(algebra: Algebra<T>): Expression<T> {
val ctx =
AsmGenerationContext(T::class.java, algebra, buildName(this))
compile(ctx)
return ctx.generate()
}
inline fun <reified T, A : NumericAlgebra<T>, E : AsmExpressionAlgebra<T, A>> A.asm(
expressionAlgebra: E,
block: E.() -> AsmNode<T>
): Expression<T> = expressionAlgebra.block().compile(expressionAlgebra.algebra)
inline fun <reified T, A : NumericAlgebra<T>, E : AsmExpressionAlgebra<T, A>> A.asm(
expressionAlgebra: E,
ast: MST
): Expression<T> = asm(expressionAlgebra) { evaluate(ast) }
inline fun <reified T, A> A.asmSpace(block: AsmExpressionSpace<T, A>.() -> AsmNode<T>): Expression<T> where A : NumericAlgebra<T>, A : Space<T> =
AsmExpressionSpace(this).let { it.block().compile(it.algebra) }
inline fun <reified T, A> A.asmSpace(ast: MST): Expression<T> where A : NumericAlgebra<T>, A : Space<T> =
asmSpace { evaluate(ast) }
inline fun <reified T, A> A.asmRing(block: AsmExpressionRing<T, A>.() -> AsmNode<T>): Expression<T> where A : NumericAlgebra<T>, A : Ring<T> =
AsmExpressionRing(this).let { it.block().compile(it.algebra) }
inline fun <reified T, A> A.asmRing(ast: MST): Expression<T> where A : NumericAlgebra<T>, A : Ring<T> =
asmRing { evaluate(ast) }
inline fun <reified T, A> A.asmField(block: AsmExpressionField<T, A>.() -> AsmNode<T>): Expression<T> where A : NumericAlgebra<T>, A : Field<T> =
AsmExpressionField(this).let { it.block().compile(it.algebra) }
inline fun <reified T, A> A.asmField(ast: MST): Expression<T> where A : NumericAlgebra<T>, A : Field<T> =
asmRing { evaluate(ast) }

View File

@ -1,19 +1,24 @@
package scientifik.kmath.asm
import scientifik.kmath.asm.internal.AsmGenerationContext
import scientifik.kmath.asm.internal.AsmBuilder
import scientifik.kmath.asm.internal.hasSpecific
import scientifik.kmath.asm.internal.optimize
import scientifik.kmath.asm.internal.tryInvokeSpecific
import scientifik.kmath.expressions.Expression
import scientifik.kmath.expressions.ExpressionAlgebra
import scientifik.kmath.operations.*
import kotlin.reflect.KClass
/**
* A function declaration that could be compiled to [AsmGenerationContext].
* A function declaration that could be compiled to [AsmBuilder].
*
* @param T the type the stored function returns.
*/
abstract class AsmNode<T> internal constructor() {
sealed class AsmExpression<T : Any>: Expression<T> {
abstract val type: KClass<out T>
abstract val algebra: Algebra<T>
/**
* Tries to evaluate this function without its variables. This method is intended for optimization.
*
@ -24,118 +29,144 @@ abstract class AsmNode<T> internal constructor() {
/**
* Compiles this declaration.
*
* @param gen the target [AsmGenerationContext].
* @param gen the target [AsmBuilder].
*/
@PublishedApi
internal abstract fun compile(gen: AsmGenerationContext<T>)
internal abstract fun appendTo(gen: AsmBuilder<T>)
/**
* Compile and cache the expression
*/
private val compiledExpression by lazy{
val builder = AsmBuilder(type.java, algebra, buildName(this))
this.appendTo(builder)
builder.generate()
}
internal class AsmUnaryOperation<T>(private val context: Algebra<T>, private val name: String, expr: AsmNode<T>) :
AsmNode<T>() {
private val expr: AsmNode<T> = expr.optimize()
override fun tryEvaluate(): T? = context { unaryOperation(name, expr.tryEvaluate() ?: return@context null) }
override fun invoke(arguments: Map<String, T>): T = compiledExpression.invoke(arguments)
}
override fun compile(gen: AsmGenerationContext<T>) {
internal class AsmUnaryOperation<T : Any>(
override val type: KClass<out T>,
override val algebra: Algebra<T>,
private val name: String,
expr: AsmExpression<T>
) : AsmExpression<T>() {
private val expr: AsmExpression<T> = expr.optimize()
override fun tryEvaluate(): T? = algebra { unaryOperation(name, expr.tryEvaluate() ?: return@algebra null) }
override fun appendTo(gen: AsmBuilder<T>) {
gen.visitLoadAlgebra()
if (!hasSpecific(context, name, 1))
if (!hasSpecific(algebra, name, 1))
gen.visitStringConstant(name)
expr.compile(gen)
expr.appendTo(gen)
if (gen.tryInvokeSpecific(context, name, 1))
if (gen.tryInvokeSpecific(algebra, name, 1))
return
gen.visitAlgebraOperation(
owner = AsmGenerationContext.ALGEBRA_CLASS,
owner = AsmBuilder.ALGEBRA_CLASS,
method = "unaryOperation",
descriptor = "(L${AsmGenerationContext.STRING_CLASS};" +
"L${AsmGenerationContext.OBJECT_CLASS};)" +
"L${AsmGenerationContext.OBJECT_CLASS};"
descriptor = "(L${AsmBuilder.STRING_CLASS};" +
"L${AsmBuilder.OBJECT_CLASS};)" +
"L${AsmBuilder.OBJECT_CLASS};"
)
}
}
internal class AsmBinaryOperation<T>(
private val context: Algebra<T>,
internal class AsmBinaryOperation<T : Any>(
override val type: KClass<out T>,
override val algebra: Algebra<T>,
private val name: String,
first: AsmNode<T>,
second: AsmNode<T>
) : AsmNode<T>() {
private val first: AsmNode<T> = first.optimize()
private val second: AsmNode<T> = second.optimize()
first: AsmExpression<T>,
second: AsmExpression<T>
) : AsmExpression<T>() {
private val first: AsmExpression<T> = first.optimize()
private val second: AsmExpression<T> = second.optimize()
override fun tryEvaluate(): T? = context {
override fun tryEvaluate(): T? = algebra {
binaryOperation(
name,
first.tryEvaluate() ?: return@context null,
second.tryEvaluate() ?: return@context null
first.tryEvaluate() ?: return@algebra null,
second.tryEvaluate() ?: return@algebra null
)
}
override fun compile(gen: AsmGenerationContext<T>) {
override fun appendTo(gen: AsmBuilder<T>) {
gen.visitLoadAlgebra()
if (!hasSpecific(context, name, 2))
if (!hasSpecific(algebra, name, 2))
gen.visitStringConstant(name)
first.compile(gen)
second.compile(gen)
first.appendTo(gen)
second.appendTo(gen)
if (gen.tryInvokeSpecific(context, name, 2))
if (gen.tryInvokeSpecific(algebra, name, 2))
return
gen.visitAlgebraOperation(
owner = AsmGenerationContext.ALGEBRA_CLASS,
owner = AsmBuilder.ALGEBRA_CLASS,
method = "binaryOperation",
descriptor = "(L${AsmGenerationContext.STRING_CLASS};" +
"L${AsmGenerationContext.OBJECT_CLASS};" +
"L${AsmGenerationContext.OBJECT_CLASS};)" +
"L${AsmGenerationContext.OBJECT_CLASS};"
descriptor = "(L${AsmBuilder.STRING_CLASS};" +
"L${AsmBuilder.OBJECT_CLASS};" +
"L${AsmBuilder.OBJECT_CLASS};)" +
"L${AsmBuilder.OBJECT_CLASS};"
)
}
}
internal class AsmVariableExpression<T>(private val name: String, private val default: T? = null) :
AsmNode<T>() {
override fun compile(gen: AsmGenerationContext<T>): Unit = gen.visitLoadFromVariables(name, default)
internal class AsmVariableExpression<T : Any>(
override val type: KClass<out T>,
override val algebra: Algebra<T>,
private val name: String,
private val default: T? = null
) : AsmExpression<T>() {
override fun appendTo(gen: AsmBuilder<T>): Unit = gen.visitLoadFromVariables(name, default)
}
internal class AsmConstantExpression<T>(private val value: T) :
AsmNode<T>() {
internal class AsmConstantExpression<T : Any>(
override val type: KClass<out T>,
override val algebra: Algebra<T>,
private val value: T
) : AsmExpression<T>() {
override fun tryEvaluate(): T = value
override fun compile(gen: AsmGenerationContext<T>): Unit = gen.visitLoadFromConstants(value)
override fun appendTo(gen: AsmBuilder<T>): Unit = gen.visitLoadFromConstants(value)
}
internal class AsmConstProductExpression<T>(
private val context: Space<T>,
expr: AsmNode<T>,
internal class AsmConstProductExpression<T : Any>(
override val type: KClass<out T>,
override val algebra: Space<T>,
expr: AsmExpression<T>,
private val const: Number
) : AsmNode<T>() {
private val expr: AsmNode<T> = expr.optimize()
) : AsmExpression<T>() {
private val expr: AsmExpression<T> = expr.optimize()
override fun tryEvaluate(): T? = context { (expr.tryEvaluate() ?: return@context null) * const }
override fun tryEvaluate(): T? = algebra { (expr.tryEvaluate() ?: return@algebra null) * const }
override fun compile(gen: AsmGenerationContext<T>) {
override fun appendTo(gen: AsmBuilder<T>) {
gen.visitLoadAlgebra()
gen.visitNumberConstant(const)
expr.compile(gen)
expr.appendTo(gen)
gen.visitAlgebraOperation(
owner = AsmGenerationContext.SPACE_OPERATIONS_CLASS,
owner = AsmBuilder.SPACE_OPERATIONS_CLASS,
method = "multiply",
descriptor = "(L${AsmGenerationContext.OBJECT_CLASS};" +
"L${AsmGenerationContext.NUMBER_CLASS};)" +
"L${AsmGenerationContext.OBJECT_CLASS};"
descriptor = "(L${AsmBuilder.OBJECT_CLASS};" +
"L${AsmBuilder.NUMBER_CLASS};)" +
"L${AsmBuilder.OBJECT_CLASS};"
)
}
}
internal class AsmNumberExpression<T>(private val context: NumericAlgebra<T>, private val value: Number) :
AsmNode<T>() {
override fun tryEvaluate(): T? = context.number(value)
internal class AsmNumberExpression<T : Any>(
override val type: KClass<out T>,
override val algebra: NumericAlgebra<T>,
private val value: Number
) : AsmExpression<T>() {
override fun tryEvaluate(): T? = algebra.number(value)
override fun compile(gen: AsmGenerationContext<T>): Unit = gen.visitNumberConstant(value)
override fun appendTo(gen: AsmBuilder<T>): Unit = gen.visitNumberConstant(value)
}
internal abstract class FunctionalCompiledExpression<T> internal constructor(
@ -146,120 +177,116 @@ internal abstract class FunctionalCompiledExpression<T> internal constructor(
}
/**
* A context class for [AsmNode] construction.
* A context class for [AsmExpression] construction.
*
* @param algebra The algebra to provide for AsmExpressions built.
*/
interface AsmExpressionAlgebra<T, A : NumericAlgebra<T>> : NumericAlgebra<AsmNode<T>>,
ExpressionAlgebra<T, AsmNode<T>> {
/**
* The algebra to provide for AsmExpressions built.
*/
val algebra: A
open class AsmExpressionAlgebra<T : Any, A : NumericAlgebra<T>>(val type: KClass<out T>, val algebra: A) :
NumericAlgebra<AsmExpression<T>>, ExpressionAlgebra<T, AsmExpression<T>> {
/**
* Builds an AsmExpression to wrap a number.
*/
override fun number(value: Number): AsmNode<T> = AsmNumberExpression(algebra, value)
override fun number(value: Number): AsmExpression<T> = AsmNumberExpression(type, algebra, value)
/**
* Builds an AsmExpression of constant expression which does not depend on arguments.
*/
override fun const(value: T): AsmNode<T> = AsmConstantExpression(value)
override fun const(value: T): AsmExpression<T> = AsmConstantExpression(type, algebra, value)
/**
* Builds an AsmExpression to access a variable.
*/
override fun variable(name: String, default: T?): AsmNode<T> = AsmVariableExpression(name, default)
override fun variable(name: String, default: T?): AsmExpression<T> = AsmVariableExpression(type, algebra, name, default)
/**
* Builds an AsmExpression of dynamic call of binary operation [operation] on [left] and [right].
*/
override fun binaryOperation(operation: String, left: AsmNode<T>, right: AsmNode<T>): AsmNode<T> =
AsmBinaryOperation(algebra, operation, left, right)
override fun binaryOperation(operation: String, left: AsmExpression<T>, right: AsmExpression<T>): AsmExpression<T> =
AsmBinaryOperation(type, algebra, operation, left, right)
/**
* Builds an AsmExpression of dynamic call of unary operation with name [operation] on [arg].
*/
override fun unaryOperation(operation: String, arg: AsmNode<T>): AsmNode<T> =
AsmUnaryOperation(algebra, operation, arg)
override fun unaryOperation(operation: String, arg: AsmExpression<T>): AsmExpression<T> =
AsmUnaryOperation(type, algebra, operation, arg)
}
/**
* A context class for [AsmNode] construction for [Space] algebras.
* A context class for [AsmExpression] construction for [Space] algebras.
*/
open class AsmExpressionSpace<T, A>(override val algebra: A) : AsmExpressionAlgebra<T, A>,
Space<AsmNode<T>> where A : Space<T>, A : NumericAlgebra<T> {
override val zero: AsmNode<T>
get() = const(algebra.zero)
open class AsmExpressionSpace<T : Any, A>(type: KClass<out T>, algebra: A) : AsmExpressionAlgebra<T, A>(type, algebra),
Space<AsmExpression<T>> where A : Space<T>, A : NumericAlgebra<T> {
override val zero: AsmExpression<T> get() = const(algebra.zero)
/**
* Builds an AsmExpression of addition of two another expressions.
*/
override fun add(a: AsmNode<T>, b: AsmNode<T>): AsmNode<T> =
AsmBinaryOperation(algebra, SpaceOperations.PLUS_OPERATION, a, b)
override fun add(a: AsmExpression<T>, b: AsmExpression<T>): AsmExpression<T> =
AsmBinaryOperation(type, algebra, SpaceOperations.PLUS_OPERATION, a, b)
/**
* Builds an AsmExpression of multiplication of expression by number.
*/
override fun multiply(a: AsmNode<T>, k: Number): AsmNode<T> = AsmConstProductExpression(algebra, a, k)
override fun multiply(a: AsmExpression<T>, k: Number): AsmExpression<T> = AsmConstProductExpression(type, algebra, a, k)
operator fun AsmNode<T>.plus(arg: T): AsmNode<T> = this + const(arg)
operator fun AsmNode<T>.minus(arg: T): AsmNode<T> = this - const(arg)
operator fun T.plus(arg: AsmNode<T>): AsmNode<T> = arg + this
operator fun T.minus(arg: AsmNode<T>): AsmNode<T> = arg - this
operator fun AsmExpression<T>.plus(arg: T): AsmExpression<T> = this + const(arg)
operator fun AsmExpression<T>.minus(arg: T): AsmExpression<T> = this - const(arg)
operator fun T.plus(arg: AsmExpression<T>): AsmExpression<T> = arg + this
operator fun T.minus(arg: AsmExpression<T>): AsmExpression<T> = arg - this
override fun unaryOperation(operation: String, arg: AsmNode<T>): AsmNode<T> =
override fun unaryOperation(operation: String, arg: AsmExpression<T>): AsmExpression<T> =
super<AsmExpressionAlgebra>.unaryOperation(operation, arg)
override fun binaryOperation(operation: String, left: AsmNode<T>, right: AsmNode<T>): AsmNode<T> =
override fun binaryOperation(operation: String, left: AsmExpression<T>, right: AsmExpression<T>): AsmExpression<T> =
super<AsmExpressionAlgebra>.binaryOperation(operation, left, right)
}
/**
* A context class for [AsmNode] construction for [Ring] algebras.
* A context class for [AsmExpression] construction for [Ring] algebras.
*/
open class AsmExpressionRing<T, A>(override val algebra: A) : AsmExpressionSpace<T, A>(algebra),
Ring<AsmNode<T>> where A : Ring<T>, A : NumericAlgebra<T> {
override val one: AsmNode<T>
get() = const(algebra.one)
open class AsmExpressionRing<T : Any, A>(type: KClass<out T>, algebra: A) : AsmExpressionSpace<T, A>(type, algebra),
Ring<AsmExpression<T>> where A : Ring<T>, A : NumericAlgebra<T> {
override val one: AsmExpression<T> get() = const(algebra.one)
/**
* Builds an AsmExpression of multiplication of two expressions.
*/
override fun multiply(a: AsmNode<T>, b: AsmNode<T>): AsmNode<T> =
AsmBinaryOperation(algebra, RingOperations.TIMES_OPERATION, a, b)
override fun multiply(a: AsmExpression<T>, b: AsmExpression<T>): AsmExpression<T> =
AsmBinaryOperation(type, algebra, RingOperations.TIMES_OPERATION, a, b)
operator fun AsmNode<T>.times(arg: T): AsmNode<T> = this * const(arg)
operator fun T.times(arg: AsmNode<T>): AsmNode<T> = arg * this
operator fun AsmExpression<T>.times(arg: T): AsmExpression<T> = this * const(arg)
operator fun T.times(arg: AsmExpression<T>): AsmExpression<T> = arg * this
override fun unaryOperation(operation: String, arg: AsmNode<T>): AsmNode<T> =
override fun unaryOperation(operation: String, arg: AsmExpression<T>): AsmExpression<T> =
super<AsmExpressionSpace>.unaryOperation(operation, arg)
override fun binaryOperation(operation: String, left: AsmNode<T>, right: AsmNode<T>): AsmNode<T> =
override fun binaryOperation(operation: String, left: AsmExpression<T>, right: AsmExpression<T>): AsmExpression<T> =
super<AsmExpressionSpace>.binaryOperation(operation, left, right)
override fun number(value: Number): AsmNode<T> = super<AsmExpressionSpace>.number(value)
override fun number(value: Number): AsmExpression<T> = super<AsmExpressionSpace>.number(value)
}
/**
* A context class for [AsmNode] construction for [Field] algebras.
* A context class for [AsmExpression] construction for [Field] algebras.
*/
open class AsmExpressionField<T, A>(override val algebra: A) :
AsmExpressionRing<T, A>(algebra),
Field<AsmNode<T>> where A : Field<T>, A : NumericAlgebra<T> {
open class AsmExpressionField<T : Any, A>(type: KClass<out T>, algebra: A) :
AsmExpressionRing<T, A>(type, algebra),
Field<AsmExpression<T>> where A : Field<T>, A : NumericAlgebra<T> {
/**
* Builds an AsmExpression of division an expression by another one.
*/
override fun divide(a: AsmNode<T>, b: AsmNode<T>): AsmNode<T> =
AsmBinaryOperation(algebra, FieldOperations.DIV_OPERATION, a, b)
override fun divide(a: AsmExpression<T>, b: AsmExpression<T>): AsmExpression<T> =
AsmBinaryOperation(type, algebra, FieldOperations.DIV_OPERATION, a, b)
operator fun AsmNode<T>.div(arg: T): AsmNode<T> = this / const(arg)
operator fun T.div(arg: AsmNode<T>): AsmNode<T> = arg / this
operator fun AsmExpression<T>.div(arg: T): AsmExpression<T> = this / const(arg)
operator fun T.div(arg: AsmExpression<T>): AsmExpression<T> = arg / this
override fun unaryOperation(operation: String, arg: AsmNode<T>): AsmNode<T> =
override fun unaryOperation(operation: String, arg: AsmExpression<T>): AsmExpression<T> =
super<AsmExpressionRing>.unaryOperation(operation, arg)
override fun binaryOperation(operation: String, left: AsmNode<T>, right: AsmNode<T>): AsmNode<T> =
override fun binaryOperation(operation: String, left: AsmExpression<T>, right: AsmExpression<T>): AsmExpression<T> =
super<AsmExpressionRing>.binaryOperation(operation, left, right)
override fun number(value: Number): AsmNode<T> = super<AsmExpressionRing>.number(value)
override fun number(value: Number): AsmExpression<T> = super<AsmExpressionRing>.number(value)
}

View File

@ -0,0 +1,47 @@
package scientifik.kmath.asm
import scientifik.kmath.ast.MST
import scientifik.kmath.ast.evaluate
import scientifik.kmath.expressions.Expression
import scientifik.kmath.operations.Field
import scientifik.kmath.operations.NumericAlgebra
import scientifik.kmath.operations.Ring
import scientifik.kmath.operations.Space
internal fun buildName(expression: AsmExpression<*>, collision: Int = 0): String {
val name = "scientifik.kmath.expressions.generated.AsmCompiledExpression_${expression.hashCode()}_$collision"
try {
Class.forName(name)
} catch (ignored: ClassNotFoundException) {
return name
}
return buildName(expression, collision + 1)
}
inline fun <reified T : Any, A : NumericAlgebra<T>, E : AsmExpressionAlgebra<T, A>> A.asm(
expressionAlgebra: E,
block: E.() -> AsmExpression<T>
): Expression<T> = expressionAlgebra.block()
inline fun <reified T : Any> NumericAlgebra<T>.asm(ast: MST): Expression<T> =
AsmExpressionAlgebra(T::class, this).evaluate(ast)
inline fun <reified T : Any, A> A.asmSpace(block: AsmExpressionSpace<T, A>.() -> AsmExpression<T>): Expression<T> where A : NumericAlgebra<T>, A : Space<T> =
AsmExpressionSpace<T, A>(T::class, this).block()
inline fun <reified T : Any, A> A.asmSpace(ast: MST): Expression<T> where A : NumericAlgebra<T>, A : Space<T> =
asmSpace { evaluate(ast) }
inline fun <reified T : Any, A> A.asmRing(block: AsmExpressionRing<T, A>.() -> AsmExpression<T>): Expression<T> where A : NumericAlgebra<T>, A : Ring<T> =
AsmExpressionRing(T::class, this).block()
inline fun <reified T : Any, A> A.asmRing(ast: MST): Expression<T> where A : NumericAlgebra<T>, A : Ring<T> =
asmRing { evaluate(ast) }
inline fun <reified T : Any, A> A.asmField(block: AsmExpressionField<T, A>.() -> AsmExpression<T>): Expression<T> where A : NumericAlgebra<T>, A : Field<T> =
AsmExpressionField(T::class, this).block()
inline fun <reified T : Any, A> A.asmField(ast: MST): Expression<T> where A : NumericAlgebra<T>, A : Field<T> =
asmRing { evaluate(ast) }

View File

@ -5,30 +5,29 @@ import org.objectweb.asm.Label
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes
import scientifik.kmath.asm.FunctionalCompiledExpression
import scientifik.kmath.asm.internal.AsmGenerationContext.ClassLoader
import scientifik.kmath.asm.internal.AsmBuilder.AsmClassLoader
import scientifik.kmath.operations.Algebra
/**
* AsmGenerationContext is a structure that abstracts building a class that unwraps [AsmNode] to plain Java
* expression. This class uses [ClassLoader] for loading the generated class, then it is able to instantiate the new
* expression. This class uses [AsmClassLoader] for loading the generated class, then it is able to instantiate the new
* class.
*
* @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.
*/
@PublishedApi
internal class AsmGenerationContext<T> @PublishedApi internal constructor(
internal class AsmBuilder<T>(
private val classOfT: Class<*>,
private val algebra: Algebra<T>,
private val className: String
) {
private class ClassLoader(parent: java.lang.ClassLoader) : java.lang.ClassLoader(parent) {
private class AsmClassLoader(parent: ClassLoader) : ClassLoader(parent) {
internal fun defineClass(name: String?, b: ByteArray): Class<*> = defineClass(name, b, 0, b.size)
}
private val classLoader: ClassLoader =
ClassLoader(javaClass.classLoader)
private val classLoader: AsmClassLoader =
AsmClassLoader(javaClass.classLoader)
@Suppress("PrivatePropertyName")
private val T_ALGEBRA_CLASS: String = algebra.javaClass.name.replace(oldChar = '.', newChar = '/')
@ -113,9 +112,8 @@ internal class AsmGenerationContext<T> @PublishedApi internal constructor(
}
}
@PublishedApi
@Suppress("UNCHECKED_CAST")
internal fun generate(): FunctionalCompiledExpression<T> {
fun generate(): FunctionalCompiledExpression<T> {
generatedInstance?.let { return it }
invokeMethodVisitor.run {
@ -188,7 +186,7 @@ internal class AsmGenerationContext<T> @PublishedApi internal constructor(
return new
}
internal fun visitLoadFromConstants(value: T) {
fun visitLoadFromConstants(value: T) {
if (classOfT in INLINABLE_NUMBERS) {
visitNumberConstant(value as Number)
visitCastToT()
@ -213,7 +211,7 @@ internal class AsmGenerationContext<T> @PublishedApi internal constructor(
private fun visitLoadThis(): Unit = invokeMethodVisitor.visitVarInsn(Opcodes.ALOAD, invokeThisVar)
internal fun visitNumberConstant(value: Number) {
fun visitNumberConstant(value: Number) {
maxStack++
val clazz = value.javaClass
val c = clazz.name.replace('.', '/')
@ -234,7 +232,7 @@ internal class AsmGenerationContext<T> @PublishedApi internal constructor(
visitLoadAnyFromConstants(value, c)
}
internal fun visitLoadFromVariables(name: String, defaultValue: T? = null): Unit = invokeMethodVisitor.run {
fun visitLoadFromVariables(name: String, defaultValue: T? = null): Unit = invokeMethodVisitor.run {
maxStack += 2
visitVarInsn(Opcodes.ALOAD, invokeArgumentsVar)
@ -262,7 +260,7 @@ internal class AsmGenerationContext<T> @PublishedApi internal constructor(
visitCastToT()
}
internal fun visitLoadAlgebra() {
fun visitLoadAlgebra() {
maxStack++
invokeMethodVisitor.visitVarInsn(Opcodes.ALOAD, invokeThisVar)
@ -274,7 +272,7 @@ internal class AsmGenerationContext<T> @PublishedApi internal constructor(
invokeMethodVisitor.visitTypeInsn(Opcodes.CHECKCAST, T_ALGEBRA_CLASS)
}
internal fun visitAlgebraOperation(
fun visitAlgebraOperation(
owner: String,
method: String,
descriptor: String,
@ -288,13 +286,12 @@ internal class AsmGenerationContext<T> @PublishedApi internal constructor(
private fun visitCastToT(): Unit = invokeMethodVisitor.visitTypeInsn(Opcodes.CHECKCAST, T_CLASS)
internal fun visitStringConstant(string: String) {
fun visitStringConstant(string: String) {
invokeMethodVisitor.visitLdcInsn(string)
}
internal companion object {
private val SIGNATURE_LETTERS by lazy {
mapOf(
companion object {
private val SIGNATURE_LETTERS = mapOf(
java.lang.Byte::class.java to "B",
java.lang.Short::class.java to "S",
java.lang.Integer::class.java to "I",
@ -302,18 +299,15 @@ internal class AsmGenerationContext<T> @PublishedApi internal constructor(
java.lang.Float::class.java to "F",
java.lang.Double::class.java to "D"
)
}
private val INLINABLE_NUMBERS by lazy { SIGNATURE_LETTERS.keys }
private val INLINABLE_NUMBERS = SIGNATURE_LETTERS.keys
internal const val FUNCTIONAL_COMPILED_EXPRESSION_CLASS =
"scientifik/kmath/asm/FunctionalCompiledExpression"
internal const val MAP_CLASS = "java/util/Map"
internal const val OBJECT_CLASS = "java/lang/Object"
internal const val ALGEBRA_CLASS = "scientifik/kmath/operations/Algebra"
internal const val SPACE_OPERATIONS_CLASS = "scientifik/kmath/operations/SpaceOperations"
internal const val STRING_CLASS = "java/lang/String"
internal const val NUMBER_CLASS = "java/lang/Number"
const val FUNCTIONAL_COMPILED_EXPRESSION_CLASS = "scientifik/kmath/asm/FunctionalCompiledExpression"
const val MAP_CLASS = "java/util/Map"
const val OBJECT_CLASS = "java/lang/Object"
const val ALGEBRA_CLASS = "scientifik/kmath/operations/Algebra"
const val SPACE_OPERATIONS_CLASS = "scientifik/kmath/operations/SpaceOperations"
const val STRING_CLASS = "java/lang/String"
const val NUMBER_CLASS = "java/lang/Number"
}
}

View File

@ -2,7 +2,7 @@ package scientifik.kmath.asm.internal
import org.objectweb.asm.Opcodes
import scientifik.kmath.asm.AsmConstantExpression
import scientifik.kmath.asm.AsmNode
import scientifik.kmath.asm.AsmExpression
import scientifik.kmath.operations.Algebra
private val methodNameAdapters: Map<String, String> = mapOf("+" to "add", "*" to "multiply", "/" to "divide")
@ -16,7 +16,7 @@ internal fun <T> hasSpecific(context: Algebra<T>, name: String, arity: Int): Boo
return true
}
internal fun <T> AsmGenerationContext<T>.tryInvokeSpecific(context: Algebra<T>, name: String, arity: Int): Boolean {
internal fun <T> AsmBuilder<T>.tryInvokeSpecific(context: Algebra<T>, name: String, arity: Int): Boolean {
val aName = methodNameAdapters[name] ?: name
context::class.java.methods.find { it.name == aName && it.parameters.size == arity }
@ -26,9 +26,9 @@ internal fun <T> AsmGenerationContext<T>.tryInvokeSpecific(context: Algebra<T>,
val sig = buildString {
append('(')
repeat(arity) { append("L${AsmGenerationContext.OBJECT_CLASS};") }
repeat(arity) { append("L${AsmBuilder.OBJECT_CLASS};") }
append(')')
append("L${AsmGenerationContext.OBJECT_CLASS};")
append("L${AsmBuilder.OBJECT_CLASS};")
}
visitAlgebraOperation(
@ -42,8 +42,7 @@ internal fun <T> AsmGenerationContext<T>.tryInvokeSpecific(context: Algebra<T>,
return true
}
@PublishedApi
internal fun <T> AsmNode<T>.optimize(): AsmNode<T> {
internal fun <T : Any> AsmExpression<T>.optimize(): AsmExpression<T> {
val a = tryEvaluate()
return if (a == null) this else AsmConstantExpression(a)
return if (a == null) this else AsmConstantExpression(type, algebra, a)
}

View File

@ -1,4 +1,4 @@
package scietifik.kmath.ast.asm
package scietifik.kmath.asm
import scientifik.kmath.asm.asmField
import scientifik.kmath.asm.asmRing

View File

@ -1,4 +1,4 @@
package scietifik.kmath.ast.asm
package scietifik.kmath.asm
import scientifik.kmath.asm.asmField
import scientifik.kmath.expressions.invoke

View File

@ -31,7 +31,7 @@ class ExpressionFieldTest {
@Test
fun separateContext() {
fun <T> FunctionalExpressionField<T>.expression(): Expression<T> {
fun <T> FunctionalExpressionField<T,*>.expression(): Expression<T> {
val x = variable("x")
return x * x + 2 * x + one
}
@ -42,7 +42,7 @@ class ExpressionFieldTest {
@Test
fun valueExpression() {
val expressionBuilder: FunctionalExpressionField<Double>.() -> Expression<Double> = {
val expressionBuilder: FunctionalExpressionField<Double,*>.() -> Expression<Double> = {
val x = variable("x")
x * x + 2 * x + one
}