All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.cloudbees.groovy.cps.impl.ContinuationGroup Maven / Gradle / Ivy

package com.cloudbees.groovy.cps.impl;

import com.cloudbees.groovy.cps.Block;
import com.cloudbees.groovy.cps.Continuable;
import com.cloudbees.groovy.cps.Continuation;
import com.cloudbees.groovy.cps.Env;
import com.cloudbees.groovy.cps.Next;
import com.cloudbees.groovy.cps.sandbox.Invoker;
import org.codehaus.groovy.runtime.callsite.CallSite;

import javax.annotation.CheckReturnValue;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static com.cloudbees.groovy.cps.impl.SourceLocation.*;

/**
 * Base class for defining a series of {@link Continuation} methods that share the same set of contextual values.
 *
 * Subtypes are expected to define a number of methods that have the same signature as {@link Continuation#receive(Object)}.
 * These methods can be wrapped into a {@link Continuation} instance via {@link #then(Block, Env, ContinuationPtr)} method.
 *
 * @see ContinuationPtr
 * @author Kohsuke Kawaguchi
 */
abstract class ContinuationGroup implements Serializable {
    public Next then(Block exp, Env e, ContinuationPtr ptr) {
        return new Next(exp,e,ptr.bind(this));
    }

    public Next then(Block exp, Env e, Continuation k) {
        return new Next(exp,e,k);
    }

    /*TODO: specify the proper owner value (to the script that includes the call site) */
    protected Next methodCall(Env e, SourceLocation loc, ContinuationPtr k, CallSiteBlock callSite, Object receiver, String methodName, Object... args) {
        return methodCall(e,loc,k.bind(this),callSite,receiver,methodName,args);
    }

    /**
     * Evaluates a function (possibly a workflow function), then pass the result to the given continuation.
     */
    protected Next methodCall(final Env e, final SourceLocation loc, final Continuation k, final CallSiteBlock callSite, final Object receiver, final String methodName, final Object... args) {
        try {
            Caller.record(receiver,methodName,args);

            Invoker inv = e.getInvoker().contextualize(callSite);
            Object v;

            if (receiver instanceof Super) {
                Super s = (Super) receiver;
                v = inv.superCall(s.senderType, s.receiver, methodName, args);
            } else {
                // TODO: spread
                v = inv.methodCall(receiver, methodName, args);
            }
            // if this was a normal function, the method had just executed synchronously
            return k.receive(v);
        } catch (CpsCallableInvocation inv) {
            return inv.invoke(e, loc, k);
        } catch (Throwable t) {
            return throwException(e, t, loc, new ReferenceStackTrace());
        }
    }

    /**
     * Fix up the stack trace of an exception thrown from synchronous code.
     */
    private void fixupStackTrace(Env e, Throwable t, SourceLocation loc, ReferenceStackTrace ref) {
        StackTraceElement[] rs = ref.getStackTrace();
        StackTraceElement[] ts = t.getStackTrace();

        if (!hasSameRoots(rs,ts)) {
            // this exception doesn't match up with what we expected.
            // maybe it was created elsewhere and thrown here?
            return;
        }

        /*
            SYNC TRACE
              this section contains the top portion of the actual stack beyond ReferenceStackTrace that led to the
              instantiation of the exception. This is the synchronous code called from CPS code that created the exception.
            CPS TRACE
              this section contains a synthesized fake stack trace that shows the logical stack trace of the CPS code
            REFERENCE TRACE
              this section contains the actual stack trace leading up to the point where ReferenceStackTrace is created
              to show how the actual execution happened on JVM
         */

        List orig = Arrays.asList(ts);
        int pos = ts.length-rs.length;
        List stack = new ArrayList(orig.subList(0,pos));

        stack.add((loc!=null ? loc : UNKNOWN).toStackTrace());
        e.buildStackTraceElements(stack,Integer.MAX_VALUE);
        stack.add(Continuable.SEPARATOR_STACK_ELEMENT);

        stack.addAll(orig.subList(pos, orig.size()));

        t.setStackTrace(stack.toArray(new StackTraceElement[stack.size()]));
    }

    /**
     * Returns true if 'rs' is at the bottom of 'ts'.
     */
    private boolean hasSameRoots(StackTraceElement[] rs, StackTraceElement[] ts) {
        int b = ts.length-rs.length;
        if (b<0)    return false;

        {// the top of the stack will have different line number because ReferenceStackTrace is created in a separate line
            StackTraceElement lhs = ts[b];
            StackTraceElement rhs = rs[0];

            if (!eq(lhs.getClassName(),rhs.getClassName())
             || !eq(lhs.getMethodName(),rhs.getMethodName())
             || !eq(lhs.getFileName(),rhs.getFileName()))
                return false;
        }

        for (int i=1; i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy