From 15dc23c1636f98422d2fc51c4c7efc78870ef1f6 Mon Sep 17 00:00:00 2001 From: John O'Reilly Date: Fri, 12 Apr 2024 13:18:38 +0100 Subject: [PATCH 1/2] support Kotlin/Wasm for apollo-adaptors --- libraries/apollo-adapters/build.gradle.kts | 7 +- .../wasmJsMain/kotlin/adapter/BigDecimal.kt | 103 ++++++++++++++++++ .../apollo3/adapter/test/BigDecimalTests.kt | 72 ++++++++++++ 3 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 libraries/apollo-adapters/src/wasmJsMain/kotlin/adapter/BigDecimal.kt create mode 100644 libraries/apollo-adapters/src/wasmJsTest/kotlin/com/apollographql/apollo3/adapter/test/BigDecimalTests.kt diff --git a/libraries/apollo-adapters/build.gradle.kts b/libraries/apollo-adapters/build.gradle.kts index 4453c40bfe5..1e367a42d68 100644 --- a/libraries/apollo-adapters/build.gradle.kts +++ b/libraries/apollo-adapters/build.gradle.kts @@ -5,7 +5,7 @@ plugins { apolloLibrary( namespace = "com.apollographql.apollo3.adapter", withLinux = false, - withWasm = false + withWasm = true ) kotlin { @@ -22,5 +22,10 @@ kotlin { implementation(npm("big.js", "5.2.2")) } } + findByName("wasmJsMain")?.apply { + dependencies { + implementation(npm("big.js", "5.2.2")) + } + } } } diff --git a/libraries/apollo-adapters/src/wasmJsMain/kotlin/adapter/BigDecimal.kt b/libraries/apollo-adapters/src/wasmJsMain/kotlin/adapter/BigDecimal.kt new file mode 100644 index 00000000000..a2abdd1d132 --- /dev/null +++ b/libraries/apollo-adapters/src/wasmJsMain/kotlin/adapter/BigDecimal.kt @@ -0,0 +1,103 @@ +package com.apollographql.apollo3.adapter + +@JsModule("big.js") +internal external fun Big(raw: JsAny): Big + +@JsName("Number") +internal external fun jsNumber(raw: Big): JsNumber + +internal external class Big { + fun plus(other: Big): Big + fun minus(other: Big): Big + fun times(other: Big): Big + fun div(other: Big): Big + fun cmp(other: Big): Int + fun eq(other: Big): Boolean + fun round(dp: Int, rm: Int): Big +} + +actual class BigDecimal { + internal val raw: Big + + internal constructor(raw: Big) { + this.raw = raw + } + + constructor() : this(raw = Big()) + + actual constructor(strVal: String) : this(raw = Big(strVal.toJsString())) + + actual constructor(doubleVal: Double) { + check(!doubleVal.isNaN() && !doubleVal.isInfinite()) + raw = Big(doubleVal.toJsNumber()) + } + + actual constructor(intVal: Int) : this(raw = Big(intVal.toJsNumber())) + + // JS does not support 64-bit integer natively. + actual constructor(longVal: Long) : this(raw = Big(longVal.toString().toJsString())) + + actual fun add(augend: BigDecimal): BigDecimal = BigDecimal(raw = raw.plus(augend.raw)) + + actual fun subtract(subtrahend: BigDecimal): BigDecimal = BigDecimal(raw = raw.minus(subtrahend.raw)) + + actual fun multiply(multiplicand: BigDecimal): BigDecimal = BigDecimal(raw = raw.times(multiplicand.raw)) + + actual fun divide(divisor: BigDecimal): BigDecimal = BigDecimal(raw = raw.div(divisor.raw)) + + actual fun negate(): BigDecimal = BigDecimal().subtract(this) + + actual fun signum(): Int { + return raw.cmp(Big()) + } + + fun toInt(): Int { + return jsNumber(raw).toInt() + } + + fun toLong(): Long { + // JSNumber is double precision, so it cannot exactly represent 64-bit `Long`. + return toString().toLong() + } + + fun toShort(): Short { + return jsNumber(raw).toInt().toShort() + } + + fun toByte(): Byte { + return jsNumber(raw).toInt().toByte() + } + + fun toChar(): Char { + return jsNumber(raw).toInt().toChar() + } + + fun toDouble(): Double { + return jsNumber(raw).toDouble() + } + + fun toFloat(): Float { + return jsNumber(raw).toDouble().toFloat() + } + + override fun equals(other: Any?): Boolean { + if (other is BigDecimal) { + return raw.eq(other.raw) + } + return false + } + + override fun hashCode(): Int = raw.toString().hashCode() + + override fun toString(): String = raw.toString() +} + +actual fun BigDecimal.toNumber(): Number { + val rounded = raw.round(0, 0) + + return if (raw.minus(rounded).eq(Big())) { + toLong() + } else { + toDouble() + } +} diff --git a/libraries/apollo-adapters/src/wasmJsTest/kotlin/com/apollographql/apollo3/adapter/test/BigDecimalTests.kt b/libraries/apollo-adapters/src/wasmJsTest/kotlin/com/apollographql/apollo3/adapter/test/BigDecimalTests.kt new file mode 100644 index 00000000000..bf780d7df80 --- /dev/null +++ b/libraries/apollo-adapters/src/wasmJsTest/kotlin/com/apollographql/apollo3/adapter/test/BigDecimalTests.kt @@ -0,0 +1,72 @@ +package com.apollographql.apollo3.adapter.test + +import com.apollographql.apollo3.adapter.BigDecimal +import kotlin.test.* + +class BigDecimalTests { + @Test + fun equality() { + assertEquals(BigDecimal("12345.12345678901234567890123"), BigDecimal("12345.12345678901234567890123")) + assertEquals(BigDecimal("987654321098765432109876543210"), BigDecimal("987654321098765432109876543210")) + assertEquals(BigDecimal("1024768"), BigDecimal("1024768")) + assertEquals(BigDecimal(-Double.MAX_VALUE), BigDecimal(-Double.MAX_VALUE)) + assertEquals(BigDecimal(Double.MAX_VALUE), BigDecimal(Double.MAX_VALUE)) + assertEquals(BigDecimal(-Long.MAX_VALUE - 1), BigDecimal(-Long.MAX_VALUE - 1)) + assertEquals(BigDecimal(Long.MAX_VALUE), BigDecimal(Long.MAX_VALUE)) + } + + @Test + fun overflow() { + assertFails { + BigDecimal("987654321098765432109876543210").toLong() + } + } + + @Test + fun truncating() { + + assertEquals( + BigDecimal("12345.12345678901234567890123").toDouble(), + 12345.123456789011 + ) + } + + @Test + fun roundTrip_Int() { + val bridged = BigDecimal(Int.MAX_VALUE) + assertEquals(bridged.toInt(), Int.MAX_VALUE) + + val bridgedNeg = BigDecimal( -Int.MAX_VALUE - 1) + assertEquals(bridgedNeg.toInt(), -Int.MAX_VALUE - 1) + } + + @Test + fun roundTrip_Long() { + val bridged = BigDecimal(Long.MAX_VALUE) + assertEquals(bridged.toLong(), Long.MAX_VALUE) + + val bridgedNeg = BigDecimal(-Long.MAX_VALUE - 1) + assertEquals(bridgedNeg.toLong(), -Long.MAX_VALUE - 1) + } + + @Test + fun roundTrip_Double() { + val bridged = BigDecimal(Double.MAX_VALUE) + assertEquals(bridged.toDouble(), Double.MAX_VALUE) + + val bridgedNeg = BigDecimal(-Double.MAX_VALUE) + assertEquals(bridgedNeg.toDouble(), -Double.MAX_VALUE) + + assertFails { + BigDecimal(Double.POSITIVE_INFINITY) + } + + assertFails { + BigDecimal(Double.NEGATIVE_INFINITY) + } + + assertFails { + BigDecimal(Double.NaN) + } + } +} From a482b0d084e378fcc3f6655bcbd8a7ef034e2a4a Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Fri, 12 Apr 2024 16:28:42 +0200 Subject: [PATCH 2/2] Share JS and Wasm tests --- build-logic/src/main/kotlin/Mpp.kt | 9 ++- .../apollo3/adapter/test/BigDecimalTests.kt | 0 .../apollo3/adapter/test/BigDecimalTests.kt | 72 ------------------- 3 files changed, 8 insertions(+), 73 deletions(-) rename libraries/apollo-adapters/src/{jsTest => jsCommonTest}/kotlin/com/apollographql/apollo3/adapter/test/BigDecimalTests.kt (100%) delete mode 100644 libraries/apollo-adapters/src/wasmJsTest/kotlin/com/apollographql/apollo3/adapter/test/BigDecimalTests.kt diff --git a/build-logic/src/main/kotlin/Mpp.kt b/build-logic/src/main/kotlin/Mpp.kt index 6603da5499d..5a6cb070878 100644 --- a/build-logic/src/main/kotlin/Mpp.kt +++ b/build-logic/src/main/kotlin/Mpp.kt @@ -159,7 +159,14 @@ private fun KotlinMultiplatformExtension.configureSourceSetGraph() { withAndroidTarget() } } - group("js") + group("jsCommon") { + group("js") { + withJs() + } + group("wasmJs") { + withWasmJs() + } + } } } } diff --git a/libraries/apollo-adapters/src/jsTest/kotlin/com/apollographql/apollo3/adapter/test/BigDecimalTests.kt b/libraries/apollo-adapters/src/jsCommonTest/kotlin/com/apollographql/apollo3/adapter/test/BigDecimalTests.kt similarity index 100% rename from libraries/apollo-adapters/src/jsTest/kotlin/com/apollographql/apollo3/adapter/test/BigDecimalTests.kt rename to libraries/apollo-adapters/src/jsCommonTest/kotlin/com/apollographql/apollo3/adapter/test/BigDecimalTests.kt diff --git a/libraries/apollo-adapters/src/wasmJsTest/kotlin/com/apollographql/apollo3/adapter/test/BigDecimalTests.kt b/libraries/apollo-adapters/src/wasmJsTest/kotlin/com/apollographql/apollo3/adapter/test/BigDecimalTests.kt deleted file mode 100644 index bf780d7df80..00000000000 --- a/libraries/apollo-adapters/src/wasmJsTest/kotlin/com/apollographql/apollo3/adapter/test/BigDecimalTests.kt +++ /dev/null @@ -1,72 +0,0 @@ -package com.apollographql.apollo3.adapter.test - -import com.apollographql.apollo3.adapter.BigDecimal -import kotlin.test.* - -class BigDecimalTests { - @Test - fun equality() { - assertEquals(BigDecimal("12345.12345678901234567890123"), BigDecimal("12345.12345678901234567890123")) - assertEquals(BigDecimal("987654321098765432109876543210"), BigDecimal("987654321098765432109876543210")) - assertEquals(BigDecimal("1024768"), BigDecimal("1024768")) - assertEquals(BigDecimal(-Double.MAX_VALUE), BigDecimal(-Double.MAX_VALUE)) - assertEquals(BigDecimal(Double.MAX_VALUE), BigDecimal(Double.MAX_VALUE)) - assertEquals(BigDecimal(-Long.MAX_VALUE - 1), BigDecimal(-Long.MAX_VALUE - 1)) - assertEquals(BigDecimal(Long.MAX_VALUE), BigDecimal(Long.MAX_VALUE)) - } - - @Test - fun overflow() { - assertFails { - BigDecimal("987654321098765432109876543210").toLong() - } - } - - @Test - fun truncating() { - - assertEquals( - BigDecimal("12345.12345678901234567890123").toDouble(), - 12345.123456789011 - ) - } - - @Test - fun roundTrip_Int() { - val bridged = BigDecimal(Int.MAX_VALUE) - assertEquals(bridged.toInt(), Int.MAX_VALUE) - - val bridgedNeg = BigDecimal( -Int.MAX_VALUE - 1) - assertEquals(bridgedNeg.toInt(), -Int.MAX_VALUE - 1) - } - - @Test - fun roundTrip_Long() { - val bridged = BigDecimal(Long.MAX_VALUE) - assertEquals(bridged.toLong(), Long.MAX_VALUE) - - val bridgedNeg = BigDecimal(-Long.MAX_VALUE - 1) - assertEquals(bridgedNeg.toLong(), -Long.MAX_VALUE - 1) - } - - @Test - fun roundTrip_Double() { - val bridged = BigDecimal(Double.MAX_VALUE) - assertEquals(bridged.toDouble(), Double.MAX_VALUE) - - val bridgedNeg = BigDecimal(-Double.MAX_VALUE) - assertEquals(bridgedNeg.toDouble(), -Double.MAX_VALUE) - - assertFails { - BigDecimal(Double.POSITIVE_INFINITY) - } - - assertFails { - BigDecimal(Double.NEGATIVE_INFINITY) - } - - assertFails { - BigDecimal(Double.NaN) - } - } -}