Re: [java code coverage] Runtime classes from Groovy source for Jenkins Pipelines

2018-02-26 Thread Marc Hoffmann

Hi Michel,

this is probably possible. As a starting point I would implement a 
ICoverageVisitor which combines IClassCoverage instances under certain 
conditions (e.g. same name). The interesting point is the merge 
algorithm itself: In your case (classes have same structure) it is 
probably realitively simple. In the general case this is probably quite 
tricky.


Cheers,
-marc




On 2018-02-24 20:31, mko...@gmail.com wrote:

Thanks Marc for taking a look. Maybe there is a way for me to use the
JaCoCO APIs to perform my own "merging"? I'm not sure how pluggable
the APIs are with JaCoCo, because most of the time it "just works" for
me and I haven't had to do anything on my own!

On Tuesday, February 20, 2018 at 1:02:06 PM UTC-6, Marc R. Hoffmann 
wrote:

Hi Michael,

thanks for the details! Indeed a variable name and a value in the 
constant pool are different. This leads to a different class id which 
is simply the CRC64 checksum of the raw class file. So current JaCoCo 
will not be able to merge them.


The tricky question is how and under what circumstances IClassCoverage 
instances can be merged. A conservative approach (which would work in 
your specific case) would be to allow merges only if both classes have 
the exact same instruction/branch structure.


Regards,
-marc

 

 

On 2018-02-19 16:06, Michael Kobit wrote:


Using the IntelliJ decompiler for one instance shows:

 


//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.mkobit.libraryexample;

import com.cloudbees.groovy.cps.Builder;
import com.cloudbees.groovy.cps.MethodLocation;
import com.cloudbees.groovy.cps.NonCPS;
import com.cloudbees.groovy.cps.WorkflowTransformed;
import com.cloudbees.groovy.cps.impl.CpsCallableInvocation;
import com.cloudbees.groovy.cps.impl.CpsFunction;
import com.cloudbees.groovy.cps.sandbox.Trusted;
import com.mkobit.libraryexample.ExampleSrc._nonCpsDouble_closure1;
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;
import org.codehaus.groovy.runtime.ArrayUtil;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
import org.jenkinsci.plugins.workflow.cps.CpsClosure2;
import org.jenkinsci.plugins.workflow.cps.Safepoint;

@WorkflowTransformed
public class ExampleSrc implements Serializable, GroovyObject {
private final Object script;
  private static final CpsFunction ___cps___0;
  private static long __timeStamp;

  public ExampleSrc(Object script) {
CallSite[] var2 = $getCallSiteArray();
MetaClass var3 = this.$getStaticMetaClass();
this.metaClass = var3;
Object var4 = var2[0].call(Objects.class, script);
this.script = var4;
  }

  @WorkflowTransformed
public void sayHelloTo(String name) {
CallSite[] var2 = $getCallSiteArray();
throw 
(Throwable)var2[1].callConstructor(CpsCallableInvocation.class, 
___cps___0, this, new Object[]{name});

  }

  @NonCPS
public List nonCpsDouble(List integers) {
CallSite[] var2 = $getCallSiteArray();
return 
(List)ScriptBytecodeAdapter.castToType(var2[2].call(integers, new 
_nonCpsDouble_closure1(this, this)), List.class);

  }

  @WorkflowTransformed
private static final CpsFunction ___cps___0() {
CallSite[] var0 = $getCallSiteArray();
Builder b = 
(Builder)ScriptBytecodeAdapter.castToType(var0[3].call(var0[4].call(var0[5].callConstructor(Builder.class, 
var0[6].callConstructor(MethodLocation.class, 
"com.mkobit.libraryexample.ExampleSrc", "sayHelloTo", 
"/tmp/jenkinsTests.tmp/jenkins1133211585081614104test/jobs/project/builds/1/libs/testLibrary/src/com/mkobit/libraryexample/ExampleSrc.groovy")), 
CpsClosure2.class), var0[7].callGetProperty(Trusted.class)), 
Builder.class);
return 
(CpsFunction)ScriptBytecodeAdapter.castToType(var0[8].callConstructor(CpsFunction.class, 
ScriptBytecodeAdapter.createList(new Object[]{"name"}), 
var0[9].call(b, var0[10].call(b, 14, Safepoint.class, "safepoint"), 
var0[11].call(b, var0[12].call(b, ArrayUtil.createArray(14, 
var0[13].call(b, 14, var0[14].call(b), "script"), var0[15].call(b, 
"echo"), false, var0[16].call(b, 14, var0[17].call(b, var0[18].call(b, 
14, "name")), var0[19].call(b, var0[20].call(b, "Hello there "), 
var0[21].call(b, "", CpsFunction.class);

  }

static {
Long var0 = 0L;
__timeStamp = var0;
Object var1 = 
$getCallSiteArray()[22].callStatic(ExampleSrc.class);
___cps___0 = (CpsFunction)ScriptBytecodeAdapter.castToType(var1, 
CpsFunction.class);

  }
}

 

And other class file:

 


//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.mkobit.libraryexample;

import com.cloudbees.groovy.cps.Builder;
import com.cloudbees.groovy.cps.MethodLocation;
import com.cloudbees.groovy.cps.NonCPS;
import com.cloudbees.groovy.cps.WorkflowTransformed;
import 

Re: [java code coverage] Runtime classes from Groovy source for Jenkins Pipelines

2018-02-24 Thread mkobit
Thanks Marc for taking a look. Maybe there is a way for me to use the JaCoCO 
APIs to perform my own "merging"? I'm not sure how pluggable the APIs are with 
JaCoCo, because most of the time it "just works" for me and I haven't had to do 
anything on my own!

On Tuesday, February 20, 2018 at 1:02:06 PM UTC-6, Marc R. Hoffmann wrote:
> Hi Michael,
> 
> thanks for the details! Indeed a variable name and a value in the constant 
> pool are different. This leads to a different class id which is simply the 
> CRC64 checksum of the raw class file. So current JaCoCo will not be able to 
> merge them.
> 
> The tricky question is how and under what circumstances IClassCoverage 
> instances can be merged. A conservative approach (which would work in your 
> specific case) would be to allow merges only if both classes have the exact 
> same instruction/branch structure.
> 
> Regards,
> -marc
> 
>  
> 
>  
> 
> On 2018-02-19 16:06, Michael Kobit wrote:
> 
> 
> Using the IntelliJ decompiler for one instance shows:
> 
>  
> 
> 
> //
> // Source code recreated from a .class file by IntelliJ IDEA
> // (powered by Fernflower decompiler)
> //
> 
> package com.mkobit.libraryexample;
> 
> import com.cloudbees.groovy.cps.Builder;
> import com.cloudbees.groovy.cps.MethodLocation;
> import com.cloudbees.groovy.cps.NonCPS;
> import com.cloudbees.groovy.cps.WorkflowTransformed;
> import com.cloudbees.groovy.cps.impl.CpsCallableInvocation;
> import com.cloudbees.groovy.cps.impl.CpsFunction;
> import com.cloudbees.groovy.cps.sandbox.Trusted;
> import com.mkobit.libraryexample.ExampleSrc._nonCpsDouble_closure1;
> import groovy.lang.GroovyObject;
> import groovy.lang.MetaClass;
> import java.io.Serializable;
> import java.util.List;
> import java.util.Objects;
> import org.codehaus.groovy.runtime.ArrayUtil;
> import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
> import org.codehaus.groovy.runtime.callsite.CallSite;
> import org.jenkinsci.plugins.workflow.cps.CpsClosure2;
> import org.jenkinsci.plugins.workflow.cps.Safepoint;
> 
> @WorkflowTransformed
> public class ExampleSrc implements Serializable, GroovyObject {
> private final Object script;
>   private static final CpsFunction ___cps___0;
>   private static long __timeStamp;
> 
>   public ExampleSrc(Object script) {
> CallSite[] var2 = $getCallSiteArray();
> MetaClass var3 = this.$getStaticMetaClass();
> this.metaClass = var3;
> Object var4 = var2[0].call(Objects.class, script);
> this.script = var4;
>   }
> 
>   @WorkflowTransformed
> public void sayHelloTo(String name) {
> CallSite[] var2 = $getCallSiteArray();
> throw (Throwable)var2[1].callConstructor(CpsCallableInvocation.class, 
> ___cps___0, this, new Object[]{name});
>   }
> 
>   @NonCPS
> public List nonCpsDouble(List integers) {
> CallSite[] var2 = $getCallSiteArray();
> return (List)ScriptBytecodeAdapter.castToType(var2[2].call(integers, new 
> _nonCpsDouble_closure1(this, this)), List.class);
>   }
> 
>   @WorkflowTransformed
> private static final CpsFunction ___cps___0() {
> CallSite[] var0 = $getCallSiteArray();
> Builder b = 
> (Builder)ScriptBytecodeAdapter.castToType(var0[3].call(var0[4].call(var0[5].callConstructor(Builder.class,
>  var0[6].callConstructor(MethodLocation.class, 
> "com.mkobit.libraryexample.ExampleSrc", "sayHelloTo", 
> "/tmp/jenkinsTests.tmp/jenkins1133211585081614104test/jobs/project/builds/1/libs/testLibrary/src/com/mkobit/libraryexample/ExampleSrc.groovy")),
>  CpsClosure2.class), var0[7].callGetProperty(Trusted.class)), Builder.class);
> return 
> (CpsFunction)ScriptBytecodeAdapter.castToType(var0[8].callConstructor(CpsFunction.class,
>  ScriptBytecodeAdapter.createList(new Object[]{"name"}), var0[9].call(b, 
> var0[10].call(b, 14, Safepoint.class, "safepoint"), var0[11].call(b, 
> var0[12].call(b, ArrayUtil.createArray(14, var0[13].call(b, 14, 
> var0[14].call(b), "script"), var0[15].call(b, "echo"), false, 
> var0[16].call(b, 14, var0[17].call(b, var0[18].call(b, 14, "name")), 
> var0[19].call(b, var0[20].call(b, "Hello there "), var0[21].call(b, 
> "", CpsFunction.class);
>   }
> 
> static {
> Long var0 = 0L;
> __timeStamp = var0;
> Object var1 = $getCallSiteArray()[22].callStatic(ExampleSrc.class);
> ___cps___0 = (CpsFunction)ScriptBytecodeAdapter.castToType(var1, 
> CpsFunction.class);
>   }
> }
> 
>  
> 
> And other class file:
> 
>  
> 
> 
> //
> // Source code recreated from a .class file by IntelliJ IDEA
> // (powered by Fernflower decompiler)
> //
> 
> package com.mkobit.libraryexample;
> 
> import com.cloudbees.groovy.cps.Builder;
> import com.cloudbees.groovy.cps.MethodLocation;
> import com.cloudbees.groovy.cps.NonCPS;
> import com.cloudbees.groovy.cps.WorkflowTransformed;
> import com.cloudbees.groovy.cps.impl.CpsCallableInvocation;
> import com.cloudbees.groovy.cps.impl.CpsFunction;
> import com.cloudbees.groovy.cps.sandbox.Trusted;
> import 

Re: [java code coverage] Runtime classes from Groovy source for Jenkins Pipelines

2018-02-20 Thread Marc Hoffmann
Hi Michael, 

thanks for the details! Indeed a variable name and a value in the
constant pool are different. This leads to a different class id which is
simply the CRC64 checksum of the raw class file. So current JaCoCo will
not be able to merge them. 

The tricky question is how and under what circumstances IClassCoverage
instances can be merged. A conservative approach (which would work in
your specific case) would be to allow merges only if both classes have
the exact same instruction/branch structure. 

Regards,
-marc 

On 2018-02-19 16:06, Michael Kobit wrote:

> Using the IntelliJ decompiler for one instance shows: 
> 
> //
> // Source code recreated from a .class file by IntelliJ IDEA
> // (powered by Fernflower decompiler)
> //
> 
> package com.mkobit.libraryexample;
> 
> import com.cloudbees.groovy.cps.Builder;
> import com.cloudbees.groovy.cps.MethodLocation;
> import com.cloudbees.groovy.cps.NonCPS;
> import com.cloudbees.groovy.cps.WorkflowTransformed;
> import com.cloudbees.groovy.cps.impl.CpsCallableInvocation;
> import com.cloudbees.groovy.cps.impl.CpsFunction;
> import com.cloudbees.groovy.cps.sandbox.Trusted;
> import com.mkobit.libraryexample.ExampleSrc._nonCpsDouble_closure1;
> import groovy.lang.GroovyObject;
> import groovy.lang.MetaClass;
> import java.io.Serializable;
> import java.util.List;
> import java.util.Objects;
> import org.codehaus.groovy.runtime.ArrayUtil;
> import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
> import org.codehaus.groovy.runtime.callsite.CallSite;
> import org.jenkinsci.plugins.workflow.cps.CpsClosure2;
> import org.jenkinsci.plugins.workflow.cps.Safepoint;
> 
> @WorkflowTransformed
> public class ExampleSrc implements Serializable, GroovyObject {
> private final Object script;
> private static final CpsFunction ___cps___0;
> private static long __timeStamp;
> 
> public ExampleSrc(Object script) {
> CallSite[] var2 = $getCallSiteArray();
> MetaClass var3 = this.$getStaticMetaClass();
> this.metaClass = var3;
> Object var4 = var2[0].call(Objects.class, script);
> this.script = var4;
> }
> 
> @WorkflowTransformed
> public void sayHelloTo(String name) {
> CallSite[] var2 = $getCallSiteArray();
> throw (Throwable)var2[1].callConstructor(CpsCallableInvocation.class, 
> ___cps___0, this, new Object[]{name});
> }
> 
> @NonCPS
> public List nonCpsDouble(List integers) {
> CallSite[] var2 = $getCallSiteArray();
> return (List)ScriptBytecodeAdapter.castToType(var2[2].call(integers, new 
> _nonCpsDouble_closure1(this, this)), List.class);
> }
> 
> @WorkflowTransformed
> private static final CpsFunction ___cps___0() {
> CallSite[] var0 = $getCallSiteArray();
> Builder b = 
> (Builder)ScriptBytecodeAdapter.castToType(var0[3].call(var0[4].call(var0[5].callConstructor(Builder.class,
>  var0[6].callConstructor(MethodLocation.class, 
> "com.mkobit.libraryexample.ExampleSrc", "sayHelloTo", 
> "/tmp/jenkinsTests.tmp/jenkins1133211585081614104test/jobs/project/builds/1/libs/testLibrary/src/com/mkobit/libraryexample/ExampleSrc.groovy")),
>  CpsClosure2.class), var0[7].callGetProperty(Trusted.class)), Builder.class);
> return 
> (CpsFunction)ScriptBytecodeAdapter.castToType(var0[8].callConstructor(CpsFunction.class,
>  ScriptBytecodeAdapter.createList(new Object[]{"name"}), var0[9].call(b, 
> var0[10].call(b, 14, Safepoint.class, "safepoint"), var0[11].call(b, 
> var0[12].call(b, ArrayUtil.createArray(14, var0[13].call(b, 14, 
> var0[14].call(b), "script"), var0[15].call(b, "echo"), false, 
> var0[16].call(b, 14, var0[17].call(b, var0[18].call(b, 14, "name")), 
> var0[19].call(b, var0[20].call(b, "Hello there "), var0[21].call(b, 
> "", CpsFunction.class);
> }
> 
> static {
> Long var0 = 0L;
> __timeStamp = var0;
> Object var1 = $getCallSiteArray()[22].callStatic(ExampleSrc.class);
> ___cps___0 = (CpsFunction)ScriptBytecodeAdapter.castToType(var1, 
> CpsFunction.class);
> }
> }
> 
> And other class file: 
> 
> //
> // Source code recreated from a .class file by IntelliJ IDEA
> // (powered by Fernflower decompiler)
> //
> 
> package com.mkobit.libraryexample;
> 
> import com.cloudbees.groovy.cps.Builder;
> import com.cloudbees.groovy.cps.MethodLocation;
> import com.cloudbees.groovy.cps.NonCPS;
> import com.cloudbees.groovy.cps.WorkflowTransformed;
> import com.cloudbees.groovy.cps.impl.CpsCallableInvocation;
> import com.cloudbees.groovy.cps.impl.CpsFunction;
> import com.cloudbees.groovy.cps.sandbox.Trusted;
> import com.mkobit.libraryexample.ExampleSrc._nonCpsDouble_closure1;
> import groovy.lang.GroovyObject;
> import groovy.lang.MetaClass;
> import java.io.Serializable;
> import java.util.List;
> import java.util.Objects;
> import org.codehaus.groovy.runtime.ArrayUtil;
> import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
> import org.codehaus.groovy.runtime.callsite.CallSite;
> import org.jenkinsci.plugins.workflow.cps.CpsClosure2;
> import org.jenkinsci.plugins.workflow.cps.Safepoint;
> 
> @WorkflowTransformed
> public class ExampleSrc implements