Skip to content

Commit

Permalink
[JENKINS-26481] Prototype of using GroovyCategorySupport to invoke Cp…
Browse files Browse the repository at this point in the history
…sDefaultGroovyMethods without hacks like DGMPatcher.
  • Loading branch information
jglick committed May 11, 2017
1 parent ef0d963 commit ab9a154
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 7 deletions.
42 changes: 40 additions & 2 deletions src/main/java/org/jenkinsci/plugins/workflow/cps/CpsThread.java
Expand Up @@ -24,10 +24,17 @@

package org.jenkinsci.plugins.workflow.cps;

import com.cloudbees.groovy.cps.Builder;
import com.cloudbees.groovy.cps.Continuable;
import com.cloudbees.groovy.cps.CpsDefaultGroovyMethods;
import com.cloudbees.groovy.cps.MethodLocation;
import com.cloudbees.groovy.cps.Outcome;
import com.cloudbees.groovy.cps.impl.Caller;
import com.cloudbees.groovy.cps.impl.CpsCallableInvocation;
import com.cloudbees.groovy.cps.impl.CpsFunction;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.SettableFuture;
import groovy.lang.Closure;
import org.jenkinsci.plugins.workflow.cps.persistence.PersistIn;
import org.jenkinsci.plugins.workflow.steps.StepExecution;

Expand All @@ -36,12 +43,16 @@
import javax.annotation.Nullable;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import static java.util.logging.Level.*;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import org.codehaus.groovy.runtime.GroovyCategorySupport;
import org.codehaus.groovy.runtime.InvokerHelper;
import static org.jenkinsci.plugins.workflow.cps.persistence.PersistenceContext.*;
import org.jenkinsci.plugins.workflow.support.concurrent.Futures;
import org.jenkinsci.plugins.workflow.support.concurrent.Timeout;
Expand Down Expand Up @@ -146,10 +157,33 @@ public StepExecution getStep() {
this.step = step;
}

/** TODO pending full coverage in {@link CpsDefaultGroovyMethods} */
public static class CpsDefaultGroovyMethodsExt {
private static MethodLocation loc(String methodName) {
return new MethodLocation(CpsDefaultGroovyMethodsExt.class, methodName);
}
public static <T> List<T> each(List<T> self, Closure<?> closure) {
if (!Caller.isAsynchronous(self, "each", closure) &&
!Caller.isAsynchronous(CpsDefaultGroovyMethodsExt.class, "each", self, closure)) {
return DefaultGroovyMethods.each(self, closure);
}
Builder b = new Builder(loc("each"));
CpsFunction f = new CpsFunction(Arrays.asList("self", "closure"), b.block(
b.staticCall(-1, CpsDefaultGroovyMethods.class, "each",
b.staticCall(-1, InvokerHelper.class, "asIterator",
b.localVariable("self")),
b.localVariable("closure")),
b.return_(b.localVariable("self"))));
throw new CpsCallableInvocation(f, null, self, closure);
}
private CpsDefaultGroovyMethodsExt() {}
}

/**
* Executes CPS code synchronously a little bit more, until it hits
* the point the workflow needs to be dehydrated.
*/
@SuppressWarnings("rawtypes")
@Nonnull Outcome runNextChunk() {
assert program!=null;

Expand All @@ -160,9 +194,13 @@ public StepExecution getStep() {

try (Timeout timeout = Timeout.limit(5, TimeUnit.MINUTES)) {
LOGGER.log(FINE, "runNextChunk on {0}", resumeValue);
Outcome o = resumeValue;
final Outcome o = resumeValue;
resumeValue = null;
outcome = program.run0(o);
outcome = GroovyCategorySupport.use(Arrays.<Class>asList(CpsDefaultGroovyMethods.class, CpsDefaultGroovyMethodsExt.class), new Closure<Outcome>(null) {
@Override public Outcome call() {
return program.run0(o);
}
});
if (outcome.getAbnormal() != null) {
LOGGER.log(FINE, "ran and produced error", outcome.getAbnormal());
} else {
Expand Down
Expand Up @@ -11,7 +11,6 @@
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;

import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException;
Expand Down Expand Up @@ -85,9 +84,11 @@ private boolean checkJenkins26481(Object[] args, /* TODO Java 8: just take Execu
if (permits(method.getDeclaringClass())) { // fine for source-defined methods to take closures
return true;
}
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof CpsClosure && parameterTypes.length > i && parameterTypes[i] == Closure.class) {
throw new UnsupportedOperationException("Calling " + method + " on a CPS-transformed closure is not yet supported (JENKINS-26481); encapsulate in a @NonCPS method, or use Java-style loops");
if (false) { // TODO be more discriminating
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof CpsClosure && parameterTypes.length > i && parameterTypes[i] == Closure.class) {
throw new UnsupportedOperationException("Calling " + method + " on a CPS-transformed closure is not yet supported (JENKINS-26481); encapsulate in a @NonCPS method, or use Java-style loops");
}
}
}
return false;
Expand Down
Expand Up @@ -247,7 +247,6 @@ public void stop(Throwable cause) throws Exception {
});
}

@Ignore("TODO backed out for JENKINS-34064")
@Issue("JENKINS-26481")
@Test public void eachClosure() {
story.addStep(new Statement() {
Expand Down

0 comments on commit ab9a154

Please sign in to comment.