Ensure serialization is usable from K/N background thread
* fix concurrency issue when accessing C2TC
This bytearray was mutating itself during the init phase. By moving it to an object, the init {} deals with the initialization and makes it thread safe. The same logic was used for ESCAPE_2_CHAR.
* Serialization meets K/N background threads
* Run all tests from both main and bg threads
* Update Gradle distribution from bin to all
* Update JsonReader, get rid of companions and late-initialization to make it K/N friendly and produce less code
* Update tests to be bg-friendly
Co-authored-by: Mohamed Zenadi <mohamed@zenadi.com>
diff --git a/build.gradle b/build.gradle
index 83346e9..9d94ad8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -91,6 +91,7 @@
// To make it visible for compiler-version.gradle
ext.compilerVersion = org.jetbrains.kotlin.config.KotlinCompilerVersion.VERSION
+ext.nativeDebugBuild = org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType.DEBUG
apply plugin: 'binary-compatibility-validator'
apiValidation {
@@ -184,4 +185,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/core/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt b/core/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt
index dedc879..393e2b5 100644
--- a/core/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt
+++ b/core/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt
@@ -5,6 +5,7 @@
package kotlinx.serialization
import kotlinx.serialization.modules.*
+import kotlin.native.concurrent.*
@Serializable
open class PolyBase(val id: Int) {
@@ -29,6 +30,7 @@
@Serializable
data class PolyDerived(val s: String) : PolyBase(1)
+@SharedImmutable
val BaseAndDerivedModule = SerializersModule {
polymorphic(PolyBase::class, PolyBase.serializer()) {
subclass(PolyDerived.serializer())
diff --git a/core/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt b/core/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt
index 2691ce0..9e51d7f 100644
--- a/core/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt
+++ b/core/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt
@@ -5,5 +5,7 @@
package kotlinx.serialization.test
import kotlinx.serialization.test.Platform
+import kotlin.native.concurrent.SharedImmutable
+@SharedImmutable
public actual val currentPlatform: Platform = Platform.NATIVE
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt b/formats/cbor/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt
index 00376dc..86aafe4 100644
--- a/formats/cbor/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt
@@ -5,6 +5,7 @@
package kotlinx.serialization
import kotlinx.serialization.modules.*
+import kotlin.native.concurrent.*
@Serializable
abstract class SimpleAbstract
@@ -18,6 +19,7 @@
@Serializable
data class PolyBox(@Polymorphic val boxed: SimpleAbstract)
+@SharedImmutable
val SimplePolymorphicModule = SerializersModule {
polymorphic(SimpleAbstract::class) {
subclass(SimpleIntInheritor.serializer())
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonReader.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonReader.kt
index 14c4a96..b1f8208 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonReader.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonReader.kt
@@ -4,9 +4,9 @@
package kotlinx.serialization.json.internal
-import kotlinx.serialization.json.internal.EscapeCharMappings.ESCAPE_2_CHAR
+import kotlinx.serialization.json.internal.CharMappings.C2TC
+import kotlinx.serialization.json.internal.CharMappings.ESCAPE_2_CHAR
import kotlin.jvm.*
-import kotlin.native.concurrent.*
internal const val lenientHint = "Use 'isLenient = true' in 'Json {}` builder to accept non-compliant JSON."
internal const val coerceInputValuesHint = "Use 'coerceInputValues = true' in 'Json {}` builder to coerce nulls to default values."
@@ -51,32 +51,20 @@
// mapping from escape chars real chars
private const val ESC2C_MAX = 0x75
-@SharedImmutable
-internal val C2TC = ByteArray(CTC_MAX).apply {
- for (i in 0..0x20) {
- initC2TC(i, TC_INVALID)
- }
-
- initC2TC(0x09, TC_WS)
- initC2TC(0x0a, TC_WS)
- initC2TC(0x0d, TC_WS)
- initC2TC(0x20, TC_WS)
- initC2TC(COMMA, TC_COMMA)
- initC2TC(COLON, TC_COLON)
- initC2TC(BEGIN_OBJ, TC_BEGIN_OBJ)
- initC2TC(END_OBJ, TC_END_OBJ)
- initC2TC(BEGIN_LIST, TC_BEGIN_LIST)
- initC2TC(END_LIST, TC_END_LIST)
- initC2TC(STRING, TC_STRING)
- initC2TC(STRING_ESC, TC_STRING_ESC)
-}
-
-// object instead of @SharedImmutable because there is mutual initialization in [initC2ESC]
-internal object EscapeCharMappings {
+// object instead of @SharedImmutable because there is mutual initialization in [initC2ESC] and [initC2TC]
+internal object CharMappings {
@JvmField
- public val ESCAPE_2_CHAR = CharArray(ESC2C_MAX)
+ val ESCAPE_2_CHAR = CharArray(ESC2C_MAX)
+
+ @JvmField
+ val C2TC = ByteArray(CTC_MAX)
init {
+ initEscape()
+ initCharToToken()
+ }
+
+ private fun initEscape() {
for (i in 0x00..0x1f) {
initC2ESC(i, UNICODE_ESC)
}
@@ -91,19 +79,36 @@
initC2ESC(STRING_ESC, STRING_ESC)
}
+ private fun initCharToToken() {
+ for (i in 0..0x20) {
+ initC2TC(i, TC_INVALID)
+ }
+
+ initC2TC(0x09, TC_WS)
+ initC2TC(0x0a, TC_WS)
+ initC2TC(0x0d, TC_WS)
+ initC2TC(0x20, TC_WS)
+ initC2TC(COMMA, TC_COMMA)
+ initC2TC(COLON, TC_COLON)
+ initC2TC(BEGIN_OBJ, TC_BEGIN_OBJ)
+ initC2TC(END_OBJ, TC_END_OBJ)
+ initC2TC(BEGIN_LIST, TC_BEGIN_LIST)
+ initC2TC(END_LIST, TC_END_LIST)
+ initC2TC(STRING, TC_STRING)
+ initC2TC(STRING_ESC, TC_STRING_ESC)
+ }
+
private fun initC2ESC(c: Int, esc: Char) {
if (esc != UNICODE_ESC) ESCAPE_2_CHAR[esc.toInt()] = c.toChar()
}
private fun initC2ESC(c: Char, esc: Char) = initC2ESC(c.toInt(), esc)
-}
-private fun ByteArray.initC2TC(c: Int, cl: Byte) {
- this[c] = cl
-}
+ private fun initC2TC(c: Int, cl: Byte) {
+ C2TC[c] = cl
+ }
-private fun ByteArray.initC2TC(c: Char, cl: Byte) {
- initC2TC(c.toInt(), cl)
+ private fun initC2TC(c: Char, cl: Byte) = initC2TC(c.toInt(), cl)
}
internal fun charToTokenClass(c: Char) = if (c.toInt() < CTC_MAX) C2TC[c.toInt()] else TC_OTHER
diff --git a/formats/json/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt b/formats/json/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt
index 88b209d..5aaf628 100644
--- a/formats/json/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt
+++ b/formats/json/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt
@@ -6,6 +6,7 @@
import kotlinx.serialization.json.*
import kotlinx.serialization.modules.*
+import kotlin.native.concurrent.*
@Serializable
open class PolyBase(val id: Int) {
@@ -34,6 +35,7 @@
@Serializable
data class PolyDerived(val s: String) : PolyBase(1)
+@SharedImmutable
val BaseAndDerivedModule = SerializersModule {
polymorphic(PolyBase::class, PolyBase.serializer()) {
subclass(PolyDerived.serializer())
diff --git a/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/PolymorphicClasses.kt b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/PolymorphicClasses.kt
index 3bd5f95..e46de17 100644
--- a/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/PolymorphicClasses.kt
+++ b/formats/json/commonTest/src/kotlinx/serialization/json/polymorphic/PolymorphicClasses.kt
@@ -7,6 +7,7 @@
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlinx.serialization.modules.*
+import kotlin.native.concurrent.*
@Serializable
internal open class InnerBase
@@ -37,7 +38,7 @@
@Serializable
internal data class OuterNullableBox(@Polymorphic val outerBase: OuterBase?, @Polymorphic val innerBase: InnerBase?)
-
+@SharedImmutable
internal val polymorphicTestModule = SerializersModule {
polymorphic(InnerBase::class) {
subclass(InnerImpl.serializer())
@@ -50,11 +51,13 @@
}
}
+@SharedImmutable
internal val polymorphicJson = Json {
serializersModule = polymorphicTestModule
encodeDefaults = true
}
+@SharedImmutable
internal val polymorphicRelaxedJson = Json {
isLenient = true
serializersModule = polymorphicTestModule
diff --git a/formats/json/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt b/formats/json/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt
index 5824904..32806c1 100644
--- a/formats/json/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt
+++ b/formats/json/nativeTest/src/kotlinx/serialization/test/CurrentPlatform.kt
@@ -4,5 +4,8 @@
package kotlinx.serialization.test
+import kotlin.native.concurrent.SharedImmutable
+
+@SharedImmutable
public actual val currentPlatform: Platform = Platform.NATIVE
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt
index 86ad325..7ef9729 100644
--- a/formats/protobuf/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/PolymorphismTestData.kt
@@ -6,6 +6,7 @@
import kotlinx.serialization.modules.*
import kotlinx.serialization.protobuf.*
+import kotlin.native.concurrent.*
@Serializable
open class PolyBase(@ProtoNumber(1) val id: Int) {
@@ -45,6 +46,7 @@
@Serializable
data class PolyBox(@Polymorphic val boxed: SimpleAbstract)
+@SharedImmutable
val SimplePolymorphicModule = SerializersModule {
polymorphic(SimpleAbstract::class) {
subclass(SimpleIntInheritor.serializer())
diff --git a/gradle.properties b/gradle.properties
index 9e7c8e3..47ed36c 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -5,7 +5,7 @@
group=org.jetbrains.kotlinx
version=1.1.0-SNAPSHOT
-kotlin.version=1.4.30-M1
+kotlin.version=1.4.30-RC
# This version take precedence if 'bootstrap' property passed to project
kotlin.version.snapshot=1.4.255-SNAPSHOT
diff --git a/gradle/configure-source-sets.gradle b/gradle/configure-source-sets.gradle
index 827ff26..fdb3f33 100644
--- a/gradle/configure-source-sets.gradle
+++ b/gradle/configure-source-sets.gradle
@@ -12,6 +12,7 @@
}
}
}
+
js {
nodejs {}
configure([compilations.main, compilations.test]) {
@@ -87,4 +88,19 @@
sourceSets.matching({ it.name.contains("Main") }).all { srcSet ->
project.ext.set("kotlin.mpp.freeCompilerArgsForSourceSet.${srcSet.name}", "-Xexplicit-api=warning")
}
+
+ def targetsWithoutTestRunners = ["linuxArm32Hfp", "linuxArm64", "mingwX86"]
+ configure(targets) {
+ // Configure additional binaries to run tests in the background
+ if (["macos", "linux", "mingw"].any { name.startsWith(it) && !targetsWithoutTestRunners.contains(name) }) {
+ binaries {
+ test("background", [nativeDebugBuild]) {
+ freeCompilerArgs += ["-trw"]
+ }
+ }
+ testRuns {
+ background { setExecutionSourceFrom(binaries.backgroundDebugTest) }
+ }
+ }
+ }
}
diff --git a/gradle/native-targets.gradle b/gradle/native-targets.gradle
index fb9901a..e5a7e0b 100644
--- a/gradle/native-targets.gradle
+++ b/gradle/native-targets.gradle
@@ -88,7 +88,6 @@
targets {
if (project.ext.nativeState == NativeState.DISABLED) return
-
if (project.ext.singleTargetMode) {
fromPreset(project.ext.ideaPreset, 'native')
} else {
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 62d4c05..e708b1c 100644
--- a/gradle/wrapper/gradle-wrapper.jar
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 4d9ca16..e66b0d6 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,9 @@
+#
+# Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+#
+
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index fbd7c51..4f906e0 100755
--- a/gradlew
+++ b/gradlew
@@ -130,7 +130,7 @@
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
+
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
diff --git a/gradlew.bat b/gradlew.bat
index a9f778a..ac1b06f 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -40,7 +40,7 @@
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
+if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -54,7 +54,7 @@
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-if exist "%JAVA_EXE%" goto init
+if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -64,21 +64,6 @@
goto fail
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
:execute
@rem Setup the command line
@@ -86,7 +71,7 @@
@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell