groovy
  1. groovy
  2. GROOVY-5090

Groovy compiler generates invalid byte code for local boolean variables that later on are referenced in a closure

    Details

    • Type: Bug Bug
    • Status: Closed Closed
    • Priority: Blocker Blocker
    • Resolution: Fixed
    • Affects Version/s: 1.8.3
    • Fix Version/s: 1.8.4, 2.0-beta-1
    • Component/s: None
    • Labels:
      None
    • Environment:
      Groovy 1.8.3 + Java 1.6.0_27
    • Number of attachments :
      0

      Description

      Some sample code to show the bug:

      class GroovyBooleanTest {
      	public boolean someCall() {
      		return true;
      	}
      	
      	public void somecode() {
      		boolean val = someCall()
      		println val
      		def c = {
      			val
      		}
      		boolean val2 = c.call()
      		println val2
      	}
      
      }
      

      decompiled with jd-gui:

      import groovy.lang.Closure;
      import groovy.lang.GroovyObject;
      import groovy.lang.MetaClass;
      import groovy.lang.Reference;
      import org.codehaus.groovy.runtime.BytecodeInterface8;
      import org.codehaus.groovy.runtime.GeneratedClosure;
      import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
      import org.codehaus.groovy.runtime.callsite.CallSite;
      import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
      
      public class GroovyBooleanTest implements GroovyObject {
      	public GroovyBooleanTest()
        {
          GroovyBooleanTest this;
          CallSite[] arrayOfCallSite = $getCallSiteArray();
          MetaClass localMetaClass = $getStaticMetaClass();
          this.metaClass = localMetaClass;
        }
      
      	public boolean someCall() {
      		CallSite[] arrayOfCallSite = $getCallSiteArray();
      		return DefaultTypeTransformation.booleanUnbox(Boolean.TRUE);
      		return DefaultTypeTransformation
      				.booleanUnbox((Integer) DefaultTypeTransformation.box(0));
      	}
      
      	public void somecode() {
      		CallSite[] arrayOfCallSite = $getCallSiteArray();
      		boolean val = new Reference((Boolean) DefaultTypeTransformation.box(0));
      		Object localObject1;
      		boolean bool1;
      		if ((__$stMC) || (BytecodeInterface8.disabledStandardMetaClass())) {
      			localObject1 = arrayOfCallSite[0].callCurrent(this);
      			((Reference) val).set((Boolean) ScriptBytecodeAdapter.castToType(
      					localObject1, $get$$class$java$lang$Boolean()));
      		} else {
      			bool1 = someCall();
      			((Reference) val).set((Boolean) ScriptBytecodeAdapter.castToType(
      					(Boolean) DefaultTypeTransformation.box(bool1),
      					$get$$class$java$lang$Boolean()));
      		}
      		arrayOfCallSite[1]
      				.callCurrent(this, (Boolean) DefaultTypeTransformation
      						.box(DefaultTypeTransformation.booleanUnbox(val.get())));
      		GroovyBooleanTest._somecode_closure1 local_somecode_closure1 = new GroovyBooleanTest._somecode_closure1(
      				this, val);
      		Object c = local_somecode_closure1;
      
      		Object localObject2 = arrayOfCallSite[2].call(c);
      		boolean val2 = DefaultTypeTransformation.booleanUnbox(localObject2);
      		arrayOfCallSite[3].callCurrent(this,
      				(Boolean) DefaultTypeTransformation.box(val2));
      	}
      
      	static {
      		__$swapInit();
      		Long localLong1 = (Long) DefaultTypeTransformation.box(0L);
      		__timeStamp__239_neverHappen1319016363968 = DefaultTypeTransformation
      				.longUnbox(localLong1);
      		Long localLong2 = (Long) DefaultTypeTransformation.box(1319016363968L);
      		__timeStamp = DefaultTypeTransformation.longUnbox(localLong2);
      	}
      
      	class _somecode_closure1 extends Closure implements GeneratedClosure {
      		public _somecode_closure1(Object _thisObject, Reference val) {
      			super(_thisObject);
      			boolean bool = val;
      			this.val = bool;
      		}
      
      		public Object doCall(Object it) {
      			CallSite[] arrayOfCallSite = $getCallSiteArray();
      			return this.val.get();
      			return null;
      		}
      
      		public Boolean getVal() {
      			CallSite[] arrayOfCallSite = $getCallSiteArray();
      			return (Boolean) ScriptBytecodeAdapter.castToType(this.val.get(),
      					$get$$class$java$lang$Boolean());
      			return null;
      		}
      
      		public Object doCall() {
      			CallSite[] arrayOfCallSite = $getCallSiteArray();
      			return arrayOfCallSite[0].callCurrent(this, ScriptBytecodeAdapter
      					.createPojoWrapper(null, $get$$class$java$lang$Object()));
      			return null;
      		}
      
      		static {
      			__$swapInit();
      		}
      	}
      }
      

      Invalid byte code is shown in this line:

      		boolean val = new Reference((Boolean) DefaultTypeTransformation.box(0));
      

      JVM accepts this, but the debugger will show always "false" for such variables.

      The type information looks invalid also in the closure class:

      	class _somecode_closure1 extends Closure implements GeneratedClosure {
      		public _somecode_closure1(Object _thisObject, Reference val) {
      			super(_thisObject);
      			boolean bool = val;
      			this.val = bool;
      		}
      

        Activity

        Hide
        blackdrag blackdrag added a comment -

        after looking at the code, the operands are actually doing the right thing. What seems to be wrong is the local variable table, which ignores, that the value is a Reference instead a boolean now.

        So this bug is about a variable table entry done wrong

        Show
        blackdrag blackdrag added a comment - after looking at the code, the operands are actually doing the right thing. What seems to be wrong is the local variable table, which ignores, that the value is a Reference instead a boolean now. So this bug is about a variable table entry done wrong
        Hide
        Lari Hotari added a comment -

        Yes, quite small problem. The invalid local variable table cause problems to the debugger. (in this case the value of a boolean variable always shows up as "false" in the debugger)

        Show
        Lari Hotari added a comment - Yes, quite small problem. The invalid local variable table cause problems to the debugger. (in this case the value of a boolean variable always shows up as "false" in the debugger)
        Hide
        blackdrag blackdrag added a comment -

        fixed

        Show
        blackdrag blackdrag added a comment - fixed
        Hide
        Lari Hotari added a comment -

        Would it be possible to backport the fix to 1.8.x ? This issue causes confusion for developers trying to debug their Grails 2.0.x code in a debugger.

        Show
        Lari Hotari added a comment - Would it be possible to backport the fix to 1.8.x ? This issue causes confusion for developers trying to debug their Grails 2.0.x code in a debugger.
        Hide
        blackdrag blackdrag added a comment -

        Lari, thanks for the pointer. I actually had the fix in 1.8.x as well, just forgot to include that in the fix versions here

        Show
        blackdrag blackdrag added a comment - Lari, thanks for the pointer. I actually had the fix in 1.8.x as well, just forgot to include that in the fix versions here

          People

          • Assignee:
            blackdrag blackdrag
            Reporter:
            Lari Hotari
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: