Spotbugs fails with violation IL_INFINITE_RECURSIVE_LOOP

Hello,

I am trying to implement some Spotbugs suggestions on my kotlin code. Spotbugs complains that my code may expose internal representation by returning reference to mutable object (EI_EXPOSE_REP) on a java.util.Date field. Same issue with EI_EXPOSE_REP2.

I fixed it by providing my own implementation for the accessors and mutators:

var creationDate: Date = Date()
		set(value) {
			field = Date(value.getTime())
		} 
		get() = Date(creationDate.getTime())

However after this change Spotbugs now complains about the IL_INFINITE_RECURSIVE_LOOP: This method unconditionally invokes itself. This would seem to indicate an infinite recursive loop that will result in a stack overflow.

And it is true. If I look at the bytecode kotlin has generated I can see that it uses invokevirtual whereas the bytecode generated by the java compiler uses getfield instead.

I am using OpenJDK 11 for the java compilation. For Kotlin I am using Kotlin 1.3.50.

OpenJDK11 bytecode

dup
aload0 // reference to self
getfield a/b/c/d/BlogArticle2.creationDate:java.util.Date
invokevirtual java/util/Date.getTime()J
invokespecial java/util/Date.<init>(J)V
areturn

Corresponding Java source code from decompilation of the java compiled bytecode

   public Date getCreationDate() {
      return new Date(this.creationDate.getTime());
   }

Kotlin 1.3.50 bytecode

dup
aload0 // reference to self
invokevirtual a/b/c/d/BlogArticle.getCreationDate()Ljava/util/Date;
invokevirtual java/util/Date.getTime()J
invokespecial java/util/Date.<init>(J)V
areturn

Corresponding java source code from decompilation of the kotlin compiled bytecode

   public final Date getCreationDate() {
      return new Date(this.getCreationDate().getTime());
   }

As you can see the decompiled source code calls the method itself instead of the field (a result of the invokevirtual bytecode instruction). Any idea if (how) I can fix it? Is this an issue with Kotlin or am I doing something wrong?

Thank you

Ok it seems I have figured it out. If I use “field” instead of the actual variable then everything works fine:

get() = Date(field.getTime())

It would be nice though if someone can share some insights of how this actually works.

When you are in a custom getter/setter, field is a special implied word in that context (like it inside a lambda is implied to be the single parameter passed to it) that refers to the actual value of the field.

This was put in so you can avoid the recursion problem you ran in to here.