From 3741450b89c2101b81041eaa3265d54575740032 Mon Sep 17 00:00:00 2001 From: tetraquark Date: Tue, 15 Jun 2021 12:31:43 +0700 Subject: [PATCH 01/18] add kotlinx serialization to deps --- buildSrc/src/main/kotlin/Deps.kt | 6 ++++++ javascript/build.gradle.kts | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/buildSrc/src/main/kotlin/Deps.kt b/buildSrc/src/main/kotlin/Deps.kt index e1c91bf..bd13d79 100644 --- a/buildSrc/src/main/kotlin/Deps.kt +++ b/buildSrc/src/main/kotlin/Deps.kt @@ -9,8 +9,10 @@ object Deps { private const val materialDesignVersion = "1.0.0" private const val androidLifecycleVersion = "2.1.0" private const val androidCoreTestingVersion = "2.1.0" + private const val quickjsVersion = "0.9.0" private const val coroutinesVersion = "1.4.2" + private const val kotlinxSerializationVersion = "1.1.0" private const val mokoTestVersion = "0.2.0" const val mokoJavascriptVersion = "0.1.0" @@ -25,11 +27,15 @@ object Deps { const val appCompat = "androidx.appcompat:appcompat:$androidAppCompatVersion" const val material = "com.google.android.material:material:$materialDesignVersion" const val lifecycle = "androidx.lifecycle:lifecycle-extensions:$androidLifecycleVersion" + + const val quickjs = "app.cash.quickjs:quickjs-android:$quickjsVersion" } object MultiPlatform { const val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion" + const val kotlinSerialization = + "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinxSerializationVersion" const val mokoTest = "dev.icerock.moko:test:$mokoTestVersion" const val mokoJavascript = "dev.icerock.moko:javascript:$mokoJavascriptVersion" diff --git a/javascript/build.gradle.kts b/javascript/build.gradle.kts index 928d042..6c7591c 100644 --- a/javascript/build.gradle.kts +++ b/javascript/build.gradle.kts @@ -8,5 +8,9 @@ plugins { } dependencies { + androidMainImplementation(Deps.Libs.Android.quickjs) + + commonMainImplementation(Deps.Libs.MultiPlatform.kotlinSerialization) + commonTestImplementation(Deps.Libs.MultiPlatform.mokoTest) } From b9a770d2744004bc525af4e0b38f4be205364052 Mon Sep 17 00:00:00 2001 From: tetraquark Date: Tue, 15 Jun 2021 12:32:00 +0700 Subject: [PATCH 02/18] add JavaScriptEvaluationException --- .../moko/javascript/JavaScriptEvaluationException.kt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JavaScriptEvaluationException.kt diff --git a/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JavaScriptEvaluationException.kt b/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JavaScriptEvaluationException.kt new file mode 100644 index 0000000..9dfa258 --- /dev/null +++ b/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JavaScriptEvaluationException.kt @@ -0,0 +1,6 @@ +package dev.icerock.moko.javascript + +class JavaScriptEvaluationException( + throwable: Throwable?, + message: String? = null +) : Exception(message, throwable) From 773d468d490936159ea6021bb2e8be57cafa6eff Mon Sep 17 00:00:00 2001 From: tetraquark Date: Tue, 15 Jun 2021 12:32:50 +0700 Subject: [PATCH 03/18] rename JsType inheritors, add close method to JavaScriptEngine --- .../icerock/moko/javascript/JavaScriptEngine.kt | 10 ++++++++++ .../dev/icerock/moko/javascript/JsType.kt | 17 ++++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt b/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt index ef51a05..ebdbe4f 100644 --- a/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt +++ b/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt @@ -5,5 +5,15 @@ package dev.icerock.moko.javascript expect class JavaScriptEngine() { + /** + * Evaluate some [script] with external [context]. + * + * @throws JavaScriptEvaluationException in case of an error in the engine evaluation + */ fun evaluate(context: Map, script: String): JsType + + /** + * Closes the engine and releases the allocated memory. + */ + fun close() } diff --git a/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JsType.kt b/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JsType.kt index c367b93..df8083b 100644 --- a/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JsType.kt +++ b/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JsType.kt @@ -4,10 +4,17 @@ package dev.icerock.moko.javascript +import kotlinx.serialization.json.JsonElement + sealed class JsType { - data class Boolean(val value: Boolean): JsType - data class String(val value: String): JsType - data class Int(val value: Int): JsType - data class Double(val value: Double): JsType - data class Json(val value: JsonElement): JsType + data class Bool(val value: Boolean): JsType() + data class Str(val value: String): JsType() + data class IntNum(val value: Int): JsType() + data class DoubleNum(val value: Double): JsType() + data class Json(val value: JsonElement): JsType() + + /** + * For "undefined" and "null". + */ + object Null : JsType() } From 3c3a454c99daf42a8559d340e04480f2f58ed439 Mon Sep 17 00:00:00 2001 From: tetraquark Date: Tue, 15 Jun 2021 13:07:30 +0700 Subject: [PATCH 04/18] add copyright --- .../dev/icerock/moko/javascript/JavaScriptEngine.kt | 3 ++- .../moko/javascript/JavaScriptEvaluationException.kt | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt b/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt index ebdbe4f..fd41fa2 100644 --- a/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt +++ b/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt @@ -8,7 +8,8 @@ expect class JavaScriptEngine() { /** * Evaluate some [script] with external [context]. * - * @throws JavaScriptEvaluationException in case of an error in the engine evaluation + * @throws JavaScriptEvaluationException in case of an error in the engine evaluation or if the + * engine has already been closed. */ fun evaluate(context: Map, script: String): JsType diff --git a/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JavaScriptEvaluationException.kt b/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JavaScriptEvaluationException.kt index 9dfa258..56ed7a5 100644 --- a/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JavaScriptEvaluationException.kt +++ b/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JavaScriptEvaluationException.kt @@ -1,6 +1,10 @@ +/* + * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. + */ + package dev.icerock.moko.javascript class JavaScriptEvaluationException( - throwable: Throwable?, + cause: Throwable? = null, message: String? = null -) : Exception(message, throwable) +) : Exception(message, cause) From fd0171fce08dc0169664e81958b417c1661d4412 Mon Sep 17 00:00:00 2001 From: tetraquark Date: Tue, 15 Jun 2021 13:46:39 +0700 Subject: [PATCH 05/18] JavaScriptEngine android implementation --- .../src/androidMain/AndroidManifest.xml | 2 +- .../moko/javascript/JavaScriptEngine.kt | 69 +++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 javascript/src/androidMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt diff --git a/javascript/src/androidMain/AndroidManifest.xml b/javascript/src/androidMain/AndroidManifest.xml index 0da09ab..309ebc8 100644 --- a/javascript/src/androidMain/AndroidManifest.xml +++ b/javascript/src/androidMain/AndroidManifest.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + diff --git a/javascript/src/androidMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt b/javascript/src/androidMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt new file mode 100644 index 0000000..2e385d8 --- /dev/null +++ b/javascript/src/androidMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. + */ + +package dev.icerock.moko.javascript + +import app.cash.quickjs.QuickJs +import app.cash.quickjs.QuickJsException +import kotlinx.serialization.SerializationException +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.encodeToJsonElement + +actual class JavaScriptEngine actual constructor(){ + private val quickJs: QuickJs = QuickJs.create() + private val json: Json = Json.Default + + @Volatile + var isClosed = false + private set + + actual fun evaluate( + context: Map, + script: String + ): JsType { + if (isClosed) throw JavaScriptEvaluationException(message = "Engine already closed") + + return try { + internalEvaluate(context, script) + } catch (exception: QuickJsException) { + throw JavaScriptEvaluationException(exception) + } + } + + actual fun close() { + if (isClosed) return + quickJs.close() + isClosed = true + } + + private fun internalEvaluate( + context: Map, + script: String + ): JsType { + context.forEach { pair -> + quickJs.set(pair.key, pair.value.javaClass, pair.value) + } + val result = quickJs.evaluate(script) + return handleQuickJsResult(result) + } + + private fun handleQuickJsResult(result: Any?): JsType { + if (result is String) { + return try { + JsType.Json(json.encodeToJsonElement(result)) + } catch (ex: SerializationException) { + JsType.Str(result) + } + } + + return when (result) { + result == null -> JsType.Null + is Boolean -> JsType.Bool(result) + is Int -> JsType.IntNum(result) + is Double -> JsType.DoubleNum(result) + is Float -> JsType.DoubleNum(result.toDouble()) + else -> throw IllegalStateException("Impossible JavaScriptEngine handler state") + } + } +} From a1af6fdd56a1d18fc53bfcb35b25a1baf067b272 Mon Sep 17 00:00:00 2001 From: Andrey Dorofeev Date: Tue, 15 Jun 2021 15:33:02 +0700 Subject: [PATCH 06/18] added ios implementation for JavaScriptEngine --- .../moko/javascript/JavaScriptEngine.kt | 99 +++++++++++++++++++ .../ios-app/ios-app.xcodeproj/project.pbxproj | 3 + .../src/Resources/Base.lproj/Main.storyboard | 75 ++++++++++++-- sample/ios-app/src/TestViewController.swift | 15 +++ .../com/icerockdev/library/Calculator.kt | 28 +++++- 5 files changed, 209 insertions(+), 11 deletions(-) create mode 100644 javascript/src/iosMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt diff --git a/javascript/src/iosMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt b/javascript/src/iosMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt new file mode 100644 index 0000000..92c9a01 --- /dev/null +++ b/javascript/src/iosMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt @@ -0,0 +1,99 @@ +/* + * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. + */ + +package dev.icerock.moko.javascript + +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonArray +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.encodeToJsonElement +import platform.Foundation.NSArray +import platform.Foundation.NSDictionary +import platform.Foundation.NSJSONSerialization +import platform.Foundation.NSString +import platform.Foundation.NSUTF8StringEncoding +import platform.Foundation.create +import platform.Foundation.dataUsingEncoding +import platform.JavaScriptCore.JSContext +import platform.JavaScriptCore.JSValue +import platform.JavaScriptCore.setObject + +actual class JavaScriptEngine actual constructor() { + actual fun evaluate(context: Map, script: String): JsType { + + val jsContext = JSContext() + + jsContext.exceptionHandler = { exceptionContext, exception -> + val message = "\"context = $exceptionContext, exception = $exception\"" + throw JavaScriptEvaluationException(cause = null, message = message) + } + + context.forEach { + jsContext.setObject( + `object` = it.value.getValue(), + forKeyedSubscript = NSString.create(string = it.key) + ) + } + + val result = jsContext.evaluateScript(script) + + return result?.toMokoJSType() ?: JsType.Null + } + + actual fun close() { + // Nothing to do here + } +} + +private fun JsonObject.toNSDictionary(): NSDictionary { + val data = NSString.create(string = this.toString()).dataUsingEncoding(NSUTF8StringEncoding) + ?: return NSDictionary() + return (NSJSONSerialization.JSONObjectWithData( + data = data, + options = 0, + error = null + ) as? NSDictionary) ?: NSDictionary() +} + +private fun JsonArray.toNSArray(): NSArray { + val data = NSString.create(string = this.toString()).dataUsingEncoding(NSUTF8StringEncoding) + ?: return NSArray() + return (NSJSONSerialization.JSONObjectWithData( + data = data, + options = 0, + error = null + ) as? NSArray) ?: NSArray() +} + +private fun JsonElement.getValue(): Any? { + return (this as? JsonObject)?.toNSDictionary() + ?: (this as? JsonArray)?.toNSArray() + ?: (this as? JsonPrimitive)?.content +} + +private fun JsType.getValue(): Any? { + return when (this) { + is JsType.Bool -> value + is JsType.Str -> value + is JsType.IntNum -> value + is JsType.DoubleNum -> value + is JsType.Json -> value.getValue() + is JsType.Null -> null + } +} + +private fun JSValue.toMokoJSType(): JsType { + return when { + isBoolean -> JsType.Bool(toBool()) + isString -> JsType.Str(toString_().orEmpty()) + isNumber -> JsType.DoubleNum(toDouble()) + isObject -> JsType.Json(Json.encodeToJsonElement(toDictionary())) + isArray -> JsType.Json(Json.encodeToJsonElement(toArray())) + isUndefined -> JsType.Null + isNull -> JsType.Null + else -> JsType.Null + } +} diff --git a/sample/ios-app/ios-app.xcodeproj/project.pbxproj b/sample/ios-app/ios-app.xcodeproj/project.pbxproj index fee4d55..f6169bb 100644 --- a/sample/ios-app/ios-app.xcodeproj/project.pbxproj +++ b/sample/ios-app/ios-app.xcodeproj/project.pbxproj @@ -145,6 +145,7 @@ hasScannedForEncodings = 0; knownRegions = ( English, + Base, ); mainGroup = 287627F61F319065007FA12B; productRefGroup = 287628001F319065007FA12B /* Products */; @@ -270,6 +271,7 @@ CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 4VU932NX78; INFOPLIST_FILE = src/Info.plist; + ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = dev.icerock.moko.sample.javascript; PRODUCT_NAME = mokoSampleJavascript; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -288,6 +290,7 @@ CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 4VU932NX78; INFOPLIST_FILE = src/Info.plist; + ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = dev.icerock.moko.sample.javascript; PRODUCT_NAME = mokoSampleJavascript; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/sample/ios-app/src/Resources/Base.lproj/Main.storyboard b/sample/ios-app/src/Resources/Base.lproj/Main.storyboard index 594bc2b..ca8b011 100644 --- a/sample/ios-app/src/Resources/Base.lproj/Main.storyboard +++ b/sample/ios-app/src/Resources/Base.lproj/Main.storyboard @@ -1,11 +1,8 @@ - - - - + + - - + @@ -14,7 +11,7 @@ - + @@ -28,7 +25,7 @@ - + @@ -36,8 +33,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sample/ios-app/src/TestViewController.swift b/sample/ios-app/src/TestViewController.swift index 23a9c17..895ec43 100644 --- a/sample/ios-app/src/TestViewController.swift +++ b/sample/ios-app/src/TestViewController.swift @@ -7,7 +7,22 @@ import MultiPlatformLibrary class TestViewController: UIViewController { + @IBOutlet private var firstValueTextField: UITextField! + @IBOutlet private var secondValueTextField: UITextField! + @IBOutlet private var resultLabel: UILabel! + override func viewDidLoad() { super.viewDidLoad() } + + @IBAction private func run() { + let result = Calculator().run(a: firstValueTextField.text ?? "", b: secondValueTextField.text ?? "") + if let value = (result as? JsType.Str)?.value { + resultLabel.text = value + } + + if let value = (result as? JsType.DoubleNum)?.value { + resultLabel.text = "\(value)" + } + } } diff --git a/sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/Calculator.kt b/sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/Calculator.kt index 4489719..32427f7 100644 --- a/sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/Calculator.kt +++ b/sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/Calculator.kt @@ -4,10 +4,32 @@ package com.icerockdev.library -import dev.icerock.moko.javascript.multiply +import dev.icerock.moko.javascript.JavaScriptEngine +import dev.icerock.moko.javascript.JsType class Calculator { - fun run() { - println(multiply(a = 2, b = 3)) + fun run(a: String, b: String): JsType { + val engine = JavaScriptEngine() + val testScript = "a+b" + + val intA = a.toIntOrNull() + val intB = b.toIntOrNull() + + val context = if (intA != null && intB != null) { + mapOf( + "a" to JsType.IntNum(intA), + "b" to JsType.IntNum(intB) + ) + } else { + mapOf( + "a" to JsType.Str(a), + "b" to JsType.Str(b) + ) + } + + return engine.evaluate( + context = context, + script = testScript + ) } } From 9d42299df86cad62a7d63ace54aee7839807ee06 Mon Sep 17 00:00:00 2001 From: Andrey Dorofeev Date: Tue, 15 Jun 2021 17:04:43 +0700 Subject: [PATCH 07/18] added test for javascript evaluate --- buildSrc/src/main/kotlin/Deps.kt | 4 +- .../moko/javascript/JavaScriptEngineTest.kt | 44 +++++++++++++++++++ .../dev/icerock/moko/javascript/SampleTest.kt | 15 ------- sample/mpp-library/build.gradle.kts | 1 + 4 files changed, 47 insertions(+), 17 deletions(-) create mode 100644 javascript/src/commonTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt delete mode 100644 javascript/src/commonTest/kotlin/dev/icerock/moko/javascript/SampleTest.kt diff --git a/buildSrc/src/main/kotlin/Deps.kt b/buildSrc/src/main/kotlin/Deps.kt index bd13d79..a5501a1 100644 --- a/buildSrc/src/main/kotlin/Deps.kt +++ b/buildSrc/src/main/kotlin/Deps.kt @@ -13,7 +13,7 @@ object Deps { private const val coroutinesVersion = "1.4.2" private const val kotlinxSerializationVersion = "1.1.0" - private const val mokoTestVersion = "0.2.0" + private const val mokoTestVersion = "0.3.0" const val mokoJavascriptVersion = "0.1.0" object Android { @@ -37,7 +37,7 @@ object Deps { const val kotlinSerialization = "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinxSerializationVersion" - const val mokoTest = "dev.icerock.moko:test:$mokoTestVersion" + const val mokoTest = "dev.icerock.moko:test-core:$mokoTestVersion" const val mokoJavascript = "dev.icerock.moko:javascript:$mokoJavascriptVersion" } } diff --git a/javascript/src/commonTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt b/javascript/src/commonTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt new file mode 100644 index 0000000..5a622d1 --- /dev/null +++ b/javascript/src/commonTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. + */ + +package dev.icerock.moko.javascript + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.encodeToJsonElement + +class JavaScriptEngineTest { + @Test + fun `test json context`() { + val form = mapOf("selector_1" to "first_value", "selector_2" to "second_value") + val profile = mapOf("email" to "test@test.com") + val formJson = Json.encodeToJsonElement(form) + val profileJson = Json.encodeToJsonElement(profile) + val context: Map = mapOf("form" to JsType.Json(formJson), "profile" to JsType.Json(profileJson)) + + val jsEngine = JavaScriptEngine() + + assertEquals(JsType.Bool(true), jsEngine.evaluate(context = context, script = "form.selector_1 == \"first_value\"")) + + assertEquals(JsType.Bool(true), jsEngine.evaluate(context = context, script = "profile.email != null")) + + assertEquals(JsType.Str("test@test.com"), jsEngine.evaluate(context = context, script = "profile.email")) + + assertEquals(JsType.Null, jsEngine.evaluate(context = context, script = "profile.first_name")) + } + + @Test + fun `test plus script`() { + val list = listOf(5, 15) + val listJson = Json.encodeToJsonElement(list) + val context: Map = mapOf("list" to JsType.Json(listJson), "number" to JsType.IntNum(4), "doubleString" to JsType.Str(" Hello ")) + + val jsEngine = JavaScriptEngine() + + assertEquals(JsType.DoubleNum(19.0), jsEngine.evaluate(context = context, script = "list[1]+number")) + + assertEquals(JsType.Str(" Hello 5"), jsEngine.evaluate(context = context, script = "doubleString+list[0]")) + } +} diff --git a/javascript/src/commonTest/kotlin/dev/icerock/moko/javascript/SampleTest.kt b/javascript/src/commonTest/kotlin/dev/icerock/moko/javascript/SampleTest.kt deleted file mode 100644 index 0309b38..0000000 --- a/javascript/src/commonTest/kotlin/dev/icerock/moko/javascript/SampleTest.kt +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -package dev.icerock.moko.javascript - -import kotlin.test.Test -import kotlin.test.assertEquals - -class SampleTest { - @Test - fun `equality test`() { - assertEquals(expected = 4, actual = multiply(a = 2, b = 2)) - } -} diff --git a/sample/mpp-library/build.gradle.kts b/sample/mpp-library/build.gradle.kts index 4c3ed48..04ed8b3 100644 --- a/sample/mpp-library/build.gradle.kts +++ b/sample/mpp-library/build.gradle.kts @@ -9,6 +9,7 @@ plugins { dependencies { commonMainApi(Deps.Libs.MultiPlatform.coroutines) + commonMainApi(Deps.Libs.MultiPlatform.kotlinSerialization) commonMainApi(Deps.Libs.MultiPlatform.mokoJavascript) } From 23dd5ea4052eabc25a5bc3e49a91b3f34d0f7c01 Mon Sep 17 00:00:00 2001 From: tetraquark Date: Tue, 15 Jun 2021 17:26:01 +0700 Subject: [PATCH 08/18] add JavaScriptEngineTest (not working), review fixes --- buildSrc/src/main/kotlin/Deps.kt | 13 ++++-- javascript/build.gradle.kts | 14 +++++++ .../moko/javascript/JavaScriptEngineTest.kt | 40 +++++++++++++++++++ .../moko/javascript/JavaScriptEngine.kt | 17 ++++---- .../dev/icerock/moko/javascript/JsType.kt | 7 ++++ .../android-app/src/main/AndroidManifest.xml | 2 +- .../com/icerockdev/library/Calculator.kt | 6 +-- 7 files changed, 81 insertions(+), 18 deletions(-) create mode 100644 javascript/src/androidAndroidTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt diff --git a/buildSrc/src/main/kotlin/Deps.kt b/buildSrc/src/main/kotlin/Deps.kt index bd13d79..0b21fb3 100644 --- a/buildSrc/src/main/kotlin/Deps.kt +++ b/buildSrc/src/main/kotlin/Deps.kt @@ -13,13 +13,14 @@ object Deps { private const val coroutinesVersion = "1.4.2" private const val kotlinxSerializationVersion = "1.1.0" - private const val mokoTestVersion = "0.2.0" + private const val mokoTestVersion = "0.3.0" + const val mokoJavascriptVersion = "0.1.0" object Android { const val compileSdk = 30 const val targetSdk = 30 - const val minSdk = 16 + const val minSdk = 18 } object Libs { @@ -37,7 +38,13 @@ object Deps { const val kotlinSerialization = "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinxSerializationVersion" - const val mokoTest = "dev.icerock.moko:test:$mokoTestVersion" + const val kotlinTest = + "org.jetbrains.kotlin:kotlin-test-common:$kotlinTestVersion" + const val kotlinTestAnnotations = + "org.jetbrains.kotlin:kotlin-test-annotations-common:$kotlinTestVersion" + const val mokoTest = "dev.icerock.moko:test-core:$mokoTestVersion" + const val mokoTestRobolectric = "dev.icerock.moko:test-roboelectric:$mokoTestVersion" + const val mokoJavascript = "dev.icerock.moko:javascript:$mokoJavascriptVersion" } } diff --git a/javascript/build.gradle.kts b/javascript/build.gradle.kts index 6c7591c..9041f23 100644 --- a/javascript/build.gradle.kts +++ b/javascript/build.gradle.kts @@ -7,10 +7,24 @@ plugins { id("publication-convention") } +android { + testOptions.unitTests.isIncludeAndroidResources = true + defaultConfig { + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } +} + dependencies { androidMainImplementation(Deps.Libs.Android.quickjs) commonMainImplementation(Deps.Libs.MultiPlatform.kotlinSerialization) + commonTestImplementation(Deps.Libs.MultiPlatform.kotlinTest) + commonTestImplementation(Deps.Libs.MultiPlatform.kotlinTestAnnotations) commonTestImplementation(Deps.Libs.MultiPlatform.mokoTest) + + androidTestImplementation("androidx.test:runner:1.3.0") + androidTestImplementation("androidx.test:rules:1.3.0") + androidTestImplementation("androidx.test.ext:junit:1.1.2") + androidTestImplementation("androidx.test.ext:junit-ktx:1.1.2") } diff --git a/javascript/src/androidAndroidTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt b/javascript/src/androidAndroidTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt new file mode 100644 index 0000000..ec1cf7a --- /dev/null +++ b/javascript/src/androidAndroidTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. + */ + +package dev.icerock.moko.javascript + +import org.junit.After +import org.junit.Before +import org.junit.Test + +class JavaScriptEngineTest { + private var _javaScriptEngine: JavaScriptEngine? = null + + private val javaScriptEngine: JavaScriptEngine + get() = _javaScriptEngine!! + + @Before + fun init() { + _javaScriptEngine = JavaScriptEngine() + } + + @After + fun dispose() { + javaScriptEngine.close() + } + + @Test + fun equalityTest() { + val jsScript = """ + var a = 1 + 2; + a + """.trimIndent() + val result = javaScriptEngine.evaluate(emptyMap(), jsScript) + + println("DBG $result") + + //assertTrue { result is JsType.IntNum } + //assertEquals(expected = 3, actual = result.intValue()) + } +} diff --git a/javascript/src/androidMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt b/javascript/src/androidMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt index 2e385d8..539e23b 100644 --- a/javascript/src/androidMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt +++ b/javascript/src/androidMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt @@ -49,21 +49,20 @@ actual class JavaScriptEngine actual constructor(){ } private fun handleQuickJsResult(result: Any?): JsType { - if (result is String) { - return try { - JsType.Json(json.encodeToJsonElement(result)) - } catch (ex: SerializationException) { - JsType.Str(result) - } - } - return when (result) { result == null -> JsType.Null is Boolean -> JsType.Bool(result) is Int -> JsType.IntNum(result) is Double -> JsType.DoubleNum(result) is Float -> JsType.DoubleNum(result.toDouble()) - else -> throw IllegalStateException("Impossible JavaScriptEngine handler state") + is String -> try { + JsType.Json(json.encodeToJsonElement(result)) + } catch (ex: SerializationException) { + JsType.Str(result) + } + else -> throw JavaScriptEvaluationException( + message = "Impossible JavaScriptEngine handler state with result [$result]" + ) } } } diff --git a/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JsType.kt b/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JsType.kt index df8083b..4bbdc4d 100644 --- a/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JsType.kt +++ b/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JsType.kt @@ -18,3 +18,10 @@ sealed class JsType { */ object Null : JsType() } + +fun JsType.boolValue(): Boolean = (this as JsType.Bool).value +fun JsType.stringValue(): String = (this as JsType.Str).value +fun JsType.intValue(): Int = (this as JsType.IntNum).value +fun JsType.doubleValue(): Double = (this as JsType.DoubleNum).value +fun JsType.jsonValue(): JsonElement = (this as JsType.Json).value +fun JsType.nullValue(): Any? = (this as JsType.Null).let { null } diff --git a/sample/android-app/src/main/AndroidManifest.xml b/sample/android-app/src/main/AndroidManifest.xml index 1485409..adf12eb 100644 --- a/sample/android-app/src/main/AndroidManifest.xml +++ b/sample/android-app/src/main/AndroidManifest.xml @@ -6,7 +6,7 @@ diff --git a/sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/Calculator.kt b/sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/Calculator.kt index 4489719..4855586 100644 --- a/sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/Calculator.kt +++ b/sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/Calculator.kt @@ -4,10 +4,6 @@ package com.icerockdev.library -import dev.icerock.moko.javascript.multiply - class Calculator { - fun run() { - println(multiply(a = 2, b = 3)) - } + fun run() = Unit } From 09b8bab87f4e4fb4c48df1d8d44fb9a59212286e Mon Sep 17 00:00:00 2001 From: tetraquark Date: Tue, 15 Jun 2021 17:35:43 +0700 Subject: [PATCH 09/18] move test libs to Deps --- buildSrc/src/main/kotlin/Deps.kt | 8 +++++++- javascript/build.gradle.kts | 8 ++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/buildSrc/src/main/kotlin/Deps.kt b/buildSrc/src/main/kotlin/Deps.kt index 0b21fb3..bb3baff 100644 --- a/buildSrc/src/main/kotlin/Deps.kt +++ b/buildSrc/src/main/kotlin/Deps.kt @@ -8,7 +8,8 @@ object Deps { private const val androidAppCompatVersion = "1.1.0" private const val materialDesignVersion = "1.0.0" private const val androidLifecycleVersion = "2.1.0" - private const val androidCoreTestingVersion = "2.1.0" + private const val androidCoreTestingVersion = "1.3.0" + private const val testJUnitExtVersion = "1.1.2" private const val quickjsVersion = "0.9.0" private const val coroutinesVersion = "1.4.2" @@ -29,6 +30,11 @@ object Deps { const val material = "com.google.android.material:material:$materialDesignVersion" const val lifecycle = "androidx.lifecycle:lifecycle-extensions:$androidLifecycleVersion" + const val testRunner = "androidx.test:runner:$androidCoreTestingVersion" + const val testRules = "androidx.test:rules:$androidCoreTestingVersion" + const val testJUnitExt = "androidx.test.ext:junit:$testJUnitExtVersion" + const val testJUnitExtKtx = "androidx.test.ext:junit-ktx:$testJUnitExtVersion" + const val quickjs = "app.cash.quickjs:quickjs-android:$quickjsVersion" } diff --git a/javascript/build.gradle.kts b/javascript/build.gradle.kts index 9041f23..ff97b94 100644 --- a/javascript/build.gradle.kts +++ b/javascript/build.gradle.kts @@ -23,8 +23,8 @@ dependencies { commonTestImplementation(Deps.Libs.MultiPlatform.kotlinTestAnnotations) commonTestImplementation(Deps.Libs.MultiPlatform.mokoTest) - androidTestImplementation("androidx.test:runner:1.3.0") - androidTestImplementation("androidx.test:rules:1.3.0") - androidTestImplementation("androidx.test.ext:junit:1.1.2") - androidTestImplementation("androidx.test.ext:junit-ktx:1.1.2") + androidTestImplementation(Deps.Libs.Android.testRunner) + androidTestImplementation(Deps.Libs.Android.testRules) + androidTestImplementation(Deps.Libs.Android.testJUnitExt) + androidTestImplementation(Deps.Libs.Android.testJUnitExtKtx) } From 295684e9b06f2c61b0983203b5e0c1eeaa6e1578 Mon Sep 17 00:00:00 2001 From: Andrey Dorofeev Date: Tue, 15 Jun 2021 18:05:27 +0700 Subject: [PATCH 10/18] move tests to iosTest --- .../kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename javascript/src/{commonTest => iosTest}/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt (100%) diff --git a/javascript/src/commonTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt b/javascript/src/iosTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt similarity index 100% rename from javascript/src/commonTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt rename to javascript/src/iosTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt From 3e570db4ed5562aa64928390553bcbdfa8321a43 Mon Sep 17 00:00:00 2001 From: tetraquark Date: Tue, 15 Jun 2021 19:16:57 +0700 Subject: [PATCH 11/18] android JavaScriptEngine improve; add JsType.value property --- javascript/build.gradle.kts | 8 ++++ .../moko/javascript/JavaScriptEngine.kt | 39 ++++++++++++------- .../dev/icerock/moko/javascript/JsType.kt | 12 ++++++ 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/javascript/build.gradle.kts b/javascript/build.gradle.kts index ff97b94..51214db 100644 --- a/javascript/build.gradle.kts +++ b/javascript/build.gradle.kts @@ -12,6 +12,14 @@ android { defaultConfig { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } + + packagingOptions { + exclude("META-INF/*.kotlin_module") + exclude("META-INF/*.kotlin_module") + exclude("META-INF/AL2.0") + exclude("META-INF/LGPL2.1") + + } } dependencies { diff --git a/javascript/src/androidMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt b/javascript/src/androidMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt index 539e23b..138ad65 100644 --- a/javascript/src/androidMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt +++ b/javascript/src/androidMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt @@ -8,9 +8,9 @@ import app.cash.quickjs.QuickJs import app.cash.quickjs.QuickJsException import kotlinx.serialization.SerializationException import kotlinx.serialization.json.Json -import kotlinx.serialization.json.encodeToJsonElement +import kotlinx.serialization.json.JsonObject -actual class JavaScriptEngine actual constructor(){ +actual class JavaScriptEngine actual constructor() { private val quickJs: QuickJs = QuickJs.create() private val json: Json = Json.Default @@ -27,7 +27,7 @@ actual class JavaScriptEngine actual constructor(){ return try { internalEvaluate(context, script) } catch (exception: QuickJsException) { - throw JavaScriptEvaluationException(exception) + throw JavaScriptEvaluationException(exception, exception.message) } } @@ -41,22 +41,33 @@ actual class JavaScriptEngine actual constructor(){ context: Map, script: String ): JsType { - context.forEach { pair -> - quickJs.set(pair.key, pair.value.javaClass, pair.value) - } - val result = quickJs.evaluate(script) + val scriptWithContext = convertContextMapToJsScript(context) + script + val result = quickJs.evaluate(scriptWithContext) return handleQuickJsResult(result) } + private fun convertContextMapToJsScript(context: Map): String { + if (context.isEmpty()) return "" + + return context.mapNotNull { pair -> + pair.value.value?.let { "var ${pair.key} = $it;" } + }.joinToString(separator = "") + } + private fun handleQuickJsResult(result: Any?): JsType { - return when (result) { + return when { result == null -> JsType.Null - is Boolean -> JsType.Bool(result) - is Int -> JsType.IntNum(result) - is Double -> JsType.DoubleNum(result) - is Float -> JsType.DoubleNum(result.toDouble()) - is String -> try { - JsType.Json(json.encodeToJsonElement(result)) + result is Boolean -> JsType.Bool(result) + result is Int -> JsType.IntNum(result) + result is Double -> JsType.DoubleNum(result) + result is Float -> JsType.DoubleNum(result.toDouble()) + result is String -> try { + val serializeResult = json.parseToJsonElement(result) + if (serializeResult is JsonObject) { + JsType.Json(serializeResult) + } else { + JsType.Str(result) + } } catch (ex: SerializationException) { JsType.Str(result) } diff --git a/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JsType.kt b/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JsType.kt index 4bbdc4d..dd62354 100644 --- a/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JsType.kt +++ b/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JsType.kt @@ -19,6 +19,18 @@ sealed class JsType { object Null : JsType() } +val JsType.value: Any? + get() { + return when (this) { + is JsType.Bool -> value + is JsType.Str -> value + is JsType.IntNum -> value + is JsType.DoubleNum -> value + is JsType.Json -> value.toString() + is JsType.Null -> null + } + } + fun JsType.boolValue(): Boolean = (this as JsType.Bool).value fun JsType.stringValue(): String = (this as JsType.Str).value fun JsType.intValue(): Int = (this as JsType.IntNum).value From 9fd5a5c918d25a4493aba4076372a0409cd4abfa Mon Sep 17 00:00:00 2001 From: tetraquark Date: Tue, 15 Jun 2021 19:17:24 +0700 Subject: [PATCH 12/18] implementation of JavaScriptEngineTest --- .../moko/javascript/JavaScriptEngineTest.kt | 139 ++++++++++++++++-- 1 file changed, 130 insertions(+), 9 deletions(-) diff --git a/javascript/src/androidAndroidTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt b/javascript/src/androidAndroidTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt index ec1cf7a..b99fa2c 100644 --- a/javascript/src/androidAndroidTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt +++ b/javascript/src/androidAndroidTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt @@ -4,9 +4,15 @@ package dev.icerock.moko.javascript +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.encodeToJsonElement import org.junit.After import org.junit.Before import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull +import kotlin.test.assertTrue class JavaScriptEngineTest { private var _javaScriptEngine: JavaScriptEngine? = null @@ -25,16 +31,131 @@ class JavaScriptEngineTest { } @Test - fun equalityTest() { - val jsScript = """ - var a = 1 + 2; - a - """.trimIndent() - val result = javaScriptEngine.evaluate(emptyMap(), jsScript) + fun evaluationTest() { + assertEquals( + expected = 101, + actual = javaScriptEngine.evaluate( + context = emptyMap(), + script = """ + var a = 1 + 100; + a + """.trimIndent() + ).intValue() + ) - println("DBG $result") + assertEquals( + expected = 1.5, + actual = javaScriptEngine.evaluate( + context = emptyMap(), + script = """ + var a = 0.5 + 1.0; + a + """.trimIndent() + ).doubleValue() + ) - //assertTrue { result is JsType.IntNum } - //assertEquals(expected = 3, actual = result.intValue()) + assertEquals( + expected = "string", + actual = javaScriptEngine.evaluate( + context = emptyMap(), + script = """ + var a = "string"; + a + """.trimIndent() + ).stringValue() + ) + + assertEquals( + expected = false, + actual = javaScriptEngine.evaluate( + context = emptyMap(), + script = """ + var a = false == true; + a + """.trimIndent() + ).boolValue() + ) + + assertTrue { + javaScriptEngine.evaluate( + context = emptyMap(), + script = """ + var obj = {male:"Ford", model:"Mustang", number:10}; + JSON.stringify(obj); + """.trimIndent() + ).jsonValue() is JsonObject + } + + assertNull( + javaScriptEngine.evaluate( + context = emptyMap(), + script = """ + var a = undefined + a + """.trimIndent() + ).nullValue() + ) + } + + @Test + fun `testJsonContext`() { + val form = mapOf("selector_1" to "first_value", "selector_2" to "second_value") + val profile = mapOf("email" to "test@test.com") + val formJson = Json.encodeToJsonElement(form) + val profileJson = Json.encodeToJsonElement(profile) + val context: Map = mapOf( + "form" to JsType.Json(formJson), + "profile" to JsType.Json(profileJson) + ) + + assertEquals( + expected = JsType.Bool(true), + actual = javaScriptEngine.evaluate( + context = context, + script = "form.selector_1 == \"first_value\"" + ) + ) + + assertEquals( + expected = JsType.Bool(true), + actual = javaScriptEngine.evaluate(context = context, script = "profile.email != null") + ) + + assertEquals( + expected = JsType.Str("test@test.com"), + actual = javaScriptEngine.evaluate(context = context, script = "profile.email") + ) + + assertEquals( + expected = JsType.Null, + actual = javaScriptEngine.evaluate(context = context, script = "profile.first_name") + ) + } + + @Test + fun testPlusScript() { + val list = listOf(5, 15) + val listJson = Json.encodeToJsonElement(list) + val context: Map = mapOf( + "list" to JsType.Json(listJson), + "number" to JsType.IntNum(4), + "doubleString" to JsType.Str("\" Hello \"") + ) + + // On iOS got JsType.DoubleNum here + assertEquals( + expected = JsType.IntNum(19), + actual = javaScriptEngine.evaluate(context = context, script = "list[1]+number") + ) + + // FIXME: Falls with exception + // Script: var list = [5,15];var number = 4;var doubleString = " Hello ";doubleString+list[0] + // Exception: + // IllegalStateException: Reader has not consumed the whole input: + // JsonReader(source=' Hello 5', currentPosition=8, tokenClass=0, tokenPosition=7, offset=7) +// assertEquals( +// actual = JsType.Str(" Hello 5"), +// expected = javaScriptEngine.evaluate(context = context, script = "doubleString+list[0]") +// ) } } From 78bf5cc462dfc17be9eaddbab063b12bef483cfe Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Wed, 16 Jun 2021 10:06:16 +0700 Subject: [PATCH 13/18] tests sharing and improvements of engine --- buildSrc/src/main/kotlin/Deps.kt | 1 + javascript/build.gradle.kts | 14 ++ .../moko/javascript/JavaScriptEngineTest.kt | 161 ------------------ .../moko/javascript/JavaScriptEngine.kt | 37 +++- .../dev/icerock/moko/javascript/JsType.kt | 28 +-- .../moko/javascript/JavaScriptEngine.kt | 18 +- .../moko/javascript/JavaScriptEngineTest.kt | 44 ----- .../JavaScriptEngineCrossTypesTests.kt | 58 +++++++ .../javascript/JavaScriptEngineJsonTests.kt | 72 ++++++++ .../JavaScriptEngineSimpleTypesTests.kt | 111 ++++++++++++ 10 files changed, 300 insertions(+), 244 deletions(-) delete mode 100644 javascript/src/androidAndroidTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt delete mode 100644 javascript/src/iosTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt create mode 100644 javascript/src/mobileDeviceTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineCrossTypesTests.kt create mode 100644 javascript/src/mobileDeviceTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineJsonTests.kt create mode 100644 javascript/src/mobileDeviceTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineSimpleTypesTests.kt diff --git a/buildSrc/src/main/kotlin/Deps.kt b/buildSrc/src/main/kotlin/Deps.kt index bb3baff..d0d8bbf 100644 --- a/buildSrc/src/main/kotlin/Deps.kt +++ b/buildSrc/src/main/kotlin/Deps.kt @@ -30,6 +30,7 @@ object Deps { const val material = "com.google.android.material:material:$materialDesignVersion" const val lifecycle = "androidx.lifecycle:lifecycle-extensions:$androidLifecycleVersion" + const val kotlinTestJUnit = "org.jetbrains.kotlin:kotlin-test-junit:$kotlinTestVersion" const val testRunner = "androidx.test:runner:$androidCoreTestingVersion" const val testRules = "androidx.test:rules:$androidCoreTestingVersion" const val testJUnitExt = "androidx.test.ext:junit:$testJUnitExtVersion" diff --git a/javascript/build.gradle.kts b/javascript/build.gradle.kts index 51214db..2776414 100644 --- a/javascript/build.gradle.kts +++ b/javascript/build.gradle.kts @@ -18,7 +18,20 @@ android { exclude("META-INF/*.kotlin_module") exclude("META-INF/AL2.0") exclude("META-INF/LGPL2.1") + } +} + +kotlin { + sourceSets { + val mobileDeviceTest by creating + + val commonTest by getting + val iosTest by getting + val androidAndroidTest by getting + mobileDeviceTest.dependsOn(commonTest) + iosTest.dependsOn(mobileDeviceTest) + androidAndroidTest.dependsOn(mobileDeviceTest) } } @@ -31,6 +44,7 @@ dependencies { commonTestImplementation(Deps.Libs.MultiPlatform.kotlinTestAnnotations) commonTestImplementation(Deps.Libs.MultiPlatform.mokoTest) + androidTestImplementation(Deps.Libs.Android.kotlinTestJUnit) androidTestImplementation(Deps.Libs.Android.testRunner) androidTestImplementation(Deps.Libs.Android.testRules) androidTestImplementation(Deps.Libs.Android.testJUnitExt) diff --git a/javascript/src/androidAndroidTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt b/javascript/src/androidAndroidTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt deleted file mode 100644 index b99fa2c..0000000 --- a/javascript/src/androidAndroidTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -package dev.icerock.moko.javascript - -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.encodeToJsonElement -import org.junit.After -import org.junit.Before -import org.junit.Test -import kotlin.test.assertEquals -import kotlin.test.assertNull -import kotlin.test.assertTrue - -class JavaScriptEngineTest { - private var _javaScriptEngine: JavaScriptEngine? = null - - private val javaScriptEngine: JavaScriptEngine - get() = _javaScriptEngine!! - - @Before - fun init() { - _javaScriptEngine = JavaScriptEngine() - } - - @After - fun dispose() { - javaScriptEngine.close() - } - - @Test - fun evaluationTest() { - assertEquals( - expected = 101, - actual = javaScriptEngine.evaluate( - context = emptyMap(), - script = """ - var a = 1 + 100; - a - """.trimIndent() - ).intValue() - ) - - assertEquals( - expected = 1.5, - actual = javaScriptEngine.evaluate( - context = emptyMap(), - script = """ - var a = 0.5 + 1.0; - a - """.trimIndent() - ).doubleValue() - ) - - assertEquals( - expected = "string", - actual = javaScriptEngine.evaluate( - context = emptyMap(), - script = """ - var a = "string"; - a - """.trimIndent() - ).stringValue() - ) - - assertEquals( - expected = false, - actual = javaScriptEngine.evaluate( - context = emptyMap(), - script = """ - var a = false == true; - a - """.trimIndent() - ).boolValue() - ) - - assertTrue { - javaScriptEngine.evaluate( - context = emptyMap(), - script = """ - var obj = {male:"Ford", model:"Mustang", number:10}; - JSON.stringify(obj); - """.trimIndent() - ).jsonValue() is JsonObject - } - - assertNull( - javaScriptEngine.evaluate( - context = emptyMap(), - script = """ - var a = undefined - a - """.trimIndent() - ).nullValue() - ) - } - - @Test - fun `testJsonContext`() { - val form = mapOf("selector_1" to "first_value", "selector_2" to "second_value") - val profile = mapOf("email" to "test@test.com") - val formJson = Json.encodeToJsonElement(form) - val profileJson = Json.encodeToJsonElement(profile) - val context: Map = mapOf( - "form" to JsType.Json(formJson), - "profile" to JsType.Json(profileJson) - ) - - assertEquals( - expected = JsType.Bool(true), - actual = javaScriptEngine.evaluate( - context = context, - script = "form.selector_1 == \"first_value\"" - ) - ) - - assertEquals( - expected = JsType.Bool(true), - actual = javaScriptEngine.evaluate(context = context, script = "profile.email != null") - ) - - assertEquals( - expected = JsType.Str("test@test.com"), - actual = javaScriptEngine.evaluate(context = context, script = "profile.email") - ) - - assertEquals( - expected = JsType.Null, - actual = javaScriptEngine.evaluate(context = context, script = "profile.first_name") - ) - } - - @Test - fun testPlusScript() { - val list = listOf(5, 15) - val listJson = Json.encodeToJsonElement(list) - val context: Map = mapOf( - "list" to JsType.Json(listJson), - "number" to JsType.IntNum(4), - "doubleString" to JsType.Str("\" Hello \"") - ) - - // On iOS got JsType.DoubleNum here - assertEquals( - expected = JsType.IntNum(19), - actual = javaScriptEngine.evaluate(context = context, script = "list[1]+number") - ) - - // FIXME: Falls with exception - // Script: var list = [5,15];var number = 4;var doubleString = " Hello ";doubleString+list[0] - // Exception: - // IllegalStateException: Reader has not consumed the whole input: - // JsonReader(source=' Hello 5', currentPosition=8, tokenClass=0, tokenPosition=7, offset=7) -// assertEquals( -// actual = JsType.Str(" Hello 5"), -// expected = javaScriptEngine.evaluate(context = context, script = "doubleString+list[0]") -// ) - } -} diff --git a/javascript/src/androidMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt b/javascript/src/androidMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt index 138ad65..302db37 100644 --- a/javascript/src/androidMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt +++ b/javascript/src/androidMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt @@ -7,7 +7,9 @@ package dev.icerock.moko.javascript import app.cash.quickjs.QuickJs import app.cash.quickjs.QuickJsException import kotlinx.serialization.SerializationException +import kotlinx.serialization.builtins.serializer import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonObject actual class JavaScriptEngine actual constructor() { @@ -46,22 +48,39 @@ actual class JavaScriptEngine actual constructor() { return handleQuickJsResult(result) } + // TODO fix pass of arguments - now wrapping of string and json invalid and will be broken on multilined strings private fun convertContextMapToJsScript(context: Map): String { if (context.isEmpty()) return "" return context.mapNotNull { pair -> - pair.value.value?.let { "var ${pair.key} = $it;" } + prepareValueForJs(pair.value)?.let { "var ${pair.key} = $it;" } }.joinToString(separator = "") } + private fun prepareValueForJs(valueWrapper: JsType): String? { + return when (valueWrapper) { + is JsType.Bool -> valueWrapper.value.toString() + is JsType.DoubleNum -> valueWrapper.value.toString() + is JsType.Json -> valueWrapper.value.let { + Json.encodeToString(JsonElement.serializer(), it) + }.let { + "JSON.parse(\"$it\")" + } + is JsType.Str -> valueWrapper.value.let { + "\"$it\"" + } + JsType.Null -> null + } + } + private fun handleQuickJsResult(result: Any?): JsType { - return when { - result == null -> JsType.Null - result is Boolean -> JsType.Bool(result) - result is Int -> JsType.IntNum(result) - result is Double -> JsType.DoubleNum(result) - result is Float -> JsType.DoubleNum(result.toDouble()) - result is String -> try { + return when (result) { + null -> JsType.Null + is Boolean -> JsType.Bool(result) + is Int -> JsType.DoubleNum(result.toDouble()) + is Double -> JsType.DoubleNum(result) + is Float -> JsType.DoubleNum(result.toDouble()) + is String -> try { val serializeResult = json.parseToJsonElement(result) if (serializeResult is JsonObject) { JsType.Json(serializeResult) @@ -70,6 +89,8 @@ actual class JavaScriptEngine actual constructor() { } } catch (ex: SerializationException) { JsType.Str(result) + } catch (ex: IllegalStateException) { + JsType.Str(result) } else -> throw JavaScriptEvaluationException( message = "Impossible JavaScriptEngine handler state with result [$result]" diff --git a/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JsType.kt b/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JsType.kt index dd62354..f711811 100644 --- a/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JsType.kt +++ b/javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JsType.kt @@ -7,33 +7,23 @@ package dev.icerock.moko.javascript import kotlinx.serialization.json.JsonElement sealed class JsType { - data class Bool(val value: Boolean): JsType() - data class Str(val value: String): JsType() - data class IntNum(val value: Int): JsType() - data class DoubleNum(val value: Double): JsType() - data class Json(val value: JsonElement): JsType() + data class Bool(override val value: Boolean) : JsType() + data class Str(override val value: String) : JsType() + data class DoubleNum(override val value: Double) : JsType() + data class Json(override val value: JsonElement) : JsType() /** * For "undefined" and "null". */ - object Null : JsType() -} - -val JsType.value: Any? - get() { - return when (this) { - is JsType.Bool -> value - is JsType.Str -> value - is JsType.IntNum -> value - is JsType.DoubleNum -> value - is JsType.Json -> value.toString() - is JsType.Null -> null - } + object Null : JsType() { + override val value: Any? get() = null } + abstract val value: Any? +} + fun JsType.boolValue(): Boolean = (this as JsType.Bool).value fun JsType.stringValue(): String = (this as JsType.Str).value -fun JsType.intValue(): Int = (this as JsType.IntNum).value fun JsType.doubleValue(): Double = (this as JsType.DoubleNum).value fun JsType.jsonValue(): JsonElement = (this as JsType.Json).value fun JsType.nullValue(): Any? = (this as JsType.Null).let { null } diff --git a/javascript/src/iosMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt b/javascript/src/iosMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt index 92c9a01..61bce11 100644 --- a/javascript/src/iosMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt +++ b/javascript/src/iosMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt @@ -33,7 +33,7 @@ actual class JavaScriptEngine actual constructor() { context.forEach { jsContext.setObject( - `object` = it.value.getValue(), + `object` = prepareValueForJsContext(it.value), forKeyedSubscript = NSString.create(string = it.key) ) } @@ -46,6 +46,11 @@ actual class JavaScriptEngine actual constructor() { actual fun close() { // Nothing to do here } + + private fun prepareValueForJsContext(valueWrapper: JsType): Any? { + return if (valueWrapper is JsType.Json) valueWrapper.value.getValue() + else valueWrapper.value + } } private fun JsonObject.toNSDictionary(): NSDictionary { @@ -74,17 +79,6 @@ private fun JsonElement.getValue(): Any? { ?: (this as? JsonPrimitive)?.content } -private fun JsType.getValue(): Any? { - return when (this) { - is JsType.Bool -> value - is JsType.Str -> value - is JsType.IntNum -> value - is JsType.DoubleNum -> value - is JsType.Json -> value.getValue() - is JsType.Null -> null - } -} - private fun JSValue.toMokoJSType(): JsType { return when { isBoolean -> JsType.Bool(toBool()) diff --git a/javascript/src/iosTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt b/javascript/src/iosTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt deleted file mode 100644 index 5a622d1..0000000 --- a/javascript/src/iosTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineTest.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. - */ - -package dev.icerock.moko.javascript - -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.encodeToJsonElement - -class JavaScriptEngineTest { - @Test - fun `test json context`() { - val form = mapOf("selector_1" to "first_value", "selector_2" to "second_value") - val profile = mapOf("email" to "test@test.com") - val formJson = Json.encodeToJsonElement(form) - val profileJson = Json.encodeToJsonElement(profile) - val context: Map = mapOf("form" to JsType.Json(formJson), "profile" to JsType.Json(profileJson)) - - val jsEngine = JavaScriptEngine() - - assertEquals(JsType.Bool(true), jsEngine.evaluate(context = context, script = "form.selector_1 == \"first_value\"")) - - assertEquals(JsType.Bool(true), jsEngine.evaluate(context = context, script = "profile.email != null")) - - assertEquals(JsType.Str("test@test.com"), jsEngine.evaluate(context = context, script = "profile.email")) - - assertEquals(JsType.Null, jsEngine.evaluate(context = context, script = "profile.first_name")) - } - - @Test - fun `test plus script`() { - val list = listOf(5, 15) - val listJson = Json.encodeToJsonElement(list) - val context: Map = mapOf("list" to JsType.Json(listJson), "number" to JsType.IntNum(4), "doubleString" to JsType.Str(" Hello ")) - - val jsEngine = JavaScriptEngine() - - assertEquals(JsType.DoubleNum(19.0), jsEngine.evaluate(context = context, script = "list[1]+number")) - - assertEquals(JsType.Str(" Hello 5"), jsEngine.evaluate(context = context, script = "doubleString+list[0]")) - } -} diff --git a/javascript/src/mobileDeviceTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineCrossTypesTests.kt b/javascript/src/mobileDeviceTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineCrossTypesTests.kt new file mode 100644 index 0000000..eb88bf4 --- /dev/null +++ b/javascript/src/mobileDeviceTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineCrossTypesTests.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. + */ + +package dev.icerock.moko.javascript + +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.encodeToJsonElement +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Ignore +import kotlin.test.Test +import kotlin.test.assertEquals + +class JavaScriptEngineCrossTypesTests { + private lateinit var javaScriptEngine: JavaScriptEngine + private lateinit var context: Map + + @BeforeTest + fun init() { + javaScriptEngine = JavaScriptEngine() + + val list = listOf(5, 15) + val listJson = Json.encodeToJsonElement(list) + + context = mapOf( + "list" to JsType.Json(listJson), + "number" to JsType.DoubleNum(4.0), + "doubleString" to JsType.Str(" Hello ") + ) + } + + @AfterTest + fun dispose() { + javaScriptEngine.close() + } + + @Test + fun numberWithNumberSumCheck() { + assertEquals( + expected = JsType.DoubleNum(19.0), + actual = javaScriptEngine.evaluate(context = context, script = "list[1]+number") + ) + } + + @Test + fun stringWithNumberSumCheck() { + // FIXME: Falls with exception + // Script: var list = [5,15];var number = 4;var doubleString = " Hello ";doubleString+list[0] + // Exception: + // IllegalStateException: Reader has not consumed the whole input: + // JsonReader(source=' Hello 5', currentPosition=8, tokenClass=0, tokenPosition=7, offset=7) + assertEquals( + actual = JsType.Str(" Hello 5"), + expected = javaScriptEngine.evaluate(context = context, script = "doubleString+list[0]") + ) + } +} diff --git a/javascript/src/mobileDeviceTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineJsonTests.kt b/javascript/src/mobileDeviceTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineJsonTests.kt new file mode 100644 index 0000000..16e210d --- /dev/null +++ b/javascript/src/mobileDeviceTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineJsonTests.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. + */ + +package dev.icerock.moko.javascript + +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.encodeToJsonElement +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class JavaScriptEngineJsonTests { + private lateinit var javaScriptEngine: JavaScriptEngine + private lateinit var context: Map + + @BeforeTest + fun init() { + javaScriptEngine = JavaScriptEngine() + + val form = mapOf("selector_1" to "first_value", "selector_2" to "second_value") + val profile = mapOf("email" to "test@test.com") + val formJson = Json.encodeToJsonElement(form) + val profileJson = Json.encodeToJsonElement(profile) + + context = mapOf( + "form" to JsType.Json(formJson), + "profile" to JsType.Json(profileJson) + ) + } + + @AfterTest + fun dispose() { + javaScriptEngine.close() + } + + @Test + fun formSelectorCheck() { + assertEquals( + expected = JsType.Bool(true), + actual = javaScriptEngine.evaluate( + context = context, + script = "form.selector_1 == \"first_value\"" + ) + ) + } + + @Test + fun profileEmailCheck() { + assertEquals( + expected = JsType.Bool(true), + actual = javaScriptEngine.evaluate(context = context, script = "profile.email != null") + ) + } + + @Test + fun profileStringGet() { + assertEquals( + expected = JsType.Str("test@test.com"), + actual = javaScriptEngine.evaluate(context = context, script = "profile.email") + ) + } + + @Test + fun profileNullGet() { + assertEquals( + expected = JsType.Null, + actual = javaScriptEngine.evaluate(context = context, script = "profile.first_name") + ) + } +} diff --git a/javascript/src/mobileDeviceTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineSimpleTypesTests.kt b/javascript/src/mobileDeviceTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineSimpleTypesTests.kt new file mode 100644 index 0000000..ae27b4f --- /dev/null +++ b/javascript/src/mobileDeviceTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineSimpleTypesTests.kt @@ -0,0 +1,111 @@ +/* + * Copyright 2021 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license. + */ + +package dev.icerock.moko.javascript + +import kotlinx.serialization.json.JsonObject +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Ignore +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull +import kotlin.test.assertTrue + +class JavaScriptEngineSimpleTypesTests { + private lateinit var javaScriptEngine: JavaScriptEngine + + @BeforeTest + fun init() { + javaScriptEngine = JavaScriptEngine() + } + + @AfterTest + fun dispose() { + javaScriptEngine.close() + } + + @Test + fun integerSumCheck() { + assertEquals( + expected = 101.0, + actual = javaScriptEngine.evaluate( + context = emptyMap(), + script = """ + var a = 1 + 100; + a + """.trimIndent() + ).doubleValue() + ) + } + + @Test + fun doubleSumCheck() { + assertEquals( + expected = 1.5, + actual = javaScriptEngine.evaluate( + context = emptyMap(), + script = """ + var a = 0.5 + 1.0; + a + """.trimIndent() + ).doubleValue() + ) + } + + @Test + fun stringReturnCheck() { + assertEquals( + expected = "string", + actual = javaScriptEngine.evaluate( + context = emptyMap(), + script = """ + var a = "string"; + a + """.trimIndent() + ).stringValue() + ) + } + + @Test + fun booleanCheck() { + assertEquals( + expected = false, + actual = javaScriptEngine.evaluate( + context = emptyMap(), + script = """ + var a = false == true; + a + """.trimIndent() + ).boolValue() + ) + } + + @Ignore // on iOS we got StrValue, on Android - JsonValue + @Test + fun jsonReadCheck() { + assertTrue { + javaScriptEngine.evaluate( + context = emptyMap(), + script = """ + var obj = {male:"Ford", model:"Mustang", number:10}; + JSON.stringify(obj); + """.trimIndent() + ).jsonValue() is JsonObject + } + } + + @Test + fun undefinedCheck() { + assertNull( + javaScriptEngine.evaluate( + context = emptyMap(), + script = """ + var a = undefined + a + """.trimIndent() + ).nullValue() + ) + } +} From b26576287c28007e677d9854743385a281187498 Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Wed, 16 Jun 2021 10:06:31 +0700 Subject: [PATCH 14/18] publishing for mavenCentral --- .../kotlin/publication-convention.gradle.kts | 76 +++++++++++++++++-- 1 file changed, 69 insertions(+), 7 deletions(-) diff --git a/buildSrc/src/main/kotlin/publication-convention.gradle.kts b/buildSrc/src/main/kotlin/publication-convention.gradle.kts index 0ccf20c..51f0985 100644 --- a/buildSrc/src/main/kotlin/publication-convention.gradle.kts +++ b/buildSrc/src/main/kotlin/publication-convention.gradle.kts @@ -1,17 +1,79 @@ +import java.util.Base64 + plugins { id("org.gradle.maven-publish") + id("signing") } -publishing { - group = "dev.icerock.moko" - version = Deps.mokoJavascriptVersion +group = "dev.icerock.moko" +version = Deps.mokoJavascriptVersion + +val javadocJar by tasks.registering(Jar::class) { + archiveClassifier.set("javadoc") +} - repositories.maven("https://api.bintray.com/maven/icerockdev/moko/moko-javascript/;publish=1") { - name = "bintray" +publishing { + repositories.maven("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/") { + name = "OSSRH" credentials { - username = System.getenv("BINTRAY_USER") - password = System.getenv("BINTRAY_KEY") + username = System.getenv("OSSRH_USER") + password = System.getenv("OSSRH_KEY") + } + } + + publications.withType { + // Stub javadoc.jar artifact + artifact(javadocJar.get()) + + // Provide artifacts information requited by Maven Central + pom { + name.set("MOKO JavaScript") + description.set("JavaScript code evaluation from common code for Kotlin Multiplatform Mobile") + url.set("https://github.com/icerockdev/moko-javascript") + licenses { + license { + name.set("Apache-2.0") + distribution.set("repo") + url.set("https://github.com/icerockdev/moko-javascript/blob/master/LICENSE.md") + } + } + + developers { + developer { + id.set("Tetraquark") + name.set("Vladislav Areshkin") + email.set("vareshkin@icerockdev.com") + } + developer { + id.set("Dorofeev") + name.set("Andrey Dorofeev") + email.set("adorofeev@icerockdev.com") + } + developer { + id.set("Alex009") + name.set("Aleksey Mikhailov") + email.set("aleksey.mikhailov@icerockdev.com") + } + } + + scm { + connection.set("scm:git:ssh://github.com/icerockdev/moko-javascript.git") + developerConnection.set("scm:git:ssh://github.com/icerockdev/moko-javascript.git") + url.set("https://github.com/icerockdev/moko-javascript") + } + } + } + + signing { + val signingKeyId: String? = System.getenv("SIGNING_KEY_ID") + val signingPassword: String? = System.getenv("SIGNING_PASSWORD") + val signingKey: String? = System.getenv("SIGNING_KEY")?.let { base64Key -> + String(Base64.getDecoder().decode(base64Key)) + } + if (signingKeyId != null) { + useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) + sign(publishing.publications) } } } From ef868f1c14adc8ddbe9771cb7fb666a77604cc56 Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Wed, 16 Jun 2021 12:29:09 +0700 Subject: [PATCH 15/18] fix android tests run --- javascript/build.gradle.kts | 7 +++++++ .../kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/javascript/build.gradle.kts b/javascript/build.gradle.kts index 2776414..eb26148 100644 --- a/javascript/build.gradle.kts +++ b/javascript/build.gradle.kts @@ -19,6 +19,13 @@ android { exclude("META-INF/AL2.0") exclude("META-INF/LGPL2.1") } + + sourceSets { + getByName("androidTest").java.srcDirs( + file("src/androidAndroidTest/kotlin"), + file("src/mobileDeviceTest/kotlin") + ) + } } kotlin { diff --git a/javascript/src/androidMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt b/javascript/src/androidMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt index 302db37..07917c3 100644 --- a/javascript/src/androidMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt +++ b/javascript/src/androidMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt @@ -43,7 +43,7 @@ actual class JavaScriptEngine actual constructor() { context: Map, script: String ): JsType { - val scriptWithContext = convertContextMapToJsScript(context) + script + val scriptWithContext = convertContextMapToJsScript(context) + script + "\n" val result = quickJs.evaluate(scriptWithContext) return handleQuickJsResult(result) } @@ -63,10 +63,14 @@ actual class JavaScriptEngine actual constructor() { is JsType.DoubleNum -> valueWrapper.value.toString() is JsType.Json -> valueWrapper.value.let { Json.encodeToString(JsonElement.serializer(), it) + }.let { + it.replace("\"", "\\\"") }.let { "JSON.parse(\"$it\")" } is JsType.Str -> valueWrapper.value.let { + it.replace("\"", "\\\"") + }.let { "\"$it\"" } JsType.Null -> null From 1184c0842d460f7ec911e408aa821c69c037ec74 Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Wed, 16 Jun 2021 12:29:17 +0700 Subject: [PATCH 16/18] fix sample compilation --- .../kotlin/com/icerockdev/library/Calculator.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/Calculator.kt b/sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/Calculator.kt index 32427f7..c377903 100644 --- a/sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/Calculator.kt +++ b/sample/mpp-library/src/commonMain/kotlin/com/icerockdev/library/Calculator.kt @@ -12,13 +12,13 @@ class Calculator { val engine = JavaScriptEngine() val testScript = "a+b" - val intA = a.toIntOrNull() - val intB = b.toIntOrNull() + val numberA = a.toDoubleOrNull() + val numberB = b.toDoubleOrNull() - val context = if (intA != null && intB != null) { + val context = if (numberA != null && numberB != null) { mapOf( - "a" to JsType.IntNum(intA), - "b" to JsType.IntNum(intB) + "a" to JsType.DoubleNum(numberA), + "b" to JsType.DoubleNum(numberB) ) } else { mapOf( From 1654d3f25bb073c914c9d48fba8b742961185595 Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Wed, 16 Jun 2021 12:33:25 +0700 Subject: [PATCH 17/18] remove commented code --- .../moko/javascript/JavaScriptEngineCrossTypesTests.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/javascript/src/mobileDeviceTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineCrossTypesTests.kt b/javascript/src/mobileDeviceTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineCrossTypesTests.kt index eb88bf4..bba83f5 100644 --- a/javascript/src/mobileDeviceTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineCrossTypesTests.kt +++ b/javascript/src/mobileDeviceTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineCrossTypesTests.kt @@ -45,11 +45,6 @@ class JavaScriptEngineCrossTypesTests { @Test fun stringWithNumberSumCheck() { - // FIXME: Falls with exception - // Script: var list = [5,15];var number = 4;var doubleString = " Hello ";doubleString+list[0] - // Exception: - // IllegalStateException: Reader has not consumed the whole input: - // JsonReader(source=' Hello 5', currentPosition=8, tokenClass=0, tokenPosition=7, offset=7) assertEquals( actual = JsType.Str(" Hello 5"), expected = javaScriptEngine.evaluate(context = context, script = "doubleString+list[0]") From 577703777668acf502bb0763ae8915ad6f49b689 Mon Sep 17 00:00:00 2001 From: Aleksey Mikhailov Date: Wed, 16 Jun 2021 12:38:35 +0700 Subject: [PATCH 18/18] update readme --- README.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 51166af..2ff2c87 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -[![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0) [![Download](https://api.bintray.com/packages/icerockdev/moko/moko-javascript/images/download.svg) ](https://bintray.com/icerockdev/moko/moko-javascript/_latestVersion) ![kotlin-version](https://img.shields.io/badge/kotlin-1.4.32-orange) +[![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0) [![Download](https://img.shields.io/maven-central/v/dev.icerock.moko/javascript) ](https://repo1.maven.org/maven2/dev/icerock/moko/javascript) ![kotlin-version](https://img.shields.io/badge/kotlin-1.4.32-orange) # Mobile Kotlin javascript -This is a Kotlin MultiPlatform library that ... +This is a Kotlin MultiPlatform library that allows you to run JavaScript code from common Kotlin code ## Table of Contents - [Features](#features) @@ -14,7 +14,8 @@ This is a Kotlin MultiPlatform library that ... - [License](#license) ## Features -... +- Evaluate JavaScript code from Kotlin common code +- Pass objects to JavaScript as global vars ## Requirements - Gradle version 6.0+ @@ -43,7 +44,16 @@ dependencies { ``` ## Usage -... +```kotlin +val javaScriptEngine = JavaScriptEngine() +val result: JsType = javaScriptEngine.evaluate( + context = emptyMap(), + script = """ "Hello" + "World" """.trimIndent() +) +if (result is JsType.Str) { + println(result.value) +} +``` ## Samples More examples can be found in the [sample directory](sample).