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

ExceptionInInitializerError when @Serializable data object contains NavDeepLink #2857

Open
FelixZY opened this issue Nov 13, 2024 · 2 comments
Assignees
Labels

Comments

@FelixZY
Copy link

FelixZY commented Nov 13, 2024

Describe the bug

While setting up my navigation graph, I ran into an ExceptionInInitializerError when trying to use @Serializable on a data object containing a NavDeepLink member.

@Serializable
data object MySerializableObject {
  @Transient val deepLink: NavDeepLink = navDeepLink<MySerializableObject>(basePath = "Hello World")
}

Making the member @Transient did not help and it seems to me that the exception is caused in the generated static initializer., i.e. here somewhere (extract from Reproduction.decompiled.java further down):

   static {
      String basePath$iv = "Hello World";
      Map typeMap$iv = MapsKt.emptyMap();
      Function1 deepLinkBuilder$iv = (Function1)MySerializableObject$special$$inlined$navDeepLink$default$1.INSTANCE;
      int $i$f$navDeepLink = false;
      deepLink = NavDeepLinkDslBuilderKt.navDeepLink(basePath$iv, Reflection.getOrCreateKotlinClass(MySerializableObject.class), typeMap$iv, deepLinkBuilder$iv);
      $cachedSerializer$delegate = LazyKt.lazy(LazyThreadSafetyMode.PUBLICATION, () -> {
         return (KSerializer)(new ObjectSerializer("se.fzy.kotlinx_serialization_plugin_bug.MySerializableObject", INSTANCE, new Annotation[0]));
      });
      $stable = 8;
   }
Full Stack trace
java.lang.ExceptionInInitializerError
	at se.fzy.kotlinx_serialization_plugin_bug.Reproduction.MySerializableObject#serializer is not null(Reproduction.kt:18)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source)
	at java.base/java.lang.reflect.Method.invoke(Unknown Source)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:112)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:40)
	at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:54)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:53)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source)
	at java.base/java.lang.reflect.Method.invoke(Unknown Source)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:92)
	at jdk.proxy1/jdk.proxy1.$Proxy4.processTestClass(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:181)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:130)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:101)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:61)
	at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:122)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:69)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Caused by: java.lang.reflect.InvocationTargetException
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source)
	at java.base/java.lang.reflect.Method.invoke(Unknown Source)
	at kotlinx.serialization.internal.PlatformKt.findObjectSerializer(Platform.kt:158)
	at kotlinx.serialization.internal.PlatformKt.constructSerializerForGivenTypeArgs(Platform.kt:51)
	at kotlinx.serialization.internal.PlatformKt.constructSerializerForGivenTypeArgs(Platform.kt:40)
	at kotlinx.serialization.internal.PlatformKt.compiledSerializerImpl(Platform.kt:27)
	at kotlinx.serialization.SerializersKt__SerializersKt.serializerOrNull(Serializers.kt:322)
	at kotlinx.serialization.SerializersKt.serializerOrNull(Unknown Source)
	at kotlinx.serialization.SerializersKt__SerializersKt.serializer(Serializers.kt:299)
	at kotlinx.serialization.SerializersKt.serializer(Unknown Source)
	at androidx.navigation.NavDeepLinkDslBuilder.<init>(NavDeepLinkDslBuilder.kt:99)
	at androidx.navigation.NavDeepLinkDslBuilderKt.navDeepLink(NavDeepLinkDslBuilder.kt:64)
	at se.fzy.kotlinx_serialization_plugin_bug.MySerializableObject.<clinit>(Reproduction.kt:31)
	... 40 more
Caused by: java.lang.NullPointerException: Cannot invoke "kotlin.Lazy.getValue()" because "se.fzy.kotlinx_serialization_plugin_bug.MySerializableObject.$cachedSerializer$delegate" is null
	at se.fzy.kotlinx_serialization_plugin_bug.MySerializableObject.get$cachedSerializer(Reproduction.kt:22)
	at se.fzy.kotlinx_serialization_plugin_bug.MySerializableObject.serializer(Reproduction.kt:22)
	... 53 more

To Reproduce

Run the Reproduction.kt unit test in https://github.com/FelixZY/kotlinx-serialization-plugin-bug . The code introducing the issue is separated into commit 1cf8080.

Reproduction junit test
package se.fzy.kotlinx_serialization_plugin_bug

import androidx.navigation.NavDeepLink
import androidx.navigation.navDeepLink
import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import org.junit.Assert.assertNotNull
import org.junit.Test

class Reproduction {
  @Test
  fun `MySerializableObject is not null`() {
    assertNotNull(MySerializableObject)
  }

  @Test
  fun `MySerializableObject#serializer is not null`() {
    assertNotNull(MySerializableObject.serializer())
  }
}

@Serializable
data object MySerializableObject {
  @Transient val deepLink: NavDeepLink = navDeepLink<MySerializableObject>(basePath = "Hello World")
}
Reproduction.decompiled.java
// Reproduction.java
package se.fzy.kotlinx_serialization_plugin_bug;

import androidx.compose.runtime.internal.StabilityInferred;
import kotlin.Metadata;
import org.junit.Assert;
import org.junit.Test;

@Metadata(
   mv = {2, 0, 0},
   k = 1,
   xi = 48,
   d1 = {"\u0000\u0014\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0002\b\u0002\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\b\u0010\u0003\u001a\u00020\u0004H\u0007J\b\u0010\u0005\u001a\u00020\u0004H\u0007¨\u0006\u0006"},
   d2 = {"Lse/fzy/kotlinx_serialization_plugin_bug/Reproduction;", "", "()V", "MySerializableObject is not null", "", "MySerializableObject#serializer is not null", "app_debugUnitTest"}
)
@StabilityInferred(
   parameters = 1
)
public final class Reproduction {
   public static final int $stable;

   @Test
   public final void MySerializableObject_is_not_null/* $FF was: MySerializableObject is not null*/() {
      Assert.assertNotNull(MySerializableObject.INSTANCE);
   }

   @Test
   public final void MySerializableObject_serializer_is_not_null/* $FF was: MySerializableObject#serializer is not null*/() {
      Assert.assertNotNull(MySerializableObject.INSTANCE.serializer());
   }
}
// MySerializableObject.java
package se.fzy.kotlinx_serialization_plugin_bug;

import androidx.compose.runtime.internal.StabilityInferred;
import androidx.navigation.NavDeepLink;
import androidx.navigation.NavDeepLinkDslBuilderKt;
import java.lang.annotation.Annotation;
import java.util.Map;
import kotlin.Lazy;
import kotlin.LazyKt;
import kotlin.LazyThreadSafetyMode;
import kotlin.Metadata;
import kotlin.collections.MapsKt;
import kotlin.jvm.functions.Function1;
import kotlin.jvm.internal.Reflection;
import kotlin.jvm.internal.SourceDebugExtension;
import kotlinx.serialization.KSerializer;
import kotlinx.serialization.Serializable;
import kotlinx.serialization.Transient;
import kotlinx.serialization.internal.ObjectSerializer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@Serializable
@Metadata(
   mv = {2, 0, 0},
   k = 1,
   xi = 48,
   d1 = {"\u0000.\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0018\u0002\n\u0002\b\u0004\n\u0002\u0010\u000b\n\u0002\b\u0002\n\u0002\u0010\b\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0010\u000e\n\u0000\bÇ\n\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002J\u0013\u0010\b\u001a\u00020\t2\b\u0010\n\u001a\u0004\u0018\u00010\u0001\u0003J\t\u0010\u000b\u001a\u00020\f\u0001J\u000f\u0010\r\u001a\b\u0012\u0004\u0012\u00020\u00000\u000e\u0001J\t\u0010\u000f\u001a\u00020\u0010\u0001R\u001c\u0010\u0003\u001a\u00020\u00048\u0006X\u0087\u0004¢\u0006\u000e\n\u0000\u0012\u0004\b\u0005\u0010\u0002\u001a\u0004\b\u0006\u0010\u0007¨\u0006\u0011"},
   d2 = {"Lse/fzy/kotlinx_serialization_plugin_bug/MySerializableObject;", "", "()V", "deepLink", "Landroidx/navigation/NavDeepLink;", "getDeepLink$annotations", "getDeepLink", "()Landroidx/navigation/NavDeepLink;", "equals", "", "other", "hashCode", "", "serializer", "Lkotlinx/serialization/KSerializer;", "toString", "", "app_debugUnitTest"}
)
@StabilityInferred(
   parameters = 0
)
@SourceDebugExtension({"SMAP\nReproduction.kt\nKotlin\n*S Kotlin\n*F\n+ 1 Reproduction.kt\nse/fzy/kotlinx_serialization_plugin_bug/MySerializableObject\n+ 2 NavDeepLinkDslBuilder.kt\nandroidx/navigation/NavDeepLinkDslBuilderKt\n*L\n1#1,26:1\n51#2,5:27\n*S KotlinDebug\n*F\n+ 1 Reproduction.kt\nse/fzy/kotlinx_serialization_plugin_bug/MySerializableObject\n*L\n24#1:27,5\n*E\n"})
public final class MySerializableObject {
   @NotNull
   public static final MySerializableObject INSTANCE = new MySerializableObject();
   @NotNull
   private static final NavDeepLink deepLink;
   // $FF: synthetic field
   private static final Lazy $cachedSerializer$delegate;
   public static final int $stable;

   private MySerializableObject() {
   }

   @NotNull
   public final NavDeepLink getDeepLink() {
      return deepLink;
   }

   /** @deprecated */
   // $FF: synthetic method
   @Transient
   public static void getDeepLink$annotations() {
   }

   @NotNull
   public String toString() {
      return "MySerializableObject";
   }

   public int hashCode() {
      return 485428276;
   }

   public boolean equals(@Nullable Object other) {
      if (this == other) {
         return true;
      } else if (!(other instanceof MySerializableObject)) {
         return false;
      } else {
         MySerializableObject var2 = (MySerializableObject)other;
         return true;
      }
   }

   @NotNull
   public final KSerializer serializer() {
      return this.get$cachedSerializer();
   }

   // $FF: synthetic method
   private final KSerializer get$cachedSerializer() {
      return (KSerializer)$cachedSerializer$delegate.getValue();
   }

   static {
      String basePath$iv = "Hello World";
      Map typeMap$iv = MapsKt.emptyMap();
      Function1 deepLinkBuilder$iv = (Function1)MySerializableObject$special$$inlined$navDeepLink$default$1.INSTANCE;
      int $i$f$navDeepLink = false;
      deepLink = NavDeepLinkDslBuilderKt.navDeepLink(basePath$iv, Reflection.getOrCreateKotlinClass(MySerializableObject.class), typeMap$iv, deepLinkBuilder$iv);
      $cachedSerializer$delegate = LazyKt.lazy(LazyThreadSafetyMode.PUBLICATION, () -> {
         return (KSerializer)(new ObjectSerializer("se.fzy.kotlinx_serialization_plugin_bug.MySerializableObject", INSTANCE, new Annotation[0]));
      });
      $stable = 8;
   }
}
// MySerializableObject$special$$inlined$navDeepLink$default$1.java
package se.fzy.kotlinx_serialization_plugin_bug;

import androidx.navigation.NavDeepLinkDslBuilder;
import kotlin.Metadata;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
import kotlin.jvm.internal.Intrinsics;
import kotlin.jvm.internal.Lambda;

@Metadata(
   mv = {2, 0, 0},
   k = 3,
   xi = 48,
   d1 = {"\u0000\u0014\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0010\u0000\n\u0002\u0018\u0002\n\u0002\b\u0002\u0010\u0000\u001a\u00020\u0001\"\n\b\u0000\u0010\u0002\u0018\u0001*\u00020\u0003*\u00020\u0004H\n¢\u0006\u0002\b\u0005¨\u0006\u0006"},
   d2 = {"<anonymous>", "", "T", "", "Landroidx/navigation/NavDeepLinkDslBuilder;", "invoke", "androidx/navigation/NavDeepLinkDslBuilderKt$navDeepLink$1"}
)
public final class MySerializableObject$special$$inlined$navDeepLink$default$1 extends Lambda implements Function1 {
   public static final MySerializableObject$special$$inlined$navDeepLink$default$1 INSTANCE = new MySerializableObject$special$$inlined$navDeepLink$default$1();

   public MySerializableObject$special$$inlined$navDeepLink$default$1() {
      super(1);
   }

   public final void invoke(NavDeepLinkDslBuilder $this$null) {
      Intrinsics.checkNotNullParameter($this$null, "$this$null");
   }

   // $FF: synthetic method
   // $FF: bridge method
   public Object invoke(Object p1) {
      this.invoke((NavDeepLinkDslBuilder)p1);
      return Unit.INSTANCE;
   }
}

Expected behavior

The program should not crash when referencing MySerializableObject or its serializer.

Written another way: the Reproduction.kt unit test should pass.

Environment

  • Kotlin version: 1.9.22, 1.9.24, 2.0.20
  • Library version: 1.6.2, 1.7.3
  • Kotlin platforms: JVM
  • Gradle version: 8.9
  • IDE version (if bug is related to the IDE) Android Studio 2024.2.1 Patch 2
@FelixZY
Copy link
Author

FelixZY commented Nov 13, 2024

Possibly a regression or variation of #2294 given the self referencing generic argument of navDeepLink<T>?

@sandwwraith
Copy link
Member

@shanshin PTAL

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

No branches or pull requests

3 participants