Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crash: Could not cast value of type 'ASLMPKMapAsNSDictionary' to 'ASLMPKotlinx_serialization_jsonJsonElement' #2919

Open
nuke-dash opened this issue Feb 4, 2025 · 8 comments

Comments

@nuke-dash
Copy link

nuke-dash commented Feb 4, 2025

Describe the bug

An object of type JsonObject cannot be used in tvOS.
When trying to access it, inspect it, debug it, it causes the compiled library to crash.

The class containing the property

data class AlternativeServer(
    val id: String,
    val queryDefinition: JsonObject?,
    var queryDefinitionAsString: String?
)

Creating the object from json

        val json = """
        {
            "id": "65f45d9f39e7c2882d833087",
            "name": "Dynamic Movies category",
            "alternatives": [
                {
                    "id": "65f45d9f39e7c2882d833088",
                    "type": "SERVER",
                    "queryDefinition": {
                        "type": "Back-office",
                        "query": "on_demand",
                        "expression": {
                            "vodProviderId": "BETV",
                            "categories": [
                                "/Movies/Horror"
                            ],
                            "sorting": [
                                {
                                    "title": "DESCENDING"
                                }
                            ]
                        }
                    }
                }
            ]
        }
        """.trimIndent()
        return jsonFormat.decodeFromString<Catalog>(json)

Using it in swift

print(String(describing: catalog?.alternatives.first?.asServer()?.queryDefinition))

Could not cast value of type 'ASLMPKMapAsNSDictionary' (0x1040305f0) to 'ASLMPKotlinx_serialization_jsonJsonElement' (0x104030198).

To Reproduce

A sample project can be found here showcasing the crash with precompiled tvOS/iOS simulator libraries.
Checkout the project, open the xcodeproj and run it.
A README is also included in the sampe project with more details.
https://github.com/nuke-dash/JsonObject-Crash

Expected behavior

To not crash.
To allow it to be printed, used.

Environment

  • Kotlin version: 2.1.10
  • Library version: 1.8.0
  • Kotlin platforms: iOS/tvOS
  • Gradle version: 8.5.2
  • IDE version: Xcode 16.2, Android Studio Ladybug Feature Drop | 2024.2.2
  • macOS: Sonoma 14.6.1
@pdvrieze
Copy link
Contributor

pdvrieze commented Feb 4, 2025

The types can be found here: https://github.com/nuke-dash/JsonObject-Crash/blob/main/appsharedlogic/shared/src/commonMain/kotlin/com/company/app/sharedlogic/models/ecc/Alternative.kt

Can you provide a stacktrace, to identify what happens, as obviously an NSLMPKMapAsNSDictionary (which appears to be an adapter from Kotlin's map type to an ios NSDictionary) is not a json element.

@nuke-dash
Copy link
Author

Yes, here is the stacktrace and a screenshot of it crashing in Xcode.

Thread 1 Queue : com.apple.main-thread (serial)
#0	0x0000000105b94f30 in __pthread_kill ()
#1	0x000000010524b124 in pthread_kill ()
#2	0x00000001802cc530 in abort ()
#3	0x000000019c845858 in swift::fatalErrorv ()
#4	0x000000019c845874 in swift::fatalError ()
#5	0x000000019c83be24 in swift::swift_dynamicCastFailure ()
#6	0x000000019c83be9c in swift::swift_dynamicCastFailure ()
#7	0x000000019c840518 in swift_dynamicCast ()
#8	0x000000018152dc6c in closure #2 (Swift.UnsafeMutableBufferPointer<τ_0_0>, Swift.UnsafeMutableBufferPointer<τ_0_1>) -> Swift.Int in static Swift.Dictionary._forceBridgeFromObjectiveC(_: __C.NSDictionary, result: inout Swift.Optional<Swift.Dictionary<τ_0_0, τ_0_1>>) -> () ()
#9	0x000000018152f9d4 in merged partial apply forwarder for closure #2 (Swift.UnsafeMutableBufferPointer<τ_0_0>, Swift.UnsafeMutableBufferPointer<τ_0_1>) -> Swift.Int in static Swift.Dictionary._forceBridgeFromObjectiveC(_: __C.NSDictionary, result: inout Swift.Optional<Swift.Dictionary<τ_0_0, τ_0_1>>) -> () ()
#10	0x000000018152f4c0 in Swift._NativeDictionary.init(_unsafeUninitializedCapacity: Swift.Int, allowingDuplicates: Swift.Bool, initializingWith: (Swift.UnsafeMutableBufferPointer<τ_0_0>, Swift.UnsafeMutableBufferPointer<τ_0_1>) -> Swift.Int) -> Swift._NativeDictionary<τ_0_0, τ_0_1> ()
#11	0x000000018152cb78 in static Swift.Dictionary._unconditionallyBridgeFromObjectiveC(Swift.Optional<__C.NSDictionary>) -> Swift.Dictionary<τ_0_0, τ_0_1> ()
#12	0x00000001052cbdf4 in closure #1 in closure #2 in ContentView.body.getter at /Users/thomaspanis/Documents/workspace/test/Crash/Crash/Crash/ContentView.swift:26
#13	0x00000001052cd0c0 in partial apply for closure #1 in closure #2 in ContentView.body.getter ()
#14	0x00000001052cd234 in thunk for @escaping @isolated(any) @callee_guaranteed @async () -> (@out A) ()
#15	0x00000001052cd390 in partial apply for thunk for @escaping @isolated(any) @callee_guaranteed @async () -> (@out A) ()

Image

@pdvrieze
Copy link
Contributor

pdvrieze commented Feb 5, 2025

I think you have the wrong thread for the stacktrace, but the screenshot is somewhat helpful (I don't have a mac). It seems that creating an AlternativeServer instance works. I would be surprised if the queryDefinition access fails (put them in separate variables to test, also inspect the actual values using the debugger).

What I don't know is what the "String(" function does exactly (I assume it converts stuff to string), and it appears there is an error there (perhaps the implementation of JsonObject.toString() is broken on IOS.

The best next step is to introspect the structure returned from the accessor using the debugger.

@nuke-dash
Copy link
Author

@pdvrieze thanks for the support so far!

The stacktrace I copied is the main thread and the same thread as you can see in the screenshot on the left side.
This is the tread that crashes.

The "String(" is just a basic way to log a variable.
It does nothing special.
https://developer.apple.com/documentation/swift/string/init(describing:)-67ncf

Trying to debug the property by printing it in the console doesn't work.
with either po or p (the xcode debug command to inspect variables).

Image

Trying to assign the queryDefinition directly to a var for better inspection also doesn't works and results in a crash immediatly.
(The yellow mark is a warning that the variable is not used.)

Image

@pdvrieze
Copy link
Contributor

pdvrieze commented Feb 6, 2025

Something really strange is going on here. Some really strange things are going on here. There is no obvious issue. Probably you want to have a look at various bits. Try to just deserialize to JsonObject directly, try to not use asynchronous methods.

I actually suspect there might be an issue with the coroutines/async code. In particular you are using swift await with a Kotlin suspend function (if it is even supported it is very new functionality that may be broken).

@xiaozhikang0916
Copy link
Contributor

You need some extra config before inspecting your kt objects, try following the instruction by importing a lldb script, make sure you are importing the correct version from your local kotlin-native distrubution, and then check what types exactlly the properties are.

@sandwwraith
Copy link
Member

sandwwraith commented Feb 7, 2025

It looks like the same issue as #759 and JetBrains/kotlin-native#3991. The problem is that JsonObject during interop transforms into NSDictionary in order to behave like Map. Unfortunately, this sometimes leads to class cast exceptions.

As a workaround, you can write all JsonElement-related functions in Kotlin and use Swift only to call these functions. It is far from ideal, but this is how Kotlin/Swift interop is currently designed.

@sandwwraith
Copy link
Member

Issue was moved here: https://youtrack.jetbrains.com/issue/KT-48079

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants