From d631c048c746fab1200ce853086b45e9ea6b0ecb Mon Sep 17 00:00:00 2001 From: Iaroslav Postovalov Date: Tue, 22 Dec 2020 16:00:51 +0700 Subject: [PATCH] Fix minor problems, update README --- kmath-ast/README.md | 41 +++++++++++++++---- .../kotlin/kscience/kmath/ast/MstAlgebra.kt | 4 +- .../kscience/kmath/asm/internal/AsmBuilder.kt | 13 +++++- .../kotlin/kscience/kmath/ast/AsmTest.kt | 2 - .../kmath/ast/ParserPrecedenceTest.kt | 2 - .../kscience/kmath/operations/Algebra.kt | 4 +- 6 files changed, 48 insertions(+), 18 deletions(-) diff --git a/kmath-ast/README.md b/kmath-ast/README.md index 043224800..ea89883ab 100644 --- a/kmath-ast/README.md +++ b/kmath-ast/README.md @@ -38,7 +38,9 @@ This subproject implements the following features: > ``` > -## Dynamic Expression Code Generation with ObjectWeb ASM +## Dynamic expression code generation + +### On JVM `kmath-ast` JVM module supports runtime code generation to eliminate overhead of tree traversal. Code generator builds a special implementation of `Expression` with implemented `invoke` function. @@ -55,19 +57,20 @@ RealField.mstInField { symbol("x") + 2 }.compile() package kscience.kmath.asm.generated; import java.util.Map; +import kotlin.jvm.functions.Function2; import kscience.kmath.asm.internal.MapIntrinsics; import kscience.kmath.expressions.Expression; -import kscience.kmath.operations.RealField; +import kscience.kmath.expressions.Symbol; -public final class AsmCompiledExpression_1073786867_0 implements Expression { - private final RealField algebra; +public final class AsmCompiledExpression_45045_0 implements Expression { + private final Object[] constants; - public final Double invoke(Map arguments) { - return (Double)this.algebra.add(((Double)MapIntrinsics.getOrFail(arguments, "x")).doubleValue(), 2.0D); + public final Double invoke(Map arguments) { + return (Double)((Function2)this.constants[0]).invoke((Double)MapIntrinsics.getOrFail(arguments, "x"), 2); } - public AsmCompiledExpression_1073786867_0(RealField algebra) { - this.algebra = algebra; + public AsmCompiledExpression_45045_0(Object[] constants) { + this.constants = constants; } } @@ -82,10 +85,30 @@ RealField.mstInField { symbol("x") + 2 }.compile() RealField.expression("x+2".parseMath()) ``` -### Known issues +#### Known issues - The same classes may be generated and loaded twice, so it is recommended to cache compiled expressions to avoid class loading overhead. - This API is not supported by non-dynamic JVM implementations (like TeaVM and GraalVM) because of using class loaders. Contributed by [Iaroslav Postovalov](https://github.com/CommanderTvis). + +### On JS + +A similar feature is also available on JS. + +```kotlin +RealField.mstInField { symbol("x") + 2 }.compile() +``` + +The code above returns expression implemented with such a JS function: + +```js +var executable = function (constants, arguments) { + return constants[1](constants[0](arguments, "x"), 2); +}; +``` + +#### Known issues + +- This feature uses `eval` which can be unavailable in several environments. diff --git a/kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MstAlgebra.kt b/kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MstAlgebra.kt index edbd7f92b..55d5b659d 100644 --- a/kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MstAlgebra.kt +++ b/kmath-ast/src/commonMain/kotlin/kscience/kmath/ast/MstAlgebra.kt @@ -27,7 +27,9 @@ public object MstSpace : Space, NumericAlgebra { public override fun add(a: MST, b: MST): MST.Binary = binaryOperation(SpaceOperations.PLUS_OPERATION)(a, b) public override operator fun MST.unaryPlus(): MST.Unary = unaryOperation(SpaceOperations.PLUS_OPERATION)(this) public override operator fun MST.unaryMinus(): MST.Unary = unaryOperation(SpaceOperations.MINUS_OPERATION)(this) - public override operator fun MST.minus(b: MST): MST.Binary = binaryOperation(SpaceOperations.MINUS_OPERATION)(this, b) + + public override operator fun MST.minus(b: MST): MST.Binary = + binaryOperation(SpaceOperations.MINUS_OPERATION)(this, b) public override fun multiply(a: MST, k: Number): MST.Binary = binaryOperation(RingOperations.TIMES_OPERATION)(a, number(k)) diff --git a/kmath-ast/src/jvmMain/kotlin/kscience/kmath/asm/internal/AsmBuilder.kt b/kmath-ast/src/jvmMain/kotlin/kscience/kmath/asm/internal/AsmBuilder.kt index 882b95bf0..af7dc61b9 100644 --- a/kmath-ast/src/jvmMain/kotlin/kscience/kmath/asm/internal/AsmBuilder.kt +++ b/kmath-ast/src/jvmMain/kotlin/kscience/kmath/asm/internal/AsmBuilder.kt @@ -2,13 +2,16 @@ package kscience.kmath.asm.internal import kscience.kmath.asm.internal.AsmBuilder.ClassLoader import kscience.kmath.ast.MST +import kscience.kmath.ast.mstInField import kscience.kmath.expressions.Expression +import kscience.kmath.operations.RealField import org.objectweb.asm.* import org.objectweb.asm.Opcodes.* import org.objectweb.asm.Type.* import org.objectweb.asm.commons.InstructionAdapter import java.lang.invoke.MethodHandles import java.lang.invoke.MethodType +import java.lang.reflect.Modifier import java.util.stream.Collectors.toMap import kotlin.contracts.InvocationKind import kotlin.contracts.contract @@ -82,7 +85,7 @@ internal class AsmBuilder( ACC_PUBLIC or ACC_FINAL, "invoke", getMethodDescriptor(tType, MAP_TYPE), - "(L${MAP_TYPE.internalName}<${STRING_TYPE.descriptor}+${tType.descriptor}>;)${tType.descriptor}", + "(L${MAP_TYPE.internalName}<${SYMBOL_TYPE.descriptor}${if (Modifier.isFinal(classOfT.modifiers)) "" else "+"}${tType.descriptor}>;)${tType.descriptor}", null, ).instructionAdapter { invokeMethodVisitor = this @@ -159,7 +162,7 @@ internal class AsmBuilder( "", getMethodDescriptor(VOID_TYPE, *OBJECT_ARRAY_TYPE.wrapToArrayIf { hasConstants }), null, - null + null, ).instructionAdapter { val l0 = label() load(0, classType) @@ -190,6 +193,7 @@ internal class AsmBuilder( } val cls = classLoader.defineClass(className, classWriter.toByteArray()) + java.io.File("dump.class").writeBytes(classWriter.toByteArray()) val l = MethodHandles.publicLookup() if (hasConstants) @@ -334,5 +338,10 @@ internal class AsmBuilder( * ASM type for MapIntrinsics. */ val MAP_INTRINSICS_TYPE: Type by lazy { getObjectType("kscience/kmath/asm/internal/MapIntrinsics") } + + /** + * ASM Type for [kscience.kmath.expressions.Symbol]. + */ + val SYMBOL_TYPE: Type by lazy { getObjectType("kscience/kmath/expressions/Symbol") } } } diff --git a/kmath-ast/src/jvmTest/kotlin/kscience/kmath/ast/AsmTest.kt b/kmath-ast/src/jvmTest/kotlin/kscience/kmath/ast/AsmTest.kt index 1ca8aa5dd..992f4024e 100644 --- a/kmath-ast/src/jvmTest/kotlin/kscience/kmath/ast/AsmTest.kt +++ b/kmath-ast/src/jvmTest/kotlin/kscience/kmath/ast/AsmTest.kt @@ -2,8 +2,6 @@ package kscience.kmath.ast import kscience.kmath.asm.compile import kscience.kmath.asm.expression -import kscience.kmath.ast.mstInField -import kscience.kmath.ast.parseMath import kscience.kmath.expressions.invoke import kscience.kmath.operations.Complex import kscience.kmath.operations.ComplexField diff --git a/kmath-ast/src/jvmTest/kotlin/kscience/kmath/ast/ParserPrecedenceTest.kt b/kmath-ast/src/jvmTest/kotlin/kscience/kmath/ast/ParserPrecedenceTest.kt index 1844a0991..561fe51bd 100644 --- a/kmath-ast/src/jvmTest/kotlin/kscience/kmath/ast/ParserPrecedenceTest.kt +++ b/kmath-ast/src/jvmTest/kotlin/kscience/kmath/ast/ParserPrecedenceTest.kt @@ -1,7 +1,5 @@ package kscience.kmath.ast -import kscience.kmath.ast.evaluate -import kscience.kmath.ast.parseMath import kscience.kmath.operations.Field import kscience.kmath.operations.RealField import kotlin.test.Test diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt index f1ceb8858..d2cf8e0dd 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/operations/Algebra.kt @@ -275,12 +275,12 @@ public interface SpaceOperations : Algebra { public companion object { /** - * The identifier of addition and unary positive. + * The identifier of addition and unary positive operator. */ public const val PLUS_OPERATION: String = "+" /** - * The identifier of subtraction and unary negation. + * The identifier of subtraction and unary negative operator. */ public const val MINUS_OPERATION: String = "-" }