Skip to content

Commit ae2271a

Browse files
committed
rework jstype
1 parent 342a58c commit ae2271a

File tree

7 files changed

+111
-43
lines changed

7 files changed

+111
-43
lines changed

gradle/libs.versions.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
[versions]
2-
kotlinTestVersion = "1.5.20"
2+
kotlinTestVersion = "1.6.21"
33
androidAppCompatVersion = "1.2.0"
44
materialDesignVersion = "1.0.0"
55
androidLifecycleVersion = "2.1.0"
66
androidCoreTestingVersion = "1.3.0"
77
testJUnitExtVersion = "1.1.2"
88
quickjsVersion = "0.9.0"
9-
coroutinesVersion = "1.5.0-native-mt"
10-
kotlinxSerializationVersion = "1.1.0"
11-
mokoTestVersion = "0.4.0"
12-
mokoJavascriptVersion = "0.2.0"
9+
coroutinesVersion = "1.6.0-native-mt"
10+
kotlinxSerializationVersion = "1.3.3"
11+
mokoTestVersion = "0.6.1"
12+
mokoJavascriptVersion = "0.3.0"
1313

1414

1515
[libraries]

javascript-build-logic/build.gradle.kts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ repositories {
1010
}
1111

1212
dependencies {
13-
api("dev.icerock:mobile-multiplatform:0.12.0")
14-
api("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20")
15-
api("com.android.tools.build:gradle:4.2.1")
13+
api("dev.icerock:mobile-multiplatform:0.14.1")
14+
api("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21")
15+
api("com.android.tools.build:gradle:7.0.1")
1616
api("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.15.0")
1717
}

javascript-build-logic/src/main/kotlin/multiplatform-library-convention.gradle.kts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,23 @@ plugins {
1111

1212
kotlin {
1313
ios()
14+
iosSimulatorArm64()
1415
android {
1516
publishLibraryVariants("release", "debug")
1617
}
1718
sourceSets {
19+
val iosSimulatorArm64Main by getting
20+
val iosSimulatorArm64Test by getting
21+
1822
val mobileDeviceTest by creating
1923

2024
val commonTest by getting
25+
val iosMain by getting
2126
val iosTest by getting
2227
val androidAndroidTest by getting
2328

29+
iosSimulatorArm64Main.dependsOn(iosMain)
30+
iosSimulatorArm64Test.dependsOn(iosTest)
2431

2532
mobileDeviceTest.dependsOn(commonTest)
2633
iosTest.dependsOn(mobileDeviceTest)

javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@ expect class JavaScriptEngine() {
1111
* @throws JavaScriptEvaluationException in case of an error in the engine evaluation or if the
1212
* engine has already been closed.
1313
*/
14-
fun evaluate(script: String): JsType
14+
fun evaluate(context: Map<String, JsType>, script: String): JsType
1515

1616
/**
1717
* Set some [context] with external [context].
1818
*/
19-
fun setContextObjects(context: Map<String, JsType>)
19+
fun setContextObjects(vararg context: Pair<String, Any>)
2020

2121
/**
2222
* Turns some [JsType] to JsonString.
2323
*/
24-
fun objectToJsonString(value: JsType): String?
24+
// fun objectToJsonString(value: JsType): String?
2525

2626
/**
2727
* Closes the engine and releases the allocated memory.

javascript/src/commonMain/kotlin/dev/icerock/moko/javascript/JsType.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ sealed class JsType {
1111
data class Str(override val value: String) : JsType()
1212
data class DoubleNum(override val value: Double) : JsType()
1313
data class Json(override val value: JsonElement) : JsType()
14-
data class AnyValue(override val value: Any) : JsType()
1514

1615
/**
1716
* For "undefined" and "null".

javascript/src/iosMain/kotlin/dev/icerock/moko/javascript/JavaScriptEngine.kt

Lines changed: 68 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@ package dev.icerock.moko.javascript
77
import kotlinx.serialization.json.Json
88
import kotlinx.serialization.json.JsonArray
99
import kotlinx.serialization.json.JsonElement
10+
import kotlinx.serialization.json.JsonNull
1011
import kotlinx.serialization.json.JsonObject
1112
import kotlinx.serialization.json.JsonPrimitive
1213
import kotlinx.serialization.json.encodeToJsonElement
1314
import platform.Foundation.NSArray
1415
import platform.Foundation.NSDictionary
1516
import platform.Foundation.NSJSONSerialization
17+
import platform.Foundation.NSNull
18+
import platform.Foundation.NSNumber
1619
import platform.Foundation.NSString
1720
import platform.Foundation.NSUTF8StringEncoding
1821
import platform.Foundation.create
@@ -23,42 +26,60 @@ import platform.JavaScriptCore.setObject
2326

2427
actual class JavaScriptEngine actual constructor() {
2528

26-
private val jsContext = JSContext()
29+
private val jsContext = JSContext().apply {
30+
exceptionHandler = { exceptionContext, exception ->
31+
val message = "\"context = $exceptionContext, exception = $exception\""
32+
throw JavaScriptEvaluationException(cause = null, message = message)
33+
}
34+
}
2735

28-
actual fun setContextObjects(context: Map<String, JsType>) {
29-
context.forEach {
36+
actual fun setContextObjects(vararg context: Pair<String, Any>) {
37+
context.forEach { (key, value) ->
38+
val contextObject = if (value is JsType) {
39+
prepareValueForJsContext(value)
40+
} else {
41+
value
42+
}
3043
jsContext.setObject(
31-
`object` = prepareValueForJsContext(it.value),
32-
forKeyedSubscript = NSString.create(string = it.key)
44+
`object` = contextObject,
45+
forKeyedSubscript = NSString.create(string = key)
3346
)
3447
}
3548
}
3649

37-
actual fun evaluate(script: String): JsType {
38-
39-
jsContext.exceptionHandler = { exceptionContext, exception ->
40-
val message = "\"context = $exceptionContext, exception = $exception\""
41-
throw JavaScriptEvaluationException(cause = null, message = message)
50+
actual fun evaluate(context: Map<String, JsType>, script: String): JsType {
51+
context.forEach { (key, value) ->
52+
jsContext.setObject(
53+
`object` = prepareValueForJsContext(value),
54+
forKeyedSubscript = NSString.create(string = key)
55+
)
4256
}
4357

4458
val result = jsContext.evaluateScript(script)
4559

60+
context.forEach { (key, _) ->
61+
jsContext.setObject(
62+
`object` = null,
63+
forKeyedSubscript = NSString.create(string = key)
64+
)
65+
}
66+
4667
return result?.toMokoJSType() ?: JsType.Null
4768
}
4869

4970
actual fun close() {
5071
// Nothing to do here
5172
}
5273

53-
actual fun objectToJsonString(value: JsType): String? {
54-
val localContext = JSContext()
55-
localContext.setObject(
56-
`object` = prepareValueForJsContext(value),
57-
forKeyedSubscript = NSString.create(string = "objectValue")
58-
)
59-
60-
return localContext.evaluateScript("JSON.stringify(objectValue);")?.toString_()
61-
}
74+
// actual fun objectToJsonString(value: JsType): String? {
75+
// val localContext = JSContext()
76+
// localContext.setObject(
77+
// `object` = prepareValueForJsContext(value),
78+
// forKeyedSubscript = NSString.create(string = "objectValue")
79+
// )
80+
//
81+
// return localContext.evaluateScript("JSON.stringify(objectValue);")?.toString_()
82+
// }
6283

6384
private fun prepareValueForJsContext(valueWrapper: JsType): Any? {
6485
return if (valueWrapper is JsType.Json) valueWrapper.value.getValue()
@@ -97,9 +118,34 @@ private fun JSValue.toMokoJSType(): JsType {
97118
isBoolean -> JsType.Bool(toBool())
98119
isString -> JsType.Str(toString_().orEmpty())
99120
isNumber -> JsType.DoubleNum(toDouble())
100-
isObject -> JsType.AnyValue(toObject()!!)
101-
isArray -> JsType.Json(Json.encodeToJsonElement(toArray()))
121+
isObject -> this.toDictionary()!!
122+
.toJson()
123+
.let { JsType.Json(it) }
124+
isArray -> this.toArray()!!
125+
.map { it.toJsonElement() }
126+
.let { JsType.Json(JsonArray(it)) }
102127
isNull -> JsType.Null
103-
else -> JsType.AnyValue(this)
128+
else -> throw IllegalArgumentException("unknown JSValue type $this")
129+
}
130+
}
131+
132+
private fun Map<Any?, *>.toJson(): JsonObject {
133+
return this.mapKeys { it.key as String
134+
}.mapValues { it.value.toJsonElement() }
135+
.let { JsonObject(it) }
136+
}
137+
138+
/**
139+
* @see https://developer.apple.com/documentation/javascriptcore/jsvalue?language=objc#1663421
140+
*/
141+
private fun Any?.toJsonElement(): JsonElement {
142+
return when(this) {
143+
null -> JsonNull
144+
is NSNull -> JsonNull
145+
is NSString -> JsonPrimitive(this as String)
146+
is NSNumber -> JsonPrimitive(this.doubleValue)
147+
is NSDictionary -> (this as Map<Any?, *>).toJson()
148+
is NSArray -> (this as List<*>).map { it.toJsonElement() }.let { JsonArray(it) }
149+
else -> throw IllegalArgumentException("unknown JSValue type $this")
104150
}
105151
}

javascript/src/mobileDeviceTest/kotlin/dev/icerock/moko/javascript/JavaScriptEngineSimpleTypesTests.kt

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44

55
package dev.icerock.moko.javascript
66

7+
import kotlinx.serialization.json.JsonArray
8+
import kotlinx.serialization.json.JsonElement
79
import kotlinx.serialization.json.JsonObject
10+
import kotlinx.serialization.json.JsonPrimitive
811
import kotlin.test.AfterTest
912
import kotlin.test.BeforeTest
10-
import kotlin.test.Ignore
1113
import kotlin.test.Test
1214
import kotlin.test.assertEquals
1315
import kotlin.test.assertNull
@@ -82,18 +84,32 @@ class JavaScriptEngineSimpleTypesTests {
8284
)
8385
}
8486

85-
@Ignore // on iOS we got StrValue, on Android - JsonValue
87+
// @Ignore // on iOS we got StrValue, on Android - JsonValue
8688
@Test
8789
fun jsonReadCheck() {
88-
assertTrue {
89-
javaScriptEngine.evaluate(
90-
context = emptyMap(),
91-
script = """
90+
val jsonObject: JsonElement = javaScriptEngine.evaluate(
91+
context = mapOf(
92+
"test" to JsType.Json(
93+
JsonObject(
94+
mapOf(
95+
"te" to JsonArray(
96+
listOf(JsonPrimitive("tetete"))
97+
)
98+
)
99+
)
100+
)
101+
),
102+
script = """
92103
var obj = {male:"Ford", model:"Mustang", number:10};
93-
JSON.stringify(obj);
104+
Object.assign(obj, test);
105+
obj
94106
""".trimIndent()
95-
).jsonValue() is JsonObject
96-
}
107+
).jsonValue()
108+
assertTrue { jsonObject is JsonObject }
109+
assertEquals(
110+
expected = """{"number":10.0,"model":"Mustang","te":["tetete"],"male":"Ford"}""",
111+
actual = jsonObject.toString()
112+
)
97113
}
98114

99115
@Test

0 commit comments

Comments
 (0)