Re: [java code coverage] Runtime classes from Groovy source for Jenkins Pipelines
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
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
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